diff options
Diffstat (limited to 'fs/xfs/xfs_inode.c')
-rw-r--r-- | fs/xfs/xfs_inode.c | 261 |
1 files changed, 106 insertions, 155 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index f43a6e01d68..6f156faf9d4 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c | |||
@@ -126,6 +126,85 @@ xfs_inobp_check( | |||
126 | #endif | 126 | #endif |
127 | 127 | ||
128 | /* | 128 | /* |
129 | * Find the buffer associated with the given inode map | ||
130 | * We do basic validation checks on the buffer once it has been | ||
131 | * retrieved from disk. | ||
132 | */ | ||
133 | STATIC int | ||
134 | xfs_imap_to_bp( | ||
135 | xfs_mount_t *mp, | ||
136 | xfs_trans_t *tp, | ||
137 | xfs_imap_t *imap, | ||
138 | xfs_buf_t **bpp, | ||
139 | uint buf_flags, | ||
140 | uint imap_flags) | ||
141 | { | ||
142 | int error; | ||
143 | int i; | ||
144 | int ni; | ||
145 | xfs_buf_t *bp; | ||
146 | |||
147 | error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap->im_blkno, | ||
148 | (int)imap->im_len, XFS_BUF_LOCK, &bp); | ||
149 | if (error) { | ||
150 | cmn_err(CE_WARN, "xfs_imap_to_bp: xfs_trans_read_buf()returned " | ||
151 | "an error %d on %s. Returning error.", | ||
152 | error, mp->m_fsname); | ||
153 | return error; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * Validate the magic number and version of every inode in the buffer | ||
158 | * (if DEBUG kernel) or the first inode in the buffer, otherwise. | ||
159 | */ | ||
160 | #ifdef DEBUG | ||
161 | ni = BBTOB(imap->im_len) >> mp->m_sb.sb_inodelog; | ||
162 | #else /* usual case */ | ||
163 | ni = 1; | ||
164 | #endif | ||
165 | |||
166 | for (i = 0; i < ni; i++) { | ||
167 | int di_ok; | ||
168 | xfs_dinode_t *dip; | ||
169 | |||
170 | dip = (xfs_dinode_t *)xfs_buf_offset(bp, | ||
171 | (i << mp->m_sb.sb_inodelog)); | ||
172 | di_ok = be16_to_cpu(dip->di_core.di_magic) == XFS_DINODE_MAGIC && | ||
173 | XFS_DINODE_GOOD_VERSION(dip->di_core.di_version); | ||
174 | if (unlikely(XFS_TEST_ERROR(!di_ok, mp, | ||
175 | XFS_ERRTAG_ITOBP_INOTOBP, | ||
176 | XFS_RANDOM_ITOBP_INOTOBP))) { | ||
177 | if (imap_flags & XFS_IMAP_BULKSTAT) { | ||
178 | xfs_trans_brelse(tp, bp); | ||
179 | return XFS_ERROR(EINVAL); | ||
180 | } | ||
181 | XFS_CORRUPTION_ERROR("xfs_imap_to_bp", | ||
182 | XFS_ERRLEVEL_HIGH, mp, dip); | ||
183 | #ifdef DEBUG | ||
184 | cmn_err(CE_PANIC, | ||
185 | "Device %s - bad inode magic/vsn " | ||
186 | "daddr %lld #%d (magic=%x)", | ||
187 | XFS_BUFTARG_NAME(mp->m_ddev_targp), | ||
188 | (unsigned long long)imap->im_blkno, i, | ||
189 | be16_to_cpu(dip->di_core.di_magic)); | ||
190 | #endif | ||
191 | xfs_trans_brelse(tp, bp); | ||
192 | return XFS_ERROR(EFSCORRUPTED); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | xfs_inobp_check(mp, bp); | ||
197 | |||
198 | /* | ||
199 | * Mark the buffer as an inode buffer now that it looks good | ||
200 | */ | ||
201 | XFS_BUF_SET_VTYPE(bp, B_FS_INO); | ||
202 | |||
203 | *bpp = bp; | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | /* | ||
129 | * This routine is called to map an inode number within a file | 208 | * This routine is called to map an inode number within a file |
130 | * system to the buffer containing the on-disk version of the | 209 | * system to the buffer containing the on-disk version of the |
131 | * inode. It returns a pointer to the buffer containing the | 210 | * inode. It returns a pointer to the buffer containing the |
@@ -147,72 +226,19 @@ xfs_inotobp( | |||
147 | xfs_buf_t **bpp, | 226 | xfs_buf_t **bpp, |
148 | int *offset) | 227 | int *offset) |
149 | { | 228 | { |
150 | int di_ok; | ||
151 | xfs_imap_t imap; | 229 | xfs_imap_t imap; |
152 | xfs_buf_t *bp; | 230 | xfs_buf_t *bp; |
153 | int error; | 231 | int error; |
154 | xfs_dinode_t *dip; | ||
155 | 232 | ||
156 | /* | ||
157 | * Call the space management code to find the location of the | ||
158 | * inode on disk. | ||
159 | */ | ||
160 | imap.im_blkno = 0; | 233 | imap.im_blkno = 0; |
161 | error = xfs_imap(mp, tp, ino, &imap, XFS_IMAP_LOOKUP); | 234 | error = xfs_imap(mp, tp, ino, &imap, XFS_IMAP_LOOKUP); |
162 | if (error != 0) { | 235 | if (error) |
163 | cmn_err(CE_WARN, | ||
164 | "xfs_inotobp: xfs_imap() returned an " | ||
165 | "error %d on %s. Returning error.", error, mp->m_fsname); | ||
166 | return error; | 236 | return error; |
167 | } | ||
168 | |||
169 | /* | ||
170 | * If the inode number maps to a block outside the bounds of the | ||
171 | * file system then return NULL rather than calling read_buf | ||
172 | * and panicing when we get an error from the driver. | ||
173 | */ | ||
174 | if ((imap.im_blkno + imap.im_len) > | ||
175 | XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) { | ||
176 | cmn_err(CE_WARN, | ||
177 | "xfs_inotobp: inode number (%llu + %d) maps to a block outside the bounds " | ||
178 | "of the file system %s. Returning EINVAL.", | ||
179 | (unsigned long long)imap.im_blkno, | ||
180 | imap.im_len, mp->m_fsname); | ||
181 | return XFS_ERROR(EINVAL); | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Read in the buffer. If tp is NULL, xfs_trans_read_buf() will | ||
186 | * default to just a read_buf() call. | ||
187 | */ | ||
188 | error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno, | ||
189 | (int)imap.im_len, XFS_BUF_LOCK, &bp); | ||
190 | 237 | ||
191 | if (error) { | 238 | error = xfs_imap_to_bp(mp, tp, &imap, &bp, XFS_BUF_LOCK, 0); |
192 | cmn_err(CE_WARN, | 239 | if (error) |
193 | "xfs_inotobp: xfs_trans_read_buf() returned an " | ||
194 | "error %d on %s. Returning error.", error, mp->m_fsname); | ||
195 | return error; | 240 | return error; |
196 | } | ||
197 | dip = (xfs_dinode_t *)xfs_buf_offset(bp, 0); | ||
198 | di_ok = | ||
199 | be16_to_cpu(dip->di_core.di_magic) == XFS_DINODE_MAGIC && | ||
200 | XFS_DINODE_GOOD_VERSION(dip->di_core.di_version); | ||
201 | if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP, | ||
202 | XFS_RANDOM_ITOBP_INOTOBP))) { | ||
203 | XFS_CORRUPTION_ERROR("xfs_inotobp", XFS_ERRLEVEL_LOW, mp, dip); | ||
204 | xfs_trans_brelse(tp, bp); | ||
205 | cmn_err(CE_WARN, | ||
206 | "xfs_inotobp: XFS_TEST_ERROR() returned an " | ||
207 | "error on %s. Returning EFSCORRUPTED.", mp->m_fsname); | ||
208 | return XFS_ERROR(EFSCORRUPTED); | ||
209 | } | ||
210 | 241 | ||
211 | xfs_inobp_check(mp, bp); | ||
212 | |||
213 | /* | ||
214 | * Set *dipp to point to the on-disk inode in the buffer. | ||
215 | */ | ||
216 | *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset); | 242 | *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset); |
217 | *bpp = bp; | 243 | *bpp = bp; |
218 | *offset = imap.im_boffset; | 244 | *offset = imap.im_boffset; |
@@ -253,41 +279,15 @@ xfs_itobp( | |||
253 | xfs_imap_t imap; | 279 | xfs_imap_t imap; |
254 | xfs_buf_t *bp; | 280 | xfs_buf_t *bp; |
255 | int error; | 281 | int error; |
256 | int i; | ||
257 | int ni; | ||
258 | 282 | ||
259 | if (ip->i_blkno == (xfs_daddr_t)0) { | 283 | if (ip->i_blkno == (xfs_daddr_t)0) { |
260 | /* | ||
261 | * Call the space management code to find the location of the | ||
262 | * inode on disk. | ||
263 | */ | ||
264 | imap.im_blkno = bno; | 284 | imap.im_blkno = bno; |
265 | if ((error = xfs_imap(mp, tp, ip->i_ino, &imap, | 285 | error = xfs_imap(mp, tp, ip->i_ino, &imap, |
266 | XFS_IMAP_LOOKUP | imap_flags))) | 286 | XFS_IMAP_LOOKUP | imap_flags); |
287 | if (error) | ||
267 | return error; | 288 | return error; |
268 | 289 | ||
269 | /* | 290 | /* |
270 | * If the inode number maps to a block outside the bounds | ||
271 | * of the file system then return NULL rather than calling | ||
272 | * read_buf and panicing when we get an error from the | ||
273 | * driver. | ||
274 | */ | ||
275 | if ((imap.im_blkno + imap.im_len) > | ||
276 | XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) { | ||
277 | #ifdef DEBUG | ||
278 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: " | ||
279 | "(imap.im_blkno (0x%llx) " | ||
280 | "+ imap.im_len (0x%llx)) > " | ||
281 | " XFS_FSB_TO_BB(mp, " | ||
282 | "mp->m_sb.sb_dblocks) (0x%llx)", | ||
283 | (unsigned long long) imap.im_blkno, | ||
284 | (unsigned long long) imap.im_len, | ||
285 | XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)); | ||
286 | #endif /* DEBUG */ | ||
287 | return XFS_ERROR(EINVAL); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * Fill in the fields in the inode that will be used to | 291 | * Fill in the fields in the inode that will be used to |
292 | * map the inode to its buffer from now on. | 292 | * map the inode to its buffer from now on. |
293 | */ | 293 | */ |
@@ -305,76 +305,10 @@ xfs_itobp( | |||
305 | } | 305 | } |
306 | ASSERT(bno == 0 || bno == imap.im_blkno); | 306 | ASSERT(bno == 0 || bno == imap.im_blkno); |
307 | 307 | ||
308 | /* | 308 | error = xfs_imap_to_bp(mp, tp, &imap, &bp, XFS_BUF_LOCK, imap_flags); |
309 | * Read in the buffer. If tp is NULL, xfs_trans_read_buf() will | 309 | if (error) |
310 | * default to just a read_buf() call. | ||
311 | */ | ||
312 | error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno, | ||
313 | (int)imap.im_len, XFS_BUF_LOCK, &bp); | ||
314 | if (error) { | ||
315 | #ifdef DEBUG | ||
316 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: " | ||
317 | "xfs_trans_read_buf() returned error %d, " | ||
318 | "imap.im_blkno 0x%llx, imap.im_len 0x%llx", | ||
319 | error, (unsigned long long) imap.im_blkno, | ||
320 | (unsigned long long) imap.im_len); | ||
321 | #endif /* DEBUG */ | ||
322 | return error; | 310 | return error; |
323 | } | ||
324 | |||
325 | /* | ||
326 | * Validate the magic number and version of every inode in the buffer | ||
327 | * (if DEBUG kernel) or the first inode in the buffer, otherwise. | ||
328 | * No validation is done here in userspace (xfs_repair). | ||
329 | */ | ||
330 | #if !defined(__KERNEL__) | ||
331 | ni = 0; | ||
332 | #elif defined(DEBUG) | ||
333 | ni = BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog; | ||
334 | #else /* usual case */ | ||
335 | ni = 1; | ||
336 | #endif | ||
337 | |||
338 | for (i = 0; i < ni; i++) { | ||
339 | int di_ok; | ||
340 | xfs_dinode_t *dip; | ||
341 | 311 | ||
342 | dip = (xfs_dinode_t *)xfs_buf_offset(bp, | ||
343 | (i << mp->m_sb.sb_inodelog)); | ||
344 | di_ok = be16_to_cpu(dip->di_core.di_magic) == XFS_DINODE_MAGIC && | ||
345 | XFS_DINODE_GOOD_VERSION(dip->di_core.di_version); | ||
346 | if (unlikely(XFS_TEST_ERROR(!di_ok, mp, | ||
347 | XFS_ERRTAG_ITOBP_INOTOBP, | ||
348 | XFS_RANDOM_ITOBP_INOTOBP))) { | ||
349 | if (imap_flags & XFS_IMAP_BULKSTAT) { | ||
350 | xfs_trans_brelse(tp, bp); | ||
351 | return XFS_ERROR(EINVAL); | ||
352 | } | ||
353 | #ifdef DEBUG | ||
354 | cmn_err(CE_ALERT, | ||
355 | "Device %s - bad inode magic/vsn " | ||
356 | "daddr %lld #%d (magic=%x)", | ||
357 | XFS_BUFTARG_NAME(mp->m_ddev_targp), | ||
358 | (unsigned long long)imap.im_blkno, i, | ||
359 | be16_to_cpu(dip->di_core.di_magic)); | ||
360 | #endif | ||
361 | XFS_CORRUPTION_ERROR("xfs_itobp", XFS_ERRLEVEL_HIGH, | ||
362 | mp, dip); | ||
363 | xfs_trans_brelse(tp, bp); | ||
364 | return XFS_ERROR(EFSCORRUPTED); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | xfs_inobp_check(mp, bp); | ||
369 | |||
370 | /* | ||
371 | * Mark the buffer as an inode buffer now that it looks good | ||
372 | */ | ||
373 | XFS_BUF_SET_VTYPE(bp, B_FS_INO); | ||
374 | |||
375 | /* | ||
376 | * Set *dipp to point to the on-disk inode in the buffer. | ||
377 | */ | ||
378 | *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset); | 312 | *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset); |
379 | *bpp = bp; | 313 | *bpp = bp; |
380 | return 0; | 314 | return 0; |
@@ -2678,14 +2612,31 @@ xfs_imap( | |||
2678 | fsbno = imap->im_blkno ? | 2612 | fsbno = imap->im_blkno ? |
2679 | XFS_DADDR_TO_FSB(mp, imap->im_blkno) : NULLFSBLOCK; | 2613 | XFS_DADDR_TO_FSB(mp, imap->im_blkno) : NULLFSBLOCK; |
2680 | error = xfs_dilocate(mp, tp, ino, &fsbno, &len, &off, flags); | 2614 | error = xfs_dilocate(mp, tp, ino, &fsbno, &len, &off, flags); |
2681 | if (error != 0) { | 2615 | if (error) |
2682 | return error; | 2616 | return error; |
2683 | } | 2617 | |
2684 | imap->im_blkno = XFS_FSB_TO_DADDR(mp, fsbno); | 2618 | imap->im_blkno = XFS_FSB_TO_DADDR(mp, fsbno); |
2685 | imap->im_len = XFS_FSB_TO_BB(mp, len); | 2619 | imap->im_len = XFS_FSB_TO_BB(mp, len); |
2686 | imap->im_agblkno = XFS_FSB_TO_AGBNO(mp, fsbno); | 2620 | imap->im_agblkno = XFS_FSB_TO_AGBNO(mp, fsbno); |
2687 | imap->im_ioffset = (ushort)off; | 2621 | imap->im_ioffset = (ushort)off; |
2688 | imap->im_boffset = (ushort)(off << mp->m_sb.sb_inodelog); | 2622 | imap->im_boffset = (ushort)(off << mp->m_sb.sb_inodelog); |
2623 | |||
2624 | /* | ||
2625 | * If the inode number maps to a block outside the bounds | ||
2626 | * of the file system then return NULL rather than calling | ||
2627 | * read_buf and panicing when we get an error from the | ||
2628 | * driver. | ||
2629 | */ | ||
2630 | if ((imap->im_blkno + imap->im_len) > | ||
2631 | XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) { | ||
2632 | xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " | ||
2633 | "(imap->im_blkno (0x%llx) + imap->im_len (0x%llx)) > " | ||
2634 | " XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) (0x%llx)", | ||
2635 | (unsigned long long) imap->im_blkno, | ||
2636 | (unsigned long long) imap->im_len, | ||
2637 | XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)); | ||
2638 | return EINVAL; | ||
2639 | } | ||
2689 | return 0; | 2640 | return 0; |
2690 | } | 2641 | } |
2691 | 2642 | ||