]>
Commit | Line | Data |
---|---|---|
9f76c5b5 | 1 | /* |
270b1db1 DC |
2 | * Copyright (c) 2000-2006 Silicon Graphics, Inc. |
3 | * Copyright (c) 2012-2013 Red Hat, Inc. | |
9f76c5b5 | 4 | * All rights reserved. |
270b1db1 DC |
5 | * |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it would be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write the Free Software Foundation, | |
17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
9f76c5b5 | 18 | */ |
9c799827 | 19 | #include "libxfs_priv.h" |
b626fb59 DC |
20 | #include "xfs_fs.h" |
21 | #include "xfs_format.h" | |
22 | #include "xfs_log_format.h" | |
23 | #include "xfs_shared.h" | |
24 | #include "xfs_trans_resv.h" | |
25 | #include "xfs_mount.h" | |
26 | #include "xfs_bmap_btree.h" | |
27 | #include "xfs_inode.h" | |
28 | #include "xfs_trace.h" | |
29 | #include "xfs_cksum.h" | |
30 | #include "xfs_trans.h" | |
31 | ||
9f76c5b5 DC |
32 | |
33 | /* | |
34 | * Each contiguous block has a header, so it is not just a simple pathlen | |
35 | * to FSB conversion. | |
36 | */ | |
37 | int | |
38 | xfs_symlink_blocks( | |
39 | struct xfs_mount *mp, | |
40 | int pathlen) | |
41 | { | |
2019931a | 42 | int buflen = XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize); |
9f76c5b5 | 43 | |
2019931a | 44 | return (pathlen + buflen - 1) / buflen; |
9f76c5b5 DC |
45 | } |
46 | ||
f7b80291 | 47 | int |
9f76c5b5 DC |
48 | xfs_symlink_hdr_set( |
49 | struct xfs_mount *mp, | |
50 | xfs_ino_t ino, | |
51 | uint32_t offset, | |
52 | uint32_t size, | |
53 | struct xfs_buf *bp) | |
54 | { | |
55 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
56 | ||
57 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
58 | return 0; | |
59 | ||
a65d8d29 | 60 | memset(dsl, 0, sizeof(struct xfs_dsymlink_hdr)); |
9f76c5b5 DC |
61 | dsl->sl_magic = cpu_to_be32(XFS_SYMLINK_MAGIC); |
62 | dsl->sl_offset = cpu_to_be32(offset); | |
63 | dsl->sl_bytes = cpu_to_be32(size); | |
9c4e12fb | 64 | uuid_copy(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid); |
9f76c5b5 DC |
65 | dsl->sl_owner = cpu_to_be64(ino); |
66 | dsl->sl_blkno = cpu_to_be64(bp->b_bn); | |
67 | bp->b_ops = &xfs_symlink_buf_ops; | |
68 | ||
69 | return sizeof(struct xfs_dsymlink_hdr); | |
70 | } | |
71 | ||
72 | /* | |
73 | * Checking of the symlink header is split into two parts. the verifier does | |
74 | * CRC, location and bounds checking, the unpacking function checks the path | |
75 | * parameters and owner. | |
76 | */ | |
77 | bool | |
78 | xfs_symlink_hdr_ok( | |
9f76c5b5 DC |
79 | xfs_ino_t ino, |
80 | uint32_t offset, | |
81 | uint32_t size, | |
82 | struct xfs_buf *bp) | |
83 | { | |
84 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
85 | ||
86 | if (offset != be32_to_cpu(dsl->sl_offset)) | |
87 | return false; | |
88 | if (size != be32_to_cpu(dsl->sl_bytes)) | |
89 | return false; | |
90 | if (ino != be64_to_cpu(dsl->sl_owner)) | |
91 | return false; | |
92 | ||
93 | /* ok */ | |
94 | return true; | |
9f76c5b5 DC |
95 | } |
96 | ||
bc01119d | 97 | static xfs_failaddr_t |
9f76c5b5 DC |
98 | xfs_symlink_verify( |
99 | struct xfs_buf *bp) | |
100 | { | |
101 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
102 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
103 | ||
104 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
bc01119d | 105 | return __this_address; |
9f76c5b5 | 106 | if (dsl->sl_magic != cpu_to_be32(XFS_SYMLINK_MAGIC)) |
bc01119d | 107 | return __this_address; |
9c4e12fb | 108 | if (!uuid_equal(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid)) |
bc01119d | 109 | return __this_address; |
9f76c5b5 | 110 | if (bp->b_bn != be64_to_cpu(dsl->sl_blkno)) |
bc01119d | 111 | return __this_address; |
9f76c5b5 | 112 | if (be32_to_cpu(dsl->sl_offset) + |
b8fb8c95 | 113 | be32_to_cpu(dsl->sl_bytes) >= XFS_SYMLINK_MAXLEN) |
bc01119d | 114 | return __this_address; |
9f76c5b5 | 115 | if (dsl->sl_owner == 0) |
bc01119d | 116 | return __this_address; |
a65d8d29 | 117 | if (!xfs_log_check_lsn(mp, be64_to_cpu(dsl->sl_lsn))) |
bc01119d | 118 | return __this_address; |
9f76c5b5 | 119 | |
bc01119d | 120 | return NULL; |
9f76c5b5 DC |
121 | } |
122 | ||
123 | static void | |
124 | xfs_symlink_read_verify( | |
125 | struct xfs_buf *bp) | |
126 | { | |
127 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
1e697959 | 128 | xfs_failaddr_t fa; |
9f76c5b5 DC |
129 | |
130 | /* no verification of non-crc buffers */ | |
131 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
132 | return; | |
133 | ||
45922933 | 134 | if (!xfs_buf_verify_cksum(bp, XFS_SYMLINK_CRC_OFF)) |
1e697959 DW |
135 | xfs_verifier_error(bp, -EFSBADCRC, __this_address); |
136 | else { | |
137 | fa = xfs_symlink_verify(bp); | |
138 | if (fa) | |
139 | xfs_verifier_error(bp, -EFSCORRUPTED, fa); | |
140 | } | |
9f76c5b5 DC |
141 | } |
142 | ||
143 | static void | |
144 | xfs_symlink_write_verify( | |
145 | struct xfs_buf *bp) | |
146 | { | |
147 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
37d086ca | 148 | struct xfs_buf_log_item *bip = bp->b_log_item; |
1e697959 | 149 | xfs_failaddr_t fa; |
9f76c5b5 DC |
150 | |
151 | /* no verification of non-crc buffers */ | |
152 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
153 | return; | |
154 | ||
1e697959 DW |
155 | fa = xfs_symlink_verify(bp); |
156 | if (fa) { | |
157 | xfs_verifier_error(bp, -EFSCORRUPTED, fa); | |
9f76c5b5 DC |
158 | return; |
159 | } | |
160 | ||
161 | if (bip) { | |
162 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
163 | dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn); | |
164 | } | |
43b5aeed | 165 | xfs_buf_update_cksum(bp, XFS_SYMLINK_CRC_OFF); |
9f76c5b5 DC |
166 | } |
167 | ||
168 | const struct xfs_buf_ops xfs_symlink_buf_ops = { | |
a3fac935 | 169 | .name = "xfs_symlink", |
9f76c5b5 DC |
170 | .verify_read = xfs_symlink_read_verify, |
171 | .verify_write = xfs_symlink_write_verify, | |
95d9582b | 172 | .verify_struct = xfs_symlink_verify, |
9f76c5b5 DC |
173 | }; |
174 | ||
88b66843 DC |
175 | void |
176 | xfs_symlink_local_to_remote( | |
177 | struct xfs_trans *tp, | |
178 | struct xfs_buf *bp, | |
179 | struct xfs_inode *ip, | |
180 | struct xfs_ifork *ifp) | |
181 | { | |
182 | struct xfs_mount *mp = ip->i_mount; | |
183 | char *buf; | |
184 | ||
19ebedcf DC |
185 | xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF); |
186 | ||
88b66843 DC |
187 | if (!xfs_sb_version_hascrc(&mp->m_sb)) { |
188 | bp->b_ops = NULL; | |
189 | memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); | |
f44fbde0 | 190 | xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1); |
88b66843 DC |
191 | return; |
192 | } | |
193 | ||
194 | /* | |
195 | * As this symlink fits in an inode literal area, it must also fit in | |
196 | * the smallest buffer the filesystem supports. | |
197 | */ | |
198 | ASSERT(BBTOB(bp->b_length) >= | |
199 | ifp->if_bytes + sizeof(struct xfs_dsymlink_hdr)); | |
200 | ||
201 | bp->b_ops = &xfs_symlink_buf_ops; | |
202 | ||
203 | buf = bp->b_addr; | |
204 | buf += xfs_symlink_hdr_set(mp, ip->i_ino, 0, ifp->if_bytes, bp); | |
205 | memcpy(buf, ifp->if_u1.if_data, ifp->if_bytes); | |
f44fbde0 BF |
206 | xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsymlink_hdr) + |
207 | ifp->if_bytes - 1); | |
88b66843 | 208 | } |
6db3a800 DW |
209 | |
210 | /* Verify the consistency of an inline symlink. */ | |
211 | xfs_failaddr_t | |
212 | xfs_symlink_shortform_verify( | |
213 | struct xfs_inode *ip) | |
214 | { | |
215 | char *sfp; | |
216 | char *endp; | |
217 | struct xfs_ifork *ifp; | |
218 | int size; | |
219 | ||
220 | ASSERT(ip->i_d.di_format == XFS_DINODE_FMT_LOCAL); | |
221 | ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); | |
222 | sfp = (char *)ifp->if_u1.if_data; | |
223 | size = ifp->if_bytes; | |
224 | endp = sfp + size; | |
225 | ||
226 | /* Zero length symlinks can exist while we're deleting a remote one. */ | |
227 | if (size == 0) | |
228 | return NULL; | |
229 | ||
230 | /* No negative sizes or overly long symlink targets. */ | |
231 | if (size < 0 || size > XFS_SYMLINK_MAXLEN) | |
232 | return __this_address; | |
233 | ||
234 | /* No NULLs in the target either. */ | |
235 | if (memchr(sfp, 0, size - 1)) | |
236 | return __this_address; | |
237 | ||
238 | /* We /did/ null-terminate the buffer, right? */ | |
239 | if (*endp != 0) | |
240 | return __this_address; | |
241 | return NULL; | |
242 | } |