diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2009-08-27 09:34:19 -0400 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2009-09-10 02:58:11 -0400 |
commit | e3c3efc243462d67ba9fa7f67620dcbc4597bf0a (patch) | |
tree | ae29cf8fade7c9bfac77f7ceff0381e775d80c0d /fs/ubifs | |
parent | 8c6866b071cb242584df287dbd342e5815e8407f (diff) |
UBIFS: add inode size debugging check
Add one more check to UBIFS - a check that makes sure that there
are no data nodes beyond inode size. And few commantaries fixes
along the line.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Reviewed-by: Adrian Hunter <Adrian.Hunter@nokia.com>
Diffstat (limited to 'fs/ubifs')
-rw-r--r-- | fs/ubifs/debug.h | 3 | ||||
-rw-r--r-- | fs/ubifs/key.h | 13 | ||||
-rw-r--r-- | fs/ubifs/super.c | 2 | ||||
-rw-r--r-- | fs/ubifs/tnc.c | 76 |
4 files changed, 91 insertions, 3 deletions
diff --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h index c1cd73b2e06e..fe2c9274c6a9 100644 --- a/fs/ubifs/debug.h +++ b/fs/ubifs/debug.h | |||
@@ -321,6 +321,8 @@ void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, | |||
321 | int dbg_check_lprops(struct ubifs_info *c); | 321 | int dbg_check_lprops(struct ubifs_info *c); |
322 | int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, | 322 | int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, |
323 | int row, int col); | 323 | int row, int col); |
324 | int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, | ||
325 | loff_t size); | ||
324 | 326 | ||
325 | /* Force the use of in-the-gaps method for testing */ | 327 | /* Force the use of in-the-gaps method for testing */ |
326 | 328 | ||
@@ -460,6 +462,7 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c); | |||
460 | #define dbg_check_heap(c, heap, cat, add_pos) ({}) | 462 | #define dbg_check_heap(c, heap, cat, add_pos) ({}) |
461 | #define dbg_check_lprops(c) 0 | 463 | #define dbg_check_lprops(c) 0 |
462 | #define dbg_check_lpt_nodes(c, cnode, row, col) 0 | 464 | #define dbg_check_lpt_nodes(c, cnode, row, col) 0 |
465 | #define dbg_check_inode_size(c, inode, size) 0 | ||
463 | #define dbg_force_in_the_gaps_enabled 0 | 466 | #define dbg_force_in_the_gaps_enabled 0 |
464 | #define dbg_force_in_the_gaps() 0 | 467 | #define dbg_force_in_the_gaps() 0 |
465 | #define dbg_failure_mode 0 | 468 | #define dbg_failure_mode 0 |
diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h index 919af84b9e17..0f530c684f0b 100644 --- a/fs/ubifs/key.h +++ b/fs/ubifs/key.h | |||
@@ -278,6 +278,18 @@ static inline void data_key_init(const struct ubifs_info *c, | |||
278 | } | 278 | } |
279 | 279 | ||
280 | /** | 280 | /** |
281 | * highest_data_key - get the highest possible data key for an inode. | ||
282 | * @c: UBIFS file-system description object | ||
283 | * @key: key to initialize | ||
284 | * @inum: inode number | ||
285 | */ | ||
286 | static inline void highest_data_key(const struct ubifs_info *c, | ||
287 | union ubifs_key *key, ino_t inum) | ||
288 | { | ||
289 | data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK); | ||
290 | } | ||
291 | |||
292 | /** | ||
281 | * trun_key_init - initialize truncation node key. | 293 | * trun_key_init - initialize truncation node key. |
282 | * @c: UBIFS file-system description object | 294 | * @c: UBIFS file-system description object |
283 | * @key: key to initialize | 295 | * @key: key to initialize |
@@ -518,4 +530,5 @@ static inline unsigned long long key_max_inode_size(const struct ubifs_info *c) | |||
518 | return 0; | 530 | return 0; |
519 | } | 531 | } |
520 | } | 532 | } |
533 | |||
521 | #endif /* !__UBIFS_KEY_H__ */ | 534 | #endif /* !__UBIFS_KEY_H__ */ |
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index b541bd75bd1f..7e2b3d4d487a 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c | |||
@@ -317,6 +317,8 @@ static int ubifs_write_inode(struct inode *inode, int wait) | |||
317 | if (err) | 317 | if (err) |
318 | ubifs_err("can't write inode %lu, error %d", | 318 | ubifs_err("can't write inode %lu, error %d", |
319 | inode->i_ino, err); | 319 | inode->i_ino, err); |
320 | else | ||
321 | err = dbg_check_inode_size(c, inode, ui->ui_size); | ||
320 | } | 322 | } |
321 | 323 | ||
322 | ui->dirty = 0; | 324 | ui->dirty = 0; |
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index f249f7b0d656..e5b1a7d00fa0 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c | |||
@@ -1159,8 +1159,8 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, | |||
1159 | * o exact match, i.e. the found zero-level znode contains key @key, then %1 | 1159 | * o exact match, i.e. the found zero-level znode contains key @key, then %1 |
1160 | * is returned and slot number of the matched branch is stored in @n; | 1160 | * is returned and slot number of the matched branch is stored in @n; |
1161 | * o not exact match, which means that zero-level znode does not contain | 1161 | * o not exact match, which means that zero-level znode does not contain |
1162 | * @key, then %0 is returned and slot number of the closed branch is stored | 1162 | * @key, then %0 is returned and slot number of the closest branch is stored |
1163 | * in @n; | 1163 | * in @n; |
1164 | * o @key is so small that it is even less than the lowest key of the | 1164 | * o @key is so small that it is even less than the lowest key of the |
1165 | * leftmost zero-level node, then %0 is returned and %0 is stored in @n. | 1165 | * leftmost zero-level node, then %0 is returned and %0 is stored in @n. |
1166 | * | 1166 | * |
@@ -1433,7 +1433,7 @@ static int maybe_leb_gced(struct ubifs_info *c, int lnum, int gc_seq1) | |||
1433 | * @lnum: LEB number is returned here | 1433 | * @lnum: LEB number is returned here |
1434 | * @offs: offset is returned here | 1434 | * @offs: offset is returned here |
1435 | * | 1435 | * |
1436 | * This function look up and reads node with key @key. The caller has to make | 1436 | * This function looks up and reads node with key @key. The caller has to make |
1437 | * sure the @node buffer is large enough to fit the node. Returns zero in case | 1437 | * sure the @node buffer is large enough to fit the node. Returns zero in case |
1438 | * of success, %-ENOENT if the node was not found, and a negative error code in | 1438 | * of success, %-ENOENT if the node was not found, and a negative error code in |
1439 | * case of failure. The node location can be returned in @lnum and @offs. | 1439 | * case of failure. The node location can be returned in @lnum and @offs. |
@@ -3268,3 +3268,73 @@ out_unlock: | |||
3268 | mutex_unlock(&c->tnc_mutex); | 3268 | mutex_unlock(&c->tnc_mutex); |
3269 | return err; | 3269 | return err; |
3270 | } | 3270 | } |
3271 | |||
3272 | #ifdef CONFIG_UBIFS_FS_DEBUG | ||
3273 | |||
3274 | /** | ||
3275 | * dbg_check_inode_size - check if inode size is correct. | ||
3276 | * @c: UBIFS file-system description object | ||
3277 | * @inum: inode number | ||
3278 | * @size: inode size | ||
3279 | * | ||
3280 | * This function makes sure that the inode size (@size) is correct and it does | ||
3281 | * not have any pages beyond @size. Returns zero if the inode is OK, %-EINVAL | ||
3282 | * if it has a data page beyond @size, and other negative error code in case of | ||
3283 | * other errors. | ||
3284 | */ | ||
3285 | int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, | ||
3286 | loff_t size) | ||
3287 | { | ||
3288 | int err, n; | ||
3289 | union ubifs_key from_key, to_key, *key; | ||
3290 | struct ubifs_znode *znode; | ||
3291 | unsigned int block; | ||
3292 | |||
3293 | if (!S_ISREG(inode->i_mode)) | ||
3294 | return 0; | ||
3295 | if (!(ubifs_chk_flags & UBIFS_CHK_GEN)) | ||
3296 | return 0; | ||
3297 | |||
3298 | block = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT; | ||
3299 | data_key_init(c, &from_key, inode->i_ino, block); | ||
3300 | highest_data_key(c, &to_key, inode->i_ino); | ||
3301 | |||
3302 | mutex_lock(&c->tnc_mutex); | ||
3303 | err = ubifs_lookup_level0(c, &from_key, &znode, &n); | ||
3304 | if (err < 0) | ||
3305 | goto out_unlock; | ||
3306 | |||
3307 | if (err) { | ||
3308 | err = -EINVAL; | ||
3309 | key = &from_key; | ||
3310 | goto out_dump; | ||
3311 | } | ||
3312 | |||
3313 | err = tnc_next(c, &znode, &n); | ||
3314 | if (err == -ENOENT) { | ||
3315 | err = 0; | ||
3316 | goto out_unlock; | ||
3317 | } | ||
3318 | if (err < 0) | ||
3319 | goto out_unlock; | ||
3320 | |||
3321 | ubifs_assert(err == 0); | ||
3322 | key = &znode->zbranch[n].key; | ||
3323 | if (!key_in_range(c, key, &from_key, &to_key)) | ||
3324 | goto out_unlock; | ||
3325 | |||
3326 | out_dump: | ||
3327 | block = key_block(c, key); | ||
3328 | ubifs_err("inode %lu has size %lld, but there are data at offset %lld " | ||
3329 | "(data key %s)", (unsigned long)inode->i_ino, size, | ||
3330 | ((loff_t)block) << UBIFS_BLOCK_SHIFT, DBGKEY(key)); | ||
3331 | dbg_dump_inode(c, inode); | ||
3332 | dbg_dump_stack(); | ||
3333 | err = -EINVAL; | ||
3334 | |||
3335 | out_unlock: | ||
3336 | mutex_unlock(&c->tnc_mutex); | ||
3337 | return err; | ||
3338 | } | ||
3339 | |||
3340 | #endif /* CONFIG_UBIFS_FS_DEBUG */ | ||