diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/dir.c | 25 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 12 | ||||
-rw-r--r-- | fs/ext4/inline.c | 136 | ||||
-rw-r--r-- | fs/ext4/xattr.h | 9 |
4 files changed, 169 insertions, 13 deletions
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 7c9d08b0f2fe..b8d877f6c1fa 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -27,23 +27,11 @@ | |||
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/rbtree.h> | 28 | #include <linux/rbtree.h> |
29 | #include "ext4.h" | 29 | #include "ext4.h" |
30 | 30 | #include "xattr.h" | |
31 | static unsigned char ext4_filetype_table[] = { | ||
32 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK | ||
33 | }; | ||
34 | 31 | ||
35 | static int ext4_dx_readdir(struct file *filp, | 32 | static int ext4_dx_readdir(struct file *filp, |
36 | void *dirent, filldir_t filldir); | 33 | void *dirent, filldir_t filldir); |
37 | 34 | ||
38 | static unsigned char get_dtype(struct super_block *sb, int filetype) | ||
39 | { | ||
40 | if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE) || | ||
41 | (filetype >= EXT4_FT_MAX)) | ||
42 | return DT_UNKNOWN; | ||
43 | |||
44 | return (ext4_filetype_table[filetype]); | ||
45 | } | ||
46 | |||
47 | /** | 35 | /** |
48 | * Check if the given dir-inode refers to an htree-indexed directory | 36 | * Check if the given dir-inode refers to an htree-indexed directory |
49 | * (or a directory which chould potentially get coverted to use htree | 37 | * (or a directory which chould potentially get coverted to use htree |
@@ -68,6 +56,9 @@ static int is_dx_dir(struct inode *inode) | |||
68 | * Return 0 if the directory entry is OK, and 1 if there is a problem | 56 | * Return 0 if the directory entry is OK, and 1 if there is a problem |
69 | * | 57 | * |
70 | * Note: this is the opposite of what ext2 and ext3 historically returned... | 58 | * Note: this is the opposite of what ext2 and ext3 historically returned... |
59 | * | ||
60 | * bh passed here can be an inode block or a dir data block, depending | ||
61 | * on the inode inline data flag. | ||
71 | */ | 62 | */ |
72 | int __ext4_check_dir_entry(const char *function, unsigned int line, | 63 | int __ext4_check_dir_entry(const char *function, unsigned int line, |
73 | struct inode *dir, struct file *filp, | 64 | struct inode *dir, struct file *filp, |
@@ -124,6 +115,14 @@ static int ext4_readdir(struct file *filp, | |||
124 | int ret = 0; | 115 | int ret = 0; |
125 | int dir_has_error = 0; | 116 | int dir_has_error = 0; |
126 | 117 | ||
118 | if (ext4_has_inline_data(inode)) { | ||
119 | int has_inline_data = 1; | ||
120 | ret = ext4_read_inline_dir(filp, dirent, filldir, | ||
121 | &has_inline_data); | ||
122 | if (has_inline_data) | ||
123 | return ret; | ||
124 | } | ||
125 | |||
127 | if (is_dx_dir(inode)) { | 126 | if (is_dx_dir(inode)) { |
128 | err = ext4_dx_readdir(filp, dirent, filldir); | 127 | err = ext4_dx_readdir(filp, dirent, filldir); |
129 | if (err != ERR_BAD_DX_DIR) { | 128 | if (err != ERR_BAD_DX_DIR) { |
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 689ce1d696b8..e3a74658c63c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1989,6 +1989,18 @@ static inline void ext4_update_dx_flag(struct inode *inode) | |||
1989 | EXT4_FEATURE_COMPAT_DIR_INDEX)) | 1989 | EXT4_FEATURE_COMPAT_DIR_INDEX)) |
1990 | ext4_clear_inode_flag(inode, EXT4_INODE_INDEX); | 1990 | ext4_clear_inode_flag(inode, EXT4_INODE_INDEX); |
1991 | } | 1991 | } |
1992 | static unsigned char ext4_filetype_table[] = { | ||
1993 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK | ||
1994 | }; | ||
1995 | |||
1996 | static inline unsigned char get_dtype(struct super_block *sb, int filetype) | ||
1997 | { | ||
1998 | if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE) || | ||
1999 | (filetype >= EXT4_FT_MAX)) | ||
2000 | return DT_UNKNOWN; | ||
2001 | |||
2002 | return ext4_filetype_table[filetype]; | ||
2003 | } | ||
1992 | 2004 | ||
1993 | /* fsync.c */ | 2005 | /* fsync.c */ |
1994 | extern int ext4_sync_file(struct file *, loff_t, loff_t, int); | 2006 | extern int ext4_sync_file(struct file *, loff_t, loff_t, int); |
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index bf7322818738..471504133c76 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c | |||
@@ -1288,6 +1288,142 @@ out: | |||
1288 | return ret; | 1288 | return ret; |
1289 | } | 1289 | } |
1290 | 1290 | ||
1291 | int ext4_read_inline_dir(struct file *filp, | ||
1292 | void *dirent, filldir_t filldir, | ||
1293 | int *has_inline_data) | ||
1294 | { | ||
1295 | int error = 0; | ||
1296 | unsigned int offset, parent_ino; | ||
1297 | int i, stored; | ||
1298 | struct ext4_dir_entry_2 *de; | ||
1299 | struct super_block *sb; | ||
1300 | struct inode *inode = filp->f_path.dentry->d_inode; | ||
1301 | int ret, inline_size = 0; | ||
1302 | struct ext4_iloc iloc; | ||
1303 | void *dir_buf = NULL; | ||
1304 | |||
1305 | ret = ext4_get_inode_loc(inode, &iloc); | ||
1306 | if (ret) | ||
1307 | return ret; | ||
1308 | |||
1309 | down_read(&EXT4_I(inode)->xattr_sem); | ||
1310 | if (!ext4_has_inline_data(inode)) { | ||
1311 | up_read(&EXT4_I(inode)->xattr_sem); | ||
1312 | *has_inline_data = 0; | ||
1313 | goto out; | ||
1314 | } | ||
1315 | |||
1316 | inline_size = ext4_get_inline_size(inode); | ||
1317 | dir_buf = kmalloc(inline_size, GFP_NOFS); | ||
1318 | if (!dir_buf) { | ||
1319 | ret = -ENOMEM; | ||
1320 | up_read(&EXT4_I(inode)->xattr_sem); | ||
1321 | goto out; | ||
1322 | } | ||
1323 | |||
1324 | ret = ext4_read_inline_data(inode, dir_buf, inline_size, &iloc); | ||
1325 | up_read(&EXT4_I(inode)->xattr_sem); | ||
1326 | if (ret < 0) | ||
1327 | goto out; | ||
1328 | |||
1329 | sb = inode->i_sb; | ||
1330 | stored = 0; | ||
1331 | parent_ino = le32_to_cpu(((struct ext4_dir_entry_2 *)dir_buf)->inode); | ||
1332 | |||
1333 | while (!error && !stored && filp->f_pos < inode->i_size) { | ||
1334 | revalidate: | ||
1335 | /* | ||
1336 | * If the version has changed since the last call to | ||
1337 | * readdir(2), then we might be pointing to an invalid | ||
1338 | * dirent right now. Scan from the start of the inline | ||
1339 | * dir to make sure. | ||
1340 | */ | ||
1341 | if (filp->f_version != inode->i_version) { | ||
1342 | for (i = 0; | ||
1343 | i < inode->i_size && i < offset;) { | ||
1344 | if (!i) { | ||
1345 | /* skip "." and ".." if needed. */ | ||
1346 | i += EXT4_INLINE_DOTDOT_SIZE; | ||
1347 | continue; | ||
1348 | } | ||
1349 | de = (struct ext4_dir_entry_2 *) | ||
1350 | (dir_buf + i); | ||
1351 | /* It's too expensive to do a full | ||
1352 | * dirent test each time round this | ||
1353 | * loop, but we do have to test at | ||
1354 | * least that it is non-zero. A | ||
1355 | * failure will be detected in the | ||
1356 | * dirent test below. */ | ||
1357 | if (ext4_rec_len_from_disk(de->rec_len, | ||
1358 | inline_size) < EXT4_DIR_REC_LEN(1)) | ||
1359 | break; | ||
1360 | i += ext4_rec_len_from_disk(de->rec_len, | ||
1361 | inline_size); | ||
1362 | } | ||
1363 | offset = i; | ||
1364 | filp->f_pos = offset; | ||
1365 | filp->f_version = inode->i_version; | ||
1366 | } | ||
1367 | |||
1368 | while (!error && filp->f_pos < inode->i_size) { | ||
1369 | if (filp->f_pos == 0) { | ||
1370 | error = filldir(dirent, ".", 1, 0, inode->i_ino, | ||
1371 | DT_DIR); | ||
1372 | if (error) | ||
1373 | break; | ||
1374 | stored++; | ||
1375 | |||
1376 | error = filldir(dirent, "..", 2, 0, parent_ino, | ||
1377 | DT_DIR); | ||
1378 | if (error) | ||
1379 | break; | ||
1380 | stored++; | ||
1381 | |||
1382 | filp->f_pos = offset = EXT4_INLINE_DOTDOT_SIZE; | ||
1383 | continue; | ||
1384 | } | ||
1385 | |||
1386 | de = (struct ext4_dir_entry_2 *)(dir_buf + offset); | ||
1387 | if (ext4_check_dir_entry(inode, filp, de, | ||
1388 | iloc.bh, dir_buf, | ||
1389 | inline_size, offset)) { | ||
1390 | ret = stored; | ||
1391 | goto out; | ||
1392 | } | ||
1393 | offset += ext4_rec_len_from_disk(de->rec_len, | ||
1394 | inline_size); | ||
1395 | if (le32_to_cpu(de->inode)) { | ||
1396 | /* We might block in the next section | ||
1397 | * if the data destination is | ||
1398 | * currently swapped out. So, use a | ||
1399 | * version stamp to detect whether or | ||
1400 | * not the directory has been modified | ||
1401 | * during the copy operation. | ||
1402 | */ | ||
1403 | u64 version = filp->f_version; | ||
1404 | |||
1405 | error = filldir(dirent, de->name, | ||
1406 | de->name_len, | ||
1407 | filp->f_pos, | ||
1408 | le32_to_cpu(de->inode), | ||
1409 | get_dtype(sb, de->file_type)); | ||
1410 | if (error) | ||
1411 | break; | ||
1412 | if (version != filp->f_version) | ||
1413 | goto revalidate; | ||
1414 | stored++; | ||
1415 | } | ||
1416 | filp->f_pos += ext4_rec_len_from_disk(de->rec_len, | ||
1417 | inline_size); | ||
1418 | } | ||
1419 | offset = 0; | ||
1420 | } | ||
1421 | out: | ||
1422 | kfree(dir_buf); | ||
1423 | brelse(iloc.bh); | ||
1424 | return ret; | ||
1425 | } | ||
1426 | |||
1291 | /* | 1427 | /* |
1292 | * Try to create the inline data for the new dir. | 1428 | * Try to create the inline data for the new dir. |
1293 | * If it succeeds, return 0, otherwise return the error. | 1429 | * If it succeeds, return 0, otherwise return the error. |
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 397ef4bbaf1e..539e6a08c95f 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h | |||
@@ -168,6 +168,9 @@ extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, | |||
168 | extern int ext4_try_create_inline_dir(handle_t *handle, | 168 | extern int ext4_try_create_inline_dir(handle_t *handle, |
169 | struct inode *parent, | 169 | struct inode *parent, |
170 | struct inode *inode); | 170 | struct inode *inode); |
171 | extern int ext4_read_inline_dir(struct file *filp, | ||
172 | void *dirent, filldir_t filldir, | ||
173 | int *has_inline_data); | ||
171 | # else /* CONFIG_EXT4_FS_XATTR */ | 174 | # else /* CONFIG_EXT4_FS_XATTR */ |
172 | 175 | ||
173 | static inline int | 176 | static inline int |
@@ -346,6 +349,12 @@ static inline int ext4_try_create_inline_dir(handle_t *handle, | |||
346 | { | 349 | { |
347 | return 0; | 350 | return 0; |
348 | } | 351 | } |
352 | static inline int ext4_read_inline_dir(struct file *filp, | ||
353 | void *dirent, filldir_t filldir, | ||
354 | int *has_inline_data) | ||
355 | { | ||
356 | return 0; | ||
357 | } | ||
349 | # endif /* CONFIG_EXT4_FS_XATTR */ | 358 | # endif /* CONFIG_EXT4_FS_XATTR */ |
350 | 359 | ||
351 | #ifdef CONFIG_EXT4_FS_SECURITY | 360 | #ifdef CONFIG_EXT4_FS_SECURITY |