diff options
Diffstat (limited to 'fs/ext3/dir.c')
-rw-r--r-- | fs/ext3/dir.c | 174 |
1 files changed, 123 insertions, 51 deletions
diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c index 34f0a072b935..92490e9f85ca 100644 --- a/fs/ext3/dir.c +++ b/fs/ext3/dir.c | |||
@@ -21,35 +21,15 @@ | |||
21 | * | 21 | * |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/fs.h> | 24 | #include <linux/compat.h> |
25 | #include <linux/jbd.h> | 25 | #include "ext3.h" |
26 | #include <linux/ext3_fs.h> | ||
27 | #include <linux/buffer_head.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/rbtree.h> | ||
30 | 26 | ||
31 | static unsigned char ext3_filetype_table[] = { | 27 | static unsigned char ext3_filetype_table[] = { |
32 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK | 28 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK |
33 | }; | 29 | }; |
34 | 30 | ||
35 | static int ext3_readdir(struct file *, void *, filldir_t); | ||
36 | static int ext3_dx_readdir(struct file * filp, | 31 | static int ext3_dx_readdir(struct file * filp, |
37 | void * dirent, filldir_t filldir); | 32 | void * dirent, filldir_t filldir); |
38 | static int ext3_release_dir (struct inode * inode, | ||
39 | struct file * filp); | ||
40 | |||
41 | const struct file_operations ext3_dir_operations = { | ||
42 | .llseek = generic_file_llseek, | ||
43 | .read = generic_read_dir, | ||
44 | .readdir = ext3_readdir, /* we take BKL. needed?*/ | ||
45 | .unlocked_ioctl = ext3_ioctl, | ||
46 | #ifdef CONFIG_COMPAT | ||
47 | .compat_ioctl = ext3_compat_ioctl, | ||
48 | #endif | ||
49 | .fsync = ext3_sync_file, /* BKL held */ | ||
50 | .release = ext3_release_dir, | ||
51 | }; | ||
52 | |||
53 | 33 | ||
54 | static unsigned char get_dtype(struct super_block *sb, int filetype) | 34 | static unsigned char get_dtype(struct super_block *sb, int filetype) |
55 | { | 35 | { |
@@ -60,6 +40,25 @@ static unsigned char get_dtype(struct super_block *sb, int filetype) | |||
60 | return (ext3_filetype_table[filetype]); | 40 | return (ext3_filetype_table[filetype]); |
61 | } | 41 | } |
62 | 42 | ||
43 | /** | ||
44 | * Check if the given dir-inode refers to an htree-indexed directory | ||
45 | * (or a directory which chould potentially get coverted to use htree | ||
46 | * indexing). | ||
47 | * | ||
48 | * Return 1 if it is a dx dir, 0 if not | ||
49 | */ | ||
50 | static int is_dx_dir(struct inode *inode) | ||
51 | { | ||
52 | struct super_block *sb = inode->i_sb; | ||
53 | |||
54 | if (EXT3_HAS_COMPAT_FEATURE(inode->i_sb, | ||
55 | EXT3_FEATURE_COMPAT_DIR_INDEX) && | ||
56 | ((EXT3_I(inode)->i_flags & EXT3_INDEX_FL) || | ||
57 | ((inode->i_size >> sb->s_blocksize_bits) == 1))) | ||
58 | return 1; | ||
59 | |||
60 | return 0; | ||
61 | } | ||
63 | 62 | ||
64 | int ext3_check_dir_entry (const char * function, struct inode * dir, | 63 | int ext3_check_dir_entry (const char * function, struct inode * dir, |
65 | struct ext3_dir_entry_2 * de, | 64 | struct ext3_dir_entry_2 * de, |
@@ -99,18 +98,13 @@ static int ext3_readdir(struct file * filp, | |||
99 | unsigned long offset; | 98 | unsigned long offset; |
100 | int i, stored; | 99 | int i, stored; |
101 | struct ext3_dir_entry_2 *de; | 100 | struct ext3_dir_entry_2 *de; |
102 | struct super_block *sb; | ||
103 | int err; | 101 | int err; |
104 | struct inode *inode = filp->f_path.dentry->d_inode; | 102 | struct inode *inode = filp->f_path.dentry->d_inode; |
103 | struct super_block *sb = inode->i_sb; | ||
105 | int ret = 0; | 104 | int ret = 0; |
106 | int dir_has_error = 0; | 105 | int dir_has_error = 0; |
107 | 106 | ||
108 | sb = inode->i_sb; | 107 | if (is_dx_dir(inode)) { |
109 | |||
110 | if (EXT3_HAS_COMPAT_FEATURE(inode->i_sb, | ||
111 | EXT3_FEATURE_COMPAT_DIR_INDEX) && | ||
112 | ((EXT3_I(inode)->i_flags & EXT3_INDEX_FL) || | ||
113 | ((inode->i_size >> sb->s_blocksize_bits) == 1))) { | ||
114 | err = ext3_dx_readdir(filp, dirent, filldir); | 108 | err = ext3_dx_readdir(filp, dirent, filldir); |
115 | if (err != ERR_BAD_DX_DIR) { | 109 | if (err != ERR_BAD_DX_DIR) { |
116 | ret = err; | 110 | ret = err; |
@@ -232,22 +226,87 @@ out: | |||
232 | return ret; | 226 | return ret; |
233 | } | 227 | } |
234 | 228 | ||
229 | static inline int is_32bit_api(void) | ||
230 | { | ||
231 | #ifdef CONFIG_COMPAT | ||
232 | return is_compat_task(); | ||
233 | #else | ||
234 | return (BITS_PER_LONG == 32); | ||
235 | #endif | ||
236 | } | ||
237 | |||
235 | /* | 238 | /* |
236 | * These functions convert from the major/minor hash to an f_pos | 239 | * These functions convert from the major/minor hash to an f_pos |
237 | * value. | 240 | * value for dx directories |
238 | * | 241 | * |
239 | * Currently we only use major hash numer. This is unfortunate, but | 242 | * Upper layer (for example NFS) should specify FMODE_32BITHASH or |
240 | * on 32-bit machines, the same VFS interface is used for lseek and | 243 | * FMODE_64BITHASH explicitly. On the other hand, we allow ext3 to be mounted |
241 | * llseek, so if we use the 64 bit offset, then the 32-bit versions of | 244 | * directly on both 32-bit and 64-bit nodes, under such case, neither |
242 | * lseek/telldir/seekdir will blow out spectacularly, and from within | 245 | * FMODE_32BITHASH nor FMODE_64BITHASH is specified. |
243 | * the ext2 low-level routine, we don't know if we're being called by | ||
244 | * a 64-bit version of the system call or the 32-bit version of the | ||
245 | * system call. Worse yet, NFSv2 only allows for a 32-bit readdir | ||
246 | * cookie. Sigh. | ||
247 | */ | 246 | */ |
248 | #define hash2pos(major, minor) (major >> 1) | 247 | static inline loff_t hash2pos(struct file *filp, __u32 major, __u32 minor) |
249 | #define pos2maj_hash(pos) ((pos << 1) & 0xffffffff) | 248 | { |
250 | #define pos2min_hash(pos) (0) | 249 | if ((filp->f_mode & FMODE_32BITHASH) || |
250 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
251 | return major >> 1; | ||
252 | else | ||
253 | return ((__u64)(major >> 1) << 32) | (__u64)minor; | ||
254 | } | ||
255 | |||
256 | static inline __u32 pos2maj_hash(struct file *filp, loff_t pos) | ||
257 | { | ||
258 | if ((filp->f_mode & FMODE_32BITHASH) || | ||
259 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
260 | return (pos << 1) & 0xffffffff; | ||
261 | else | ||
262 | return ((pos >> 32) << 1) & 0xffffffff; | ||
263 | } | ||
264 | |||
265 | static inline __u32 pos2min_hash(struct file *filp, loff_t pos) | ||
266 | { | ||
267 | if ((filp->f_mode & FMODE_32BITHASH) || | ||
268 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
269 | return 0; | ||
270 | else | ||
271 | return pos & 0xffffffff; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * Return 32- or 64-bit end-of-file for dx directories | ||
276 | */ | ||
277 | static inline loff_t ext3_get_htree_eof(struct file *filp) | ||
278 | { | ||
279 | if ((filp->f_mode & FMODE_32BITHASH) || | ||
280 | (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api())) | ||
281 | return EXT3_HTREE_EOF_32BIT; | ||
282 | else | ||
283 | return EXT3_HTREE_EOF_64BIT; | ||
284 | } | ||
285 | |||
286 | |||
287 | /* | ||
288 | * ext3_dir_llseek() calls generic_file_llseek[_size]() to handle both | ||
289 | * non-htree and htree directories, where the "offset" is in terms | ||
290 | * of the filename hash value instead of the byte offset. | ||
291 | * | ||
292 | * Because we may return a 64-bit hash that is well beyond s_maxbytes, | ||
293 | * we need to pass the max hash as the maximum allowable offset in | ||
294 | * the htree directory case. | ||
295 | * | ||
296 | * NOTE: offsets obtained *before* ext3_set_inode_flag(dir, EXT3_INODE_INDEX) | ||
297 | * will be invalid once the directory was converted into a dx directory | ||
298 | */ | ||
299 | loff_t ext3_dir_llseek(struct file *file, loff_t offset, int origin) | ||
300 | { | ||
301 | struct inode *inode = file->f_mapping->host; | ||
302 | int dx_dir = is_dx_dir(inode); | ||
303 | |||
304 | if (likely(dx_dir)) | ||
305 | return generic_file_llseek_size(file, offset, origin, | ||
306 | ext3_get_htree_eof(file)); | ||
307 | else | ||
308 | return generic_file_llseek(file, offset, origin); | ||
309 | } | ||
251 | 310 | ||
252 | /* | 311 | /* |
253 | * This structure holds the nodes of the red-black tree used to store | 312 | * This structure holds the nodes of the red-black tree used to store |
@@ -308,15 +367,16 @@ static void free_rb_tree_fname(struct rb_root *root) | |||
308 | } | 367 | } |
309 | 368 | ||
310 | 369 | ||
311 | static struct dir_private_info *ext3_htree_create_dir_info(loff_t pos) | 370 | static struct dir_private_info *ext3_htree_create_dir_info(struct file *filp, |
371 | loff_t pos) | ||
312 | { | 372 | { |
313 | struct dir_private_info *p; | 373 | struct dir_private_info *p; |
314 | 374 | ||
315 | p = kzalloc(sizeof(struct dir_private_info), GFP_KERNEL); | 375 | p = kzalloc(sizeof(struct dir_private_info), GFP_KERNEL); |
316 | if (!p) | 376 | if (!p) |
317 | return NULL; | 377 | return NULL; |
318 | p->curr_hash = pos2maj_hash(pos); | 378 | p->curr_hash = pos2maj_hash(filp, pos); |
319 | p->curr_minor_hash = pos2min_hash(pos); | 379 | p->curr_minor_hash = pos2min_hash(filp, pos); |
320 | return p; | 380 | return p; |
321 | } | 381 | } |
322 | 382 | ||
@@ -406,7 +466,7 @@ static int call_filldir(struct file * filp, void * dirent, | |||
406 | printk("call_filldir: called with null fname?!?\n"); | 466 | printk("call_filldir: called with null fname?!?\n"); |
407 | return 0; | 467 | return 0; |
408 | } | 468 | } |
409 | curr_pos = hash2pos(fname->hash, fname->minor_hash); | 469 | curr_pos = hash2pos(filp, fname->hash, fname->minor_hash); |
410 | while (fname) { | 470 | while (fname) { |
411 | error = filldir(dirent, fname->name, | 471 | error = filldir(dirent, fname->name, |
412 | fname->name_len, curr_pos, | 472 | fname->name_len, curr_pos, |
@@ -431,13 +491,13 @@ static int ext3_dx_readdir(struct file * filp, | |||
431 | int ret; | 491 | int ret; |
432 | 492 | ||
433 | if (!info) { | 493 | if (!info) { |
434 | info = ext3_htree_create_dir_info(filp->f_pos); | 494 | info = ext3_htree_create_dir_info(filp, filp->f_pos); |
435 | if (!info) | 495 | if (!info) |
436 | return -ENOMEM; | 496 | return -ENOMEM; |
437 | filp->private_data = info; | 497 | filp->private_data = info; |
438 | } | 498 | } |
439 | 499 | ||
440 | if (filp->f_pos == EXT3_HTREE_EOF) | 500 | if (filp->f_pos == ext3_get_htree_eof(filp)) |
441 | return 0; /* EOF */ | 501 | return 0; /* EOF */ |
442 | 502 | ||
443 | /* Some one has messed with f_pos; reset the world */ | 503 | /* Some one has messed with f_pos; reset the world */ |
@@ -445,8 +505,8 @@ static int ext3_dx_readdir(struct file * filp, | |||
445 | free_rb_tree_fname(&info->root); | 505 | free_rb_tree_fname(&info->root); |
446 | info->curr_node = NULL; | 506 | info->curr_node = NULL; |
447 | info->extra_fname = NULL; | 507 | info->extra_fname = NULL; |
448 | info->curr_hash = pos2maj_hash(filp->f_pos); | 508 | info->curr_hash = pos2maj_hash(filp, filp->f_pos); |
449 | info->curr_minor_hash = pos2min_hash(filp->f_pos); | 509 | info->curr_minor_hash = pos2min_hash(filp, filp->f_pos); |
450 | } | 510 | } |
451 | 511 | ||
452 | /* | 512 | /* |
@@ -478,7 +538,7 @@ static int ext3_dx_readdir(struct file * filp, | |||
478 | if (ret < 0) | 538 | if (ret < 0) |
479 | return ret; | 539 | return ret; |
480 | if (ret == 0) { | 540 | if (ret == 0) { |
481 | filp->f_pos = EXT3_HTREE_EOF; | 541 | filp->f_pos = ext3_get_htree_eof(filp); |
482 | break; | 542 | break; |
483 | } | 543 | } |
484 | info->curr_node = rb_first(&info->root); | 544 | info->curr_node = rb_first(&info->root); |
@@ -498,7 +558,7 @@ static int ext3_dx_readdir(struct file * filp, | |||
498 | info->curr_minor_hash = fname->minor_hash; | 558 | info->curr_minor_hash = fname->minor_hash; |
499 | } else { | 559 | } else { |
500 | if (info->next_hash == ~0) { | 560 | if (info->next_hash == ~0) { |
501 | filp->f_pos = EXT3_HTREE_EOF; | 561 | filp->f_pos = ext3_get_htree_eof(filp); |
502 | break; | 562 | break; |
503 | } | 563 | } |
504 | info->curr_hash = info->next_hash; | 564 | info->curr_hash = info->next_hash; |
@@ -517,3 +577,15 @@ static int ext3_release_dir (struct inode * inode, struct file * filp) | |||
517 | 577 | ||
518 | return 0; | 578 | return 0; |
519 | } | 579 | } |
580 | |||
581 | const struct file_operations ext3_dir_operations = { | ||
582 | .llseek = ext3_dir_llseek, | ||
583 | .read = generic_read_dir, | ||
584 | .readdir = ext3_readdir, | ||
585 | .unlocked_ioctl = ext3_ioctl, | ||
586 | #ifdef CONFIG_COMPAT | ||
587 | .compat_ioctl = ext3_compat_ioctl, | ||
588 | #endif | ||
589 | .fsync = ext3_sync_file, | ||
590 | .release = ext3_release_dir, | ||
591 | }; | ||