diff options
author | Dave Chinner <dchinner@redhat.com> | 2013-04-03 01:11:19 -0400 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2013-04-27 12:49:28 -0400 |
commit | f948dd76dde021c050c7c35720dc066a8b9a5e35 (patch) | |
tree | 457f8097ba46f72ecb6417f96f890f9ee202f6cc /fs/xfs/xfs_symlink.c | |
parent | 19de7351a8eb82dc99745e60e8f43474831d99c7 (diff) |
xfs: add CRC checks to remote symlinks
Add a header to the remote symlink block, containing location and
owner information, as well as CRCs and LSN fields. This requires
verifiers to be added to the remote symlink buffers for CRC enabled
filesystems.
This also fixes a bug reading multiple block symlinks, where the second
block overwrites the first block when copying out the link name.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Ben Myers <bpm@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_symlink.c')
-rw-r--r-- | fs/xfs/xfs_symlink.c | 279 |
1 files changed, 240 insertions, 39 deletions
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 7512c96c7f29..5f234389327c 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c | |||
@@ -43,6 +43,152 @@ | |||
43 | #include "xfs_log_priv.h" | 43 | #include "xfs_log_priv.h" |
44 | #include "xfs_trace.h" | 44 | #include "xfs_trace.h" |
45 | #include "xfs_symlink.h" | 45 | #include "xfs_symlink.h" |
46 | #include "xfs_cksum.h" | ||
47 | #include "xfs_buf_item.h" | ||
48 | |||
49 | |||
50 | /* | ||
51 | * Each contiguous block has a header, so it is not just a simple pathlen | ||
52 | * to FSB conversion. | ||
53 | */ | ||
54 | int | ||
55 | xfs_symlink_blocks( | ||
56 | struct xfs_mount *mp, | ||
57 | int pathlen) | ||
58 | { | ||
59 | int fsblocks = 0; | ||
60 | int len = pathlen; | ||
61 | |||
62 | do { | ||
63 | fsblocks++; | ||
64 | len -= XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize); | ||
65 | } while (len > 0); | ||
66 | |||
67 | ASSERT(fsblocks <= XFS_SYMLINK_MAPS); | ||
68 | return fsblocks; | ||
69 | } | ||
70 | |||
71 | static int | ||
72 | xfs_symlink_hdr_set( | ||
73 | struct xfs_mount *mp, | ||
74 | xfs_ino_t ino, | ||
75 | uint32_t offset, | ||
76 | uint32_t size, | ||
77 | struct xfs_buf *bp) | ||
78 | { | ||
79 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
80 | |||
81 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
82 | return 0; | ||
83 | |||
84 | dsl->sl_magic = cpu_to_be32(XFS_SYMLINK_MAGIC); | ||
85 | dsl->sl_offset = cpu_to_be32(offset); | ||
86 | dsl->sl_bytes = cpu_to_be32(size); | ||
87 | uuid_copy(&dsl->sl_uuid, &mp->m_sb.sb_uuid); | ||
88 | dsl->sl_owner = cpu_to_be64(ino); | ||
89 | dsl->sl_blkno = cpu_to_be64(bp->b_bn); | ||
90 | bp->b_ops = &xfs_symlink_buf_ops; | ||
91 | |||
92 | return sizeof(struct xfs_dsymlink_hdr); | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Checking of the symlink header is split into two parts. the verifier does | ||
97 | * CRC, location and bounds checking, the unpacking function checks the path | ||
98 | * parameters and owner. | ||
99 | */ | ||
100 | bool | ||
101 | xfs_symlink_hdr_ok( | ||
102 | struct xfs_mount *mp, | ||
103 | xfs_ino_t ino, | ||
104 | uint32_t offset, | ||
105 | uint32_t size, | ||
106 | struct xfs_buf *bp) | ||
107 | { | ||
108 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
109 | |||
110 | if (offset != be32_to_cpu(dsl->sl_offset)) | ||
111 | return false; | ||
112 | if (size != be32_to_cpu(dsl->sl_bytes)) | ||
113 | return false; | ||
114 | if (ino != be64_to_cpu(dsl->sl_owner)) | ||
115 | return false; | ||
116 | |||
117 | /* ok */ | ||
118 | return true; | ||
119 | } | ||
120 | |||
121 | static bool | ||
122 | xfs_symlink_verify( | ||
123 | struct xfs_buf *bp) | ||
124 | { | ||
125 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
126 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
127 | |||
128 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
129 | return false; | ||
130 | if (dsl->sl_magic != cpu_to_be32(XFS_SYMLINK_MAGIC)) | ||
131 | return false; | ||
132 | if (!uuid_equal(&dsl->sl_uuid, &mp->m_sb.sb_uuid)) | ||
133 | return false; | ||
134 | if (bp->b_bn != be64_to_cpu(dsl->sl_blkno)) | ||
135 | return false; | ||
136 | if (be32_to_cpu(dsl->sl_offset) + | ||
137 | be32_to_cpu(dsl->sl_bytes) >= MAXPATHLEN) | ||
138 | return false; | ||
139 | if (dsl->sl_owner == 0) | ||
140 | return false; | ||
141 | |||
142 | return true; | ||
143 | } | ||
144 | |||
145 | static void | ||
146 | xfs_symlink_read_verify( | ||
147 | struct xfs_buf *bp) | ||
148 | { | ||
149 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
150 | |||
151 | /* no verification of non-crc buffers */ | ||
152 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
153 | return; | ||
154 | |||
155 | if (!xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length), | ||
156 | offsetof(struct xfs_dsymlink_hdr, sl_crc)) || | ||
157 | !xfs_symlink_verify(bp)) { | ||
158 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); | ||
159 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | static void | ||
164 | xfs_symlink_write_verify( | ||
165 | struct xfs_buf *bp) | ||
166 | { | ||
167 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
168 | struct xfs_buf_log_item *bip = bp->b_fspriv; | ||
169 | |||
170 | /* no verification of non-crc buffers */ | ||
171 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
172 | return; | ||
173 | |||
174 | if (!xfs_symlink_verify(bp)) { | ||
175 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); | ||
176 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
177 | return; | ||
178 | } | ||
179 | |||
180 | if (bip) { | ||
181 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
182 | dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn); | ||
183 | } | ||
184 | xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), | ||
185 | offsetof(struct xfs_dsymlink_hdr, sl_crc)); | ||
186 | } | ||
187 | |||
188 | const struct xfs_buf_ops xfs_symlink_buf_ops = { | ||
189 | .verify_read = xfs_symlink_read_verify, | ||
190 | .verify_write = xfs_symlink_write_verify, | ||
191 | }; | ||
46 | 192 | ||
47 | void | 193 | void |
48 | xfs_symlink_local_to_remote( | 194 | xfs_symlink_local_to_remote( |
@@ -51,38 +197,60 @@ xfs_symlink_local_to_remote( | |||
51 | struct xfs_inode *ip, | 197 | struct xfs_inode *ip, |
52 | struct xfs_ifork *ifp) | 198 | struct xfs_ifork *ifp) |
53 | { | 199 | { |
54 | /* remote symlink blocks are not verifiable until CRCs come along */ | 200 | struct xfs_mount *mp = ip->i_mount; |
55 | bp->b_ops = NULL; | 201 | char *buf; |
56 | memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); | 202 | |
203 | if (!xfs_sb_version_hascrc(&mp->m_sb)) { | ||
204 | bp->b_ops = NULL; | ||
205 | memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); | ||
206 | return; | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * As this symlink fits in an inode literal area, it must also fit in | ||
211 | * the smallest buffer the filesystem supports. | ||
212 | */ | ||
213 | ASSERT(BBTOB(bp->b_length) >= | ||
214 | ifp->if_bytes + sizeof(struct xfs_dsymlink_hdr)); | ||
215 | |||
216 | bp->b_ops = &xfs_symlink_buf_ops; | ||
217 | |||
218 | buf = bp->b_addr; | ||
219 | buf += xfs_symlink_hdr_set(mp, ip->i_ino, 0, ifp->if_bytes, bp); | ||
220 | memcpy(buf, ifp->if_u1.if_data, ifp->if_bytes); | ||
57 | } | 221 | } |
58 | 222 | ||
59 | /* ----- Kernel only functions below ----- */ | 223 | /* ----- Kernel only functions below ----- */ |
60 | |||
61 | STATIC int | 224 | STATIC int |
62 | xfs_readlink_bmap( | 225 | xfs_readlink_bmap( |
63 | xfs_inode_t *ip, | 226 | struct xfs_inode *ip, |
64 | char *link) | 227 | char *link) |
65 | { | 228 | { |
66 | xfs_mount_t *mp = ip->i_mount; | 229 | struct xfs_mount *mp = ip->i_mount; |
67 | int pathlen = ip->i_d.di_size; | 230 | struct xfs_bmbt_irec mval[XFS_SYMLINK_MAPS]; |
68 | int nmaps = XFS_SYMLINK_MAPS; | 231 | struct xfs_buf *bp; |
69 | xfs_bmbt_irec_t mval[XFS_SYMLINK_MAPS]; | 232 | xfs_daddr_t d; |
70 | xfs_daddr_t d; | 233 | char *cur_chunk; |
71 | int byte_cnt; | 234 | int pathlen = ip->i_d.di_size; |
72 | int n; | 235 | int nmaps = XFS_SYMLINK_MAPS; |
73 | xfs_buf_t *bp; | 236 | int byte_cnt; |
74 | int error = 0; | 237 | int n; |
238 | int error = 0; | ||
239 | int fsblocks = 0; | ||
240 | int offset; | ||
75 | 241 | ||
76 | error = xfs_bmapi_read(ip, 0, XFS_B_TO_FSB(mp, pathlen), mval, &nmaps, | 242 | fsblocks = xfs_symlink_blocks(mp, pathlen); |
77 | 0); | 243 | error = xfs_bmapi_read(ip, 0, fsblocks, mval, &nmaps, 0); |
78 | if (error) | 244 | if (error) |
79 | goto out; | 245 | goto out; |
80 | 246 | ||
247 | offset = 0; | ||
81 | for (n = 0; n < nmaps; n++) { | 248 | for (n = 0; n < nmaps; n++) { |
82 | d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); | 249 | d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); |
83 | byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); | 250 | byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); |
84 | 251 | ||
85 | bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0, NULL); | 252 | bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0, |
253 | &xfs_symlink_buf_ops); | ||
86 | if (!bp) | 254 | if (!bp) |
87 | return XFS_ERROR(ENOMEM); | 255 | return XFS_ERROR(ENOMEM); |
88 | error = bp->b_error; | 256 | error = bp->b_error; |
@@ -91,13 +259,34 @@ xfs_readlink_bmap( | |||
91 | xfs_buf_relse(bp); | 259 | xfs_buf_relse(bp); |
92 | goto out; | 260 | goto out; |
93 | } | 261 | } |
262 | byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt); | ||
94 | if (pathlen < byte_cnt) | 263 | if (pathlen < byte_cnt) |
95 | byte_cnt = pathlen; | 264 | byte_cnt = pathlen; |
265 | |||
266 | cur_chunk = bp->b_addr; | ||
267 | if (xfs_sb_version_hascrc(&mp->m_sb)) { | ||
268 | if (!xfs_symlink_hdr_ok(mp, ip->i_ino, offset, | ||
269 | byte_cnt, bp)) { | ||
270 | error = EFSCORRUPTED; | ||
271 | xfs_alert(mp, | ||
272 | "symlink header does not match required off/len/owner (0x%x/Ox%x,0x%llx)", | ||
273 | offset, byte_cnt, ip->i_ino); | ||
274 | xfs_buf_relse(bp); | ||
275 | goto out; | ||
276 | |||
277 | } | ||
278 | |||
279 | cur_chunk += sizeof(struct xfs_dsymlink_hdr); | ||
280 | } | ||
281 | |||
282 | memcpy(link + offset, bp->b_addr, byte_cnt); | ||
283 | |||
96 | pathlen -= byte_cnt; | 284 | pathlen -= byte_cnt; |
285 | offset += byte_cnt; | ||
97 | 286 | ||
98 | memcpy(link, bp->b_addr, byte_cnt); | ||
99 | xfs_buf_relse(bp); | 287 | xfs_buf_relse(bp); |
100 | } | 288 | } |
289 | ASSERT(pathlen == 0); | ||
101 | 290 | ||
102 | link[ip->i_d.di_size] = '\0'; | 291 | link[ip->i_d.di_size] = '\0'; |
103 | error = 0; | 292 | error = 0; |
@@ -108,10 +297,10 @@ xfs_readlink_bmap( | |||
108 | 297 | ||
109 | int | 298 | int |
110 | xfs_readlink( | 299 | xfs_readlink( |
111 | xfs_inode_t *ip, | 300 | struct xfs_inode *ip, |
112 | char *link) | 301 | char *link) |
113 | { | 302 | { |
114 | xfs_mount_t *mp = ip->i_mount; | 303 | struct xfs_mount *mp = ip->i_mount; |
115 | xfs_fsize_t pathlen; | 304 | xfs_fsize_t pathlen; |
116 | int error = 0; | 305 | int error = 0; |
117 | 306 | ||
@@ -150,26 +339,26 @@ xfs_readlink( | |||
150 | 339 | ||
151 | int | 340 | int |
152 | xfs_symlink( | 341 | xfs_symlink( |
153 | xfs_inode_t *dp, | 342 | struct xfs_inode *dp, |
154 | struct xfs_name *link_name, | 343 | struct xfs_name *link_name, |
155 | const char *target_path, | 344 | const char *target_path, |
156 | umode_t mode, | 345 | umode_t mode, |
157 | xfs_inode_t **ipp) | 346 | struct xfs_inode **ipp) |
158 | { | 347 | { |
159 | xfs_mount_t *mp = dp->i_mount; | 348 | struct xfs_mount *mp = dp->i_mount; |
160 | xfs_trans_t *tp; | 349 | struct xfs_trans *tp = NULL; |
161 | xfs_inode_t *ip; | 350 | struct xfs_inode *ip = NULL; |
162 | int error; | 351 | int error = 0; |
163 | int pathlen; | 352 | int pathlen; |
164 | xfs_bmap_free_t free_list; | 353 | struct xfs_bmap_free free_list; |
165 | xfs_fsblock_t first_block; | 354 | xfs_fsblock_t first_block; |
166 | bool unlock_dp_on_error = false; | 355 | bool unlock_dp_on_error = false; |
167 | uint cancel_flags; | 356 | uint cancel_flags; |
168 | int committed; | 357 | int committed; |
169 | xfs_fileoff_t first_fsb; | 358 | xfs_fileoff_t first_fsb; |
170 | xfs_filblks_t fs_blocks; | 359 | xfs_filblks_t fs_blocks; |
171 | int nmaps; | 360 | int nmaps; |
172 | xfs_bmbt_irec_t mval[XFS_SYMLINK_MAPS]; | 361 | struct xfs_bmbt_irec mval[XFS_SYMLINK_MAPS]; |
173 | xfs_daddr_t d; | 362 | xfs_daddr_t d; |
174 | const char *cur_chunk; | 363 | const char *cur_chunk; |
175 | int byte_cnt; | 364 | int byte_cnt; |
@@ -180,9 +369,6 @@ xfs_symlink( | |||
180 | uint resblks; | 369 | uint resblks; |
181 | 370 | ||
182 | *ipp = NULL; | 371 | *ipp = NULL; |
183 | error = 0; | ||
184 | ip = NULL; | ||
185 | tp = NULL; | ||
186 | 372 | ||
187 | trace_xfs_symlink(dp, link_name); | 373 | trace_xfs_symlink(dp, link_name); |
188 | 374 | ||
@@ -307,6 +493,8 @@ xfs_symlink( | |||
307 | xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE); | 493 | xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE); |
308 | 494 | ||
309 | } else { | 495 | } else { |
496 | int offset; | ||
497 | |||
310 | first_fsb = 0; | 498 | first_fsb = 0; |
311 | nmaps = XFS_SYMLINK_MAPS; | 499 | nmaps = XFS_SYMLINK_MAPS; |
312 | 500 | ||
@@ -322,7 +510,10 @@ xfs_symlink( | |||
322 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | 510 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); |
323 | 511 | ||
324 | cur_chunk = target_path; | 512 | cur_chunk = target_path; |
513 | offset = 0; | ||
325 | for (n = 0; n < nmaps; n++) { | 514 | for (n = 0; n < nmaps; n++) { |
515 | char *buf; | ||
516 | |||
326 | d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); | 517 | d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); |
327 | byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); | 518 | byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); |
328 | bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d, | 519 | bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d, |
@@ -331,15 +522,25 @@ xfs_symlink( | |||
331 | error = ENOMEM; | 522 | error = ENOMEM; |
332 | goto error2; | 523 | goto error2; |
333 | } | 524 | } |
525 | bp->b_ops = &xfs_symlink_buf_ops; | ||
526 | |||
527 | byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt); | ||
334 | if (pathlen < byte_cnt) { | 528 | if (pathlen < byte_cnt) { |
335 | byte_cnt = pathlen; | 529 | byte_cnt = pathlen; |
336 | } | 530 | } |
337 | pathlen -= byte_cnt; | ||
338 | 531 | ||
339 | memcpy(bp->b_addr, cur_chunk, byte_cnt); | 532 | buf = bp->b_addr; |
533 | buf += xfs_symlink_hdr_set(mp, ip->i_ino, offset, | ||
534 | byte_cnt, bp); | ||
535 | |||
536 | memcpy(buf, cur_chunk, byte_cnt); | ||
537 | |||
340 | cur_chunk += byte_cnt; | 538 | cur_chunk += byte_cnt; |
539 | pathlen -= byte_cnt; | ||
540 | offset += byte_cnt; | ||
341 | 541 | ||
342 | xfs_trans_log_buf(tp, bp, 0, byte_cnt - 1); | 542 | xfs_trans_log_buf(tp, bp, 0, (buf + byte_cnt - 1) - |
543 | (char *)bp->b_addr); | ||
343 | } | 544 | } |
344 | } | 545 | } |
345 | 546 | ||
@@ -392,7 +593,7 @@ xfs_symlink( | |||
392 | /* | 593 | /* |
393 | * Free a symlink that has blocks associated with it. | 594 | * Free a symlink that has blocks associated with it. |
394 | */ | 595 | */ |
395 | STATIC int | 596 | int |
396 | xfs_inactive_symlink_rmt( | 597 | xfs_inactive_symlink_rmt( |
397 | xfs_inode_t *ip, | 598 | xfs_inode_t *ip, |
398 | xfs_trans_t **tpp) | 599 | xfs_trans_t **tpp) |
@@ -438,12 +639,12 @@ xfs_inactive_symlink_rmt( | |||
438 | done = 0; | 639 | done = 0; |
439 | xfs_bmap_init(&free_list, &first_block); | 640 | xfs_bmap_init(&free_list, &first_block); |
440 | nmaps = ARRAY_SIZE(mval); | 641 | nmaps = ARRAY_SIZE(mval); |
441 | error = xfs_bmapi_read(ip, 0, XFS_B_TO_FSB(mp, size), | 642 | error = xfs_bmapi_read(ip, 0, xfs_symlink_blocks(mp, size), |
442 | mval, &nmaps, 0); | 643 | mval, &nmaps, 0); |
443 | if (error) | 644 | if (error) |
444 | goto error0; | 645 | goto error0; |
445 | /* | 646 | /* |
446 | * Invalidate the block(s). | 647 | * Invalidate the block(s). No validation is done. |
447 | */ | 648 | */ |
448 | for (i = 0; i < nmaps; i++) { | 649 | for (i = 0; i < nmaps; i++) { |
449 | bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, | 650 | bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, |