diff options
Diffstat (limited to 'fs/gfs2/dir.c')
-rw-r--r-- | fs/gfs2/dir.c | 38 |
1 files changed, 33 insertions, 5 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 82a1ac7895a2..a96fa07b3f3b 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c | |||
@@ -1262,9 +1262,10 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, | |||
1262 | u64 leaf_no) | 1262 | u64 leaf_no) |
1263 | { | 1263 | { |
1264 | struct gfs2_inode *ip = GFS2_I(inode); | 1264 | struct gfs2_inode *ip = GFS2_I(inode); |
1265 | struct gfs2_sbd *sdp = GFS2_SB(inode); | ||
1265 | struct buffer_head *bh; | 1266 | struct buffer_head *bh; |
1266 | struct gfs2_leaf *lf; | 1267 | struct gfs2_leaf *lf; |
1267 | unsigned entries = 0; | 1268 | unsigned entries = 0, entries2 = 0; |
1268 | unsigned leaves = 0; | 1269 | unsigned leaves = 0; |
1269 | const struct gfs2_dirent **darr, *dent; | 1270 | const struct gfs2_dirent **darr, *dent; |
1270 | struct dirent_gather g; | 1271 | struct dirent_gather g; |
@@ -1290,7 +1291,13 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, | |||
1290 | return 0; | 1291 | return 0; |
1291 | 1292 | ||
1292 | error = -ENOMEM; | 1293 | error = -ENOMEM; |
1293 | larr = vmalloc((leaves + entries) * sizeof(void *)); | 1294 | /* |
1295 | * The extra 99 entries are not normally used, but are a buffer | ||
1296 | * zone in case the number of entries in the leaf is corrupt. | ||
1297 | * 99 is the maximum number of entries that can fit in a single | ||
1298 | * leaf block. | ||
1299 | */ | ||
1300 | larr = vmalloc((leaves + entries + 99) * sizeof(void *)); | ||
1294 | if (!larr) | 1301 | if (!larr) |
1295 | goto out; | 1302 | goto out; |
1296 | darr = (const struct gfs2_dirent **)(larr + leaves); | 1303 | darr = (const struct gfs2_dirent **)(larr + leaves); |
@@ -1305,10 +1312,20 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, | |||
1305 | lf = (struct gfs2_leaf *)bh->b_data; | 1312 | lf = (struct gfs2_leaf *)bh->b_data; |
1306 | lfn = be64_to_cpu(lf->lf_next); | 1313 | lfn = be64_to_cpu(lf->lf_next); |
1307 | if (lf->lf_entries) { | 1314 | if (lf->lf_entries) { |
1315 | entries2 += be16_to_cpu(lf->lf_entries); | ||
1308 | dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, | 1316 | dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, |
1309 | gfs2_dirent_gather, NULL, &g); | 1317 | gfs2_dirent_gather, NULL, &g); |
1310 | error = PTR_ERR(dent); | 1318 | error = PTR_ERR(dent); |
1311 | if (IS_ERR(dent)) { | 1319 | if (IS_ERR(dent)) |
1320 | goto out_kfree; | ||
1321 | if (entries2 != g.offset) { | ||
1322 | fs_warn(sdp, "Number of entries corrupt in dir " | ||
1323 | "leaf %llu, entries2 (%u) != " | ||
1324 | "g.offset (%u)\n", | ||
1325 | (unsigned long long)bh->b_blocknr, | ||
1326 | entries2, g.offset); | ||
1327 | |||
1328 | error = -EIO; | ||
1312 | goto out_kfree; | 1329 | goto out_kfree; |
1313 | } | 1330 | } |
1314 | error = 0; | 1331 | error = 0; |
@@ -1318,6 +1335,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, | |||
1318 | } | 1335 | } |
1319 | } while(lfn); | 1336 | } while(lfn); |
1320 | 1337 | ||
1338 | BUG_ON(entries2 != entries); | ||
1321 | error = do_filldir_main(ip, offset, opaque, filldir, darr, | 1339 | error = do_filldir_main(ip, offset, opaque, filldir, darr, |
1322 | entries, copied); | 1340 | entries, copied); |
1323 | out_kfree: | 1341 | out_kfree: |
@@ -1401,6 +1419,7 @@ int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, | |||
1401 | filldir_t filldir) | 1419 | filldir_t filldir) |
1402 | { | 1420 | { |
1403 | struct gfs2_inode *dip = GFS2_I(inode); | 1421 | struct gfs2_inode *dip = GFS2_I(inode); |
1422 | struct gfs2_sbd *sdp = GFS2_SB(inode); | ||
1404 | struct dirent_gather g; | 1423 | struct dirent_gather g; |
1405 | const struct gfs2_dirent **darr, *dent; | 1424 | const struct gfs2_dirent **darr, *dent; |
1406 | struct buffer_head *dibh; | 1425 | struct buffer_head *dibh; |
@@ -1423,8 +1442,8 @@ int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, | |||
1423 | return error; | 1442 | return error; |
1424 | 1443 | ||
1425 | error = -ENOMEM; | 1444 | error = -ENOMEM; |
1426 | darr = kmalloc(dip->i_di.di_entries * sizeof(struct gfs2_dirent *), | 1445 | /* 96 is max number of dirents which can be stuffed into an inode */ |
1427 | GFP_KERNEL); | 1446 | darr = kmalloc(96 * sizeof(struct gfs2_dirent *), GFP_KERNEL); |
1428 | if (darr) { | 1447 | if (darr) { |
1429 | g.pdent = darr; | 1448 | g.pdent = darr; |
1430 | g.offset = 0; | 1449 | g.offset = 0; |
@@ -1434,6 +1453,15 @@ int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, | |||
1434 | error = PTR_ERR(dent); | 1453 | error = PTR_ERR(dent); |
1435 | goto out; | 1454 | goto out; |
1436 | } | 1455 | } |
1456 | if (dip->i_di.di_entries != g.offset) { | ||
1457 | fs_warn(sdp, "Number of entries corrupt in dir %llu, " | ||
1458 | "ip->i_di.di_entries (%u) != g.offset (%u)\n", | ||
1459 | (unsigned long long)dip->i_num.no_addr, | ||
1460 | dip->i_di.di_entries, | ||
1461 | g.offset); | ||
1462 | error = -EIO; | ||
1463 | goto out; | ||
1464 | } | ||
1437 | error = do_filldir_main(dip, offset, opaque, filldir, darr, | 1465 | error = do_filldir_main(dip, offset, opaque, filldir, darr, |
1438 | dip->i_di.di_entries, &copied); | 1466 | dip->i_di.di_entries, &copied); |
1439 | out: | 1467 | out: |