diff options
author | Steven J. Magnani <steve@digidescorp.com> | 2012-10-04 20:14:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 14:05:09 -0400 |
commit | 7669e8fb09da47dd45c07a51394f01031ea81da8 (patch) | |
tree | 1112667a0606e27999a18b8dae574ae8ad01a151 /fs/fat | |
parent | 21b6633d516c4f5d03ec02ede6374e320191003f (diff) |
fat (exportfs): fix dentry reconnection
Maintain an index of directory inodes by starting cluster, so that
fat_get_parent() can return the proper cached inode rather than inventing
one that cannot be traced back to the filesystem root.
Add a new msdos/vfat binary mount option "nfs" so that FAT filesystems
that are _not_ exported via NFS are not saddled with maintenance of an
index they will never use.
Finally, simplify NFS file handle generation and lookups. An
ext2-congruent implementation is adequate for FAT needs.
Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/dir.c | 20 | ||||
-rw-r--r-- | fs/fat/fat.h | 27 | ||||
-rw-r--r-- | fs/fat/inode.c | 71 | ||||
-rw-r--r-- | fs/fat/namei_msdos.c | 5 | ||||
-rw-r--r-- | fs/fat/namei_vfat.c | 5 | ||||
-rw-r--r-- | fs/fat/nfs.c | 150 |
6 files changed, 141 insertions, 137 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index d70d8f31f704..55e088cc0613 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c | |||
@@ -872,21 +872,23 @@ static int fat_get_short_entry(struct inode *dir, loff_t *pos, | |||
872 | } | 872 | } |
873 | 873 | ||
874 | /* | 874 | /* |
875 | * The ".." entry can not provide the "struct fat_slot_info" informations | 875 | * The ".." entry can not provide the "struct fat_slot_info" information |
876 | * for inode. So, this function provide the some informations only. | 876 | * for inode, nor a usable i_pos. So, this function provides some information |
877 | * only. | ||
878 | * | ||
879 | * Since this function walks through the on-disk inodes within a directory, | ||
880 | * callers are responsible for taking any locks necessary to prevent the | ||
881 | * directory from changing. | ||
877 | */ | 882 | */ |
878 | int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, | 883 | int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, |
879 | struct msdos_dir_entry **de, loff_t *i_pos) | 884 | struct msdos_dir_entry **de) |
880 | { | 885 | { |
881 | loff_t offset; | 886 | loff_t offset = 0; |
882 | 887 | ||
883 | offset = 0; | 888 | *de = NULL; |
884 | *bh = NULL; | ||
885 | while (fat_get_short_entry(dir, &offset, bh, de) >= 0) { | 889 | while (fat_get_short_entry(dir, &offset, bh, de) >= 0) { |
886 | if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) { | 890 | if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) |
887 | *i_pos = fat_make_i_pos(dir->i_sb, *bh, *de); | ||
888 | return 0; | 891 | return 0; |
889 | } | ||
890 | } | 892 | } |
891 | return -ENOENT; | 893 | return -ENOENT; |
892 | } | 894 | } |
diff --git a/fs/fat/fat.h b/fs/fat/fat.h index fb95939ff870..ec54c3a7f2f7 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/string.h> | 5 | #include <linux/string.h> |
6 | #include <linux/nls.h> | 6 | #include <linux/nls.h> |
7 | #include <linux/fs.h> | 7 | #include <linux/fs.h> |
8 | #include <linux/hash.h> | ||
8 | #include <linux/mutex.h> | 9 | #include <linux/mutex.h> |
9 | #include <linux/ratelimit.h> | 10 | #include <linux/ratelimit.h> |
10 | #include <linux/msdos_fs.h> | 11 | #include <linux/msdos_fs.h> |
@@ -46,7 +47,8 @@ struct fat_mount_options { | |||
46 | usefree:1, /* Use free_clusters for FAT32 */ | 47 | usefree:1, /* Use free_clusters for FAT32 */ |
47 | tz_utc:1, /* Filesystem timestamps are in UTC */ | 48 | tz_utc:1, /* Filesystem timestamps are in UTC */ |
48 | rodir:1, /* allow ATTR_RO for directory */ | 49 | rodir:1, /* allow ATTR_RO for directory */ |
49 | discard:1; /* Issue discard requests on deletions */ | 50 | discard:1, /* Issue discard requests on deletions */ |
51 | nfs:1; /* Do extra work needed for NFS export */ | ||
50 | }; | 52 | }; |
51 | 53 | ||
52 | #define FAT_HASH_BITS 8 | 54 | #define FAT_HASH_BITS 8 |
@@ -88,6 +90,9 @@ struct msdos_sb_info { | |||
88 | 90 | ||
89 | spinlock_t inode_hash_lock; | 91 | spinlock_t inode_hash_lock; |
90 | struct hlist_head inode_hashtable[FAT_HASH_SIZE]; | 92 | struct hlist_head inode_hashtable[FAT_HASH_SIZE]; |
93 | |||
94 | spinlock_t dir_hash_lock; | ||
95 | struct hlist_head dir_hashtable[FAT_HASH_SIZE]; | ||
91 | }; | 96 | }; |
92 | 97 | ||
93 | #define FAT_CACHE_VALID 0 /* special case for valid cache */ | 98 | #define FAT_CACHE_VALID 0 /* special case for valid cache */ |
@@ -110,6 +115,7 @@ struct msdos_inode_info { | |||
110 | int i_attrs; /* unused attribute bits */ | 115 | int i_attrs; /* unused attribute bits */ |
111 | loff_t i_pos; /* on-disk position of directory entry or 0 */ | 116 | loff_t i_pos; /* on-disk position of directory entry or 0 */ |
112 | struct hlist_node i_fat_hash; /* hash by i_location */ | 117 | struct hlist_node i_fat_hash; /* hash by i_location */ |
118 | struct hlist_node i_dir_hash; /* hash by i_logstart */ | ||
113 | struct rw_semaphore truncate_lock; /* protect bmap against truncate */ | 119 | struct rw_semaphore truncate_lock; /* protect bmap against truncate */ |
114 | struct inode vfs_inode; | 120 | struct inode vfs_inode; |
115 | }; | 121 | }; |
@@ -262,7 +268,7 @@ extern int fat_subdirs(struct inode *dir); | |||
262 | extern int fat_scan(struct inode *dir, const unsigned char *name, | 268 | extern int fat_scan(struct inode *dir, const unsigned char *name, |
263 | struct fat_slot_info *sinfo); | 269 | struct fat_slot_info *sinfo); |
264 | extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, | 270 | extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, |
265 | struct msdos_dir_entry **de, loff_t *i_pos); | 271 | struct msdos_dir_entry **de); |
266 | extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts); | 272 | extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts); |
267 | extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots, | 273 | extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots, |
268 | struct fat_slot_info *sinfo); | 274 | struct fat_slot_info *sinfo); |
@@ -341,18 +347,9 @@ extern int fat_fill_super(struct super_block *sb, void *data, int silent, | |||
341 | 347 | ||
342 | extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, | 348 | extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, |
343 | struct inode *i2); | 349 | struct inode *i2); |
344 | static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi, | 350 | static inline unsigned long fat_dir_hash(int logstart) |
345 | struct inode *inode) | ||
346 | { | 351 | { |
347 | loff_t i_pos; | 352 | return hash_32(logstart, FAT_HASH_BITS); |
348 | #if BITS_PER_LONG == 32 | ||
349 | spin_lock(&sbi->inode_hash_lock); | ||
350 | #endif | ||
351 | i_pos = MSDOS_I(inode)->i_pos; | ||
352 | #if BITS_PER_LONG == 32 | ||
353 | spin_unlock(&sbi->inode_hash_lock); | ||
354 | #endif | ||
355 | return i_pos; | ||
356 | } | 353 | } |
357 | 354 | ||
358 | /* fat/misc.c */ | 355 | /* fat/misc.c */ |
@@ -382,10 +379,10 @@ void fat_cache_destroy(void); | |||
382 | 379 | ||
383 | /* fat/nfs.c */ | 380 | /* fat/nfs.c */ |
384 | struct fid; | 381 | struct fid; |
385 | extern int fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, | ||
386 | struct inode *parent); | ||
387 | extern struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, | 382 | extern struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, |
388 | int fh_len, int fh_type); | 383 | int fh_len, int fh_type); |
384 | extern struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid, | ||
385 | int fh_len, int fh_type); | ||
389 | extern struct dentry *fat_get_parent(struct dentry *child_dir); | 386 | extern struct dentry *fat_get_parent(struct dentry *child_dir); |
390 | 387 | ||
391 | /* helper for printk */ | 388 | /* helper for printk */ |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 169f6ebddf96..5741b11650f1 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c | |||
@@ -281,15 +281,42 @@ static inline unsigned long fat_hash(loff_t i_pos) | |||
281 | return hash_32(i_pos, FAT_HASH_BITS); | 281 | return hash_32(i_pos, FAT_HASH_BITS); |
282 | } | 282 | } |
283 | 283 | ||
284 | static void dir_hash_init(struct super_block *sb) | ||
285 | { | ||
286 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | ||
287 | int i; | ||
288 | |||
289 | spin_lock_init(&sbi->dir_hash_lock); | ||
290 | for (i = 0; i < FAT_HASH_SIZE; i++) | ||
291 | INIT_HLIST_HEAD(&sbi->dir_hashtable[i]); | ||
292 | } | ||
293 | |||
284 | void fat_attach(struct inode *inode, loff_t i_pos) | 294 | void fat_attach(struct inode *inode, loff_t i_pos) |
285 | { | 295 | { |
286 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | 296 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); |
287 | struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos); | ||
288 | 297 | ||
289 | spin_lock(&sbi->inode_hash_lock); | 298 | if (inode->i_ino != MSDOS_ROOT_INO) { |
290 | MSDOS_I(inode)->i_pos = i_pos; | 299 | struct hlist_head *head = sbi->inode_hashtable |
291 | hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head); | 300 | + fat_hash(i_pos); |
292 | spin_unlock(&sbi->inode_hash_lock); | 301 | |
302 | spin_lock(&sbi->inode_hash_lock); | ||
303 | MSDOS_I(inode)->i_pos = i_pos; | ||
304 | hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head); | ||
305 | spin_unlock(&sbi->inode_hash_lock); | ||
306 | } | ||
307 | |||
308 | /* If NFS support is enabled, cache the mapping of start cluster | ||
309 | * to directory inode. This is used during reconnection of | ||
310 | * dentries to the filesystem root. | ||
311 | */ | ||
312 | if (S_ISDIR(inode->i_mode) && sbi->options.nfs) { | ||
313 | struct hlist_head *d_head = sbi->dir_hashtable; | ||
314 | d_head += fat_dir_hash(MSDOS_I(inode)->i_logstart); | ||
315 | |||
316 | spin_lock(&sbi->dir_hash_lock); | ||
317 | hlist_add_head(&MSDOS_I(inode)->i_dir_hash, d_head); | ||
318 | spin_unlock(&sbi->dir_hash_lock); | ||
319 | } | ||
293 | } | 320 | } |
294 | EXPORT_SYMBOL_GPL(fat_attach); | 321 | EXPORT_SYMBOL_GPL(fat_attach); |
295 | 322 | ||
@@ -300,6 +327,12 @@ void fat_detach(struct inode *inode) | |||
300 | MSDOS_I(inode)->i_pos = 0; | 327 | MSDOS_I(inode)->i_pos = 0; |
301 | hlist_del_init(&MSDOS_I(inode)->i_fat_hash); | 328 | hlist_del_init(&MSDOS_I(inode)->i_fat_hash); |
302 | spin_unlock(&sbi->inode_hash_lock); | 329 | spin_unlock(&sbi->inode_hash_lock); |
330 | |||
331 | if (S_ISDIR(inode->i_mode) && sbi->options.nfs) { | ||
332 | spin_lock(&sbi->dir_hash_lock); | ||
333 | hlist_del_init(&MSDOS_I(inode)->i_dir_hash); | ||
334 | spin_unlock(&sbi->dir_hash_lock); | ||
335 | } | ||
303 | } | 336 | } |
304 | EXPORT_SYMBOL_GPL(fat_detach); | 337 | EXPORT_SYMBOL_GPL(fat_detach); |
305 | 338 | ||
@@ -504,6 +537,7 @@ static void init_once(void *foo) | |||
504 | ei->cache_valid_id = FAT_CACHE_VALID + 1; | 537 | ei->cache_valid_id = FAT_CACHE_VALID + 1; |
505 | INIT_LIST_HEAD(&ei->cache_lru); | 538 | INIT_LIST_HEAD(&ei->cache_lru); |
506 | INIT_HLIST_NODE(&ei->i_fat_hash); | 539 | INIT_HLIST_NODE(&ei->i_fat_hash); |
540 | INIT_HLIST_NODE(&ei->i_dir_hash); | ||
507 | inode_init_once(&ei->vfs_inode); | 541 | inode_init_once(&ei->vfs_inode); |
508 | } | 542 | } |
509 | 543 | ||
@@ -562,6 +596,20 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf) | |||
562 | return 0; | 596 | return 0; |
563 | } | 597 | } |
564 | 598 | ||
599 | static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi, | ||
600 | struct inode *inode) | ||
601 | { | ||
602 | loff_t i_pos; | ||
603 | #if BITS_PER_LONG == 32 | ||
604 | spin_lock(&sbi->inode_hash_lock); | ||
605 | #endif | ||
606 | i_pos = MSDOS_I(inode)->i_pos; | ||
607 | #if BITS_PER_LONG == 32 | ||
608 | spin_unlock(&sbi->inode_hash_lock); | ||
609 | #endif | ||
610 | return i_pos; | ||
611 | } | ||
612 | |||
565 | static int __fat_write_inode(struct inode *inode, int wait) | 613 | static int __fat_write_inode(struct inode *inode, int wait) |
566 | { | 614 | { |
567 | struct super_block *sb = inode->i_sb; | 615 | struct super_block *sb = inode->i_sb; |
@@ -655,8 +703,8 @@ static const struct super_operations fat_sops = { | |||
655 | }; | 703 | }; |
656 | 704 | ||
657 | static const struct export_operations fat_export_ops = { | 705 | static const struct export_operations fat_export_ops = { |
658 | .encode_fh = fat_encode_fh, | ||
659 | .fh_to_dentry = fat_fh_to_dentry, | 706 | .fh_to_dentry = fat_fh_to_dentry, |
707 | .fh_to_parent = fat_fh_to_parent, | ||
660 | .get_parent = fat_get_parent, | 708 | .get_parent = fat_get_parent, |
661 | }; | 709 | }; |
662 | 710 | ||
@@ -706,6 +754,8 @@ static int fat_show_options(struct seq_file *m, struct dentry *root) | |||
706 | seq_puts(m, ",usefree"); | 754 | seq_puts(m, ",usefree"); |
707 | if (opts->quiet) | 755 | if (opts->quiet) |
708 | seq_puts(m, ",quiet"); | 756 | seq_puts(m, ",quiet"); |
757 | if (opts->nfs) | ||
758 | seq_puts(m, ",nfs"); | ||
709 | if (opts->showexec) | 759 | if (opts->showexec) |
710 | seq_puts(m, ",showexec"); | 760 | seq_puts(m, ",showexec"); |
711 | if (opts->sys_immutable) | 761 | if (opts->sys_immutable) |
@@ -750,7 +800,7 @@ enum { | |||
750 | Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, | 800 | Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, |
751 | Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, | 801 | Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, |
752 | Opt_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont, | 802 | Opt_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont, |
753 | Opt_err_panic, Opt_err_ro, Opt_discard, Opt_err, | 803 | Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_err, |
754 | }; | 804 | }; |
755 | 805 | ||
756 | static const match_table_t fat_tokens = { | 806 | static const match_table_t fat_tokens = { |
@@ -779,6 +829,7 @@ static const match_table_t fat_tokens = { | |||
779 | {Opt_err_panic, "errors=panic"}, | 829 | {Opt_err_panic, "errors=panic"}, |
780 | {Opt_err_ro, "errors=remount-ro"}, | 830 | {Opt_err_ro, "errors=remount-ro"}, |
781 | {Opt_discard, "discard"}, | 831 | {Opt_discard, "discard"}, |
832 | {Opt_nfs, "nfs"}, | ||
782 | {Opt_obsolete, "conv=binary"}, | 833 | {Opt_obsolete, "conv=binary"}, |
783 | {Opt_obsolete, "conv=text"}, | 834 | {Opt_obsolete, "conv=text"}, |
784 | {Opt_obsolete, "conv=auto"}, | 835 | {Opt_obsolete, "conv=auto"}, |
@@ -859,6 +910,7 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, | |||
859 | opts->numtail = 1; | 910 | opts->numtail = 1; |
860 | opts->usefree = opts->nocase = 0; | 911 | opts->usefree = opts->nocase = 0; |
861 | opts->tz_utc = 0; | 912 | opts->tz_utc = 0; |
913 | opts->nfs = 0; | ||
862 | opts->errors = FAT_ERRORS_RO; | 914 | opts->errors = FAT_ERRORS_RO; |
863 | *debug = 0; | 915 | *debug = 0; |
864 | 916 | ||
@@ -1023,6 +1075,9 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, | |||
1023 | case Opt_discard: | 1075 | case Opt_discard: |
1024 | opts->discard = 1; | 1076 | opts->discard = 1; |
1025 | break; | 1077 | break; |
1078 | case Opt_nfs: | ||
1079 | opts->nfs = 1; | ||
1080 | break; | ||
1026 | 1081 | ||
1027 | /* obsolete mount options */ | 1082 | /* obsolete mount options */ |
1028 | case Opt_obsolete: | 1083 | case Opt_obsolete: |
@@ -1313,6 +1368,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, | |||
1313 | 1368 | ||
1314 | /* set up enough so that it can read an inode */ | 1369 | /* set up enough so that it can read an inode */ |
1315 | fat_hash_init(sb); | 1370 | fat_hash_init(sb); |
1371 | dir_hash_init(sb); | ||
1316 | fat_ent_access_init(sb); | 1372 | fat_ent_access_init(sb); |
1317 | 1373 | ||
1318 | /* | 1374 | /* |
@@ -1367,6 +1423,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, | |||
1367 | } | 1423 | } |
1368 | error = -ENOMEM; | 1424 | error = -ENOMEM; |
1369 | insert_inode_hash(root_inode); | 1425 | insert_inode_hash(root_inode); |
1426 | fat_attach(root_inode, 0); | ||
1370 | sb->s_root = d_make_root(root_inode); | 1427 | sb->s_root = d_make_root(root_inode); |
1371 | if (!sb->s_root) { | 1428 | if (!sb->s_root) { |
1372 | fat_msg(sb, KERN_ERR, "get root inode failed"); | 1429 | fat_msg(sb, KERN_ERR, "get root inode failed"); |
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index b0e12bf9f4a1..c27c63020292 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c | |||
@@ -440,7 +440,7 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, | |||
440 | struct inode *old_inode, *new_inode; | 440 | struct inode *old_inode, *new_inode; |
441 | struct fat_slot_info old_sinfo, sinfo; | 441 | struct fat_slot_info old_sinfo, sinfo; |
442 | struct timespec ts; | 442 | struct timespec ts; |
443 | loff_t dotdot_i_pos, new_i_pos; | 443 | loff_t new_i_pos; |
444 | int err, old_attrs, is_dir, update_dotdot, corrupt = 0; | 444 | int err, old_attrs, is_dir, update_dotdot, corrupt = 0; |
445 | 445 | ||
446 | old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; | 446 | old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; |
@@ -456,8 +456,7 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, | |||
456 | is_dir = S_ISDIR(old_inode->i_mode); | 456 | is_dir = S_ISDIR(old_inode->i_mode); |
457 | update_dotdot = (is_dir && old_dir != new_dir); | 457 | update_dotdot = (is_dir && old_dir != new_dir); |
458 | if (update_dotdot) { | 458 | if (update_dotdot) { |
459 | if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de, | 459 | if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) { |
460 | &dotdot_i_pos) < 0) { | ||
461 | err = -EIO; | 460 | err = -EIO; |
462 | goto out; | 461 | goto out; |
463 | } | 462 | } |
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 6a6d8c0715a1..e535dd75b986 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c | |||
@@ -914,7 +914,7 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
914 | struct inode *old_inode, *new_inode; | 914 | struct inode *old_inode, *new_inode; |
915 | struct fat_slot_info old_sinfo, sinfo; | 915 | struct fat_slot_info old_sinfo, sinfo; |
916 | struct timespec ts; | 916 | struct timespec ts; |
917 | loff_t dotdot_i_pos, new_i_pos; | 917 | loff_t new_i_pos; |
918 | int err, is_dir, update_dotdot, corrupt = 0; | 918 | int err, is_dir, update_dotdot, corrupt = 0; |
919 | struct super_block *sb = old_dir->i_sb; | 919 | struct super_block *sb = old_dir->i_sb; |
920 | 920 | ||
@@ -929,8 +929,7 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
929 | is_dir = S_ISDIR(old_inode->i_mode); | 929 | is_dir = S_ISDIR(old_inode->i_mode); |
930 | update_dotdot = (is_dir && old_dir != new_dir); | 930 | update_dotdot = (is_dir && old_dir != new_dir); |
931 | if (update_dotdot) { | 931 | if (update_dotdot) { |
932 | if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de, | 932 | if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) { |
933 | &dotdot_i_pos) < 0) { | ||
934 | err = -EIO; | 933 | err = -EIO; |
935 | goto out; | 934 | goto out; |
936 | } | 935 | } |
diff --git a/fs/fat/nfs.c b/fs/fat/nfs.c index 21609a1e9355..ef4b5faba87b 100644 --- a/fs/fat/nfs.c +++ b/fs/fat/nfs.c | |||
@@ -14,47 +14,46 @@ | |||
14 | #include <linux/exportfs.h> | 14 | #include <linux/exportfs.h> |
15 | #include "fat.h" | 15 | #include "fat.h" |
16 | 16 | ||
17 | /* | 17 | /** |
18 | * a FAT file handle with fhtype 3 is | 18 | * Look up a directory inode given its starting cluster. |
19 | * 0/ i_ino - for fast, reliable lookup if still in the cache | ||
20 | * 1/ i_generation - to see if i_ino is still valid | ||
21 | * bit 0 == 0 iff directory | ||
22 | * 2/ i_pos(8-39) - if ino has changed, but still in cache | ||
23 | * 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos | ||
24 | * 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc | ||
25 | * | ||
26 | * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum | ||
27 | * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits | ||
28 | * of i_logstart is used to store the directory entry offset. | ||
29 | */ | 19 | */ |
30 | 20 | static struct inode *fat_dget(struct super_block *sb, int i_logstart) | |
31 | int | ||
32 | fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent) | ||
33 | { | 21 | { |
34 | int len = *lenp; | 22 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
35 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | 23 | struct hlist_head *head; |
36 | loff_t i_pos; | 24 | struct hlist_node *_p; |
25 | struct msdos_inode_info *i; | ||
26 | struct inode *inode = NULL; | ||
37 | 27 | ||
38 | if (len < 5) { | 28 | head = sbi->dir_hashtable + fat_dir_hash(i_logstart); |
39 | *lenp = 5; | 29 | spin_lock(&sbi->dir_hash_lock); |
40 | return 255; /* no room */ | 30 | hlist_for_each_entry(i, _p, head, i_dir_hash) { |
31 | BUG_ON(i->vfs_inode.i_sb != sb); | ||
32 | if (i->i_logstart != i_logstart) | ||
33 | continue; | ||
34 | inode = igrab(&i->vfs_inode); | ||
35 | if (inode) | ||
36 | break; | ||
41 | } | 37 | } |
42 | 38 | spin_unlock(&sbi->dir_hash_lock); | |
43 | i_pos = fat_i_pos_read(sbi, inode); | 39 | return inode; |
44 | *lenp = 5; | ||
45 | fh[0] = inode->i_ino; | ||
46 | fh[1] = inode->i_generation; | ||
47 | fh[2] = i_pos >> 8; | ||
48 | fh[3] = ((i_pos & 0xf0) << 24) | MSDOS_I(inode)->i_logstart; | ||
49 | fh[4] = (i_pos & 0x0f) << 28; | ||
50 | if (parent) | ||
51 | fh[4] |= MSDOS_I(parent)->i_logstart; | ||
52 | return 3; | ||
53 | } | 40 | } |
54 | 41 | ||
55 | static int fat_is_valid_fh(int fh_len, int fh_type) | 42 | static struct inode *fat_nfs_get_inode(struct super_block *sb, |
43 | u64 ino, u32 generation) | ||
56 | { | 44 | { |
57 | return ((fh_len >= 5) && (fh_type == 3)); | 45 | struct inode *inode; |
46 | |||
47 | if ((ino < MSDOS_ROOT_INO) || (ino == MSDOS_FSINFO_INO)) | ||
48 | return NULL; | ||
49 | |||
50 | inode = ilookup(sb, ino); | ||
51 | if (inode && generation && (inode->i_generation != generation)) { | ||
52 | iput(inode); | ||
53 | inode = NULL; | ||
54 | } | ||
55 | |||
56 | return inode; | ||
58 | } | 57 | } |
59 | 58 | ||
60 | /** | 59 | /** |
@@ -64,57 +63,19 @@ static int fat_is_valid_fh(int fh_len, int fh_type) | |||
64 | struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, | 63 | struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, |
65 | int fh_len, int fh_type) | 64 | int fh_len, int fh_type) |
66 | { | 65 | { |
67 | struct inode *inode = NULL; | 66 | return generic_fh_to_dentry(sb, fid, fh_len, fh_type, |
68 | u32 *fh = fid->raw; | 67 | fat_nfs_get_inode); |
69 | loff_t i_pos; | 68 | } |
70 | unsigned long i_ino; | ||
71 | __u32 i_generation; | ||
72 | int i_logstart; | ||
73 | |||
74 | if (!fat_is_valid_fh(fh_len, fh_type)) | ||
75 | return NULL; | ||
76 | |||
77 | i_ino = fh[0]; | ||
78 | i_generation = fh[1]; | ||
79 | i_logstart = fh[3] & 0x0fffffff; | ||
80 | |||
81 | /* Try i_ino lookup first - fastest and most reliable */ | ||
82 | inode = ilookup(sb, i_ino); | ||
83 | if (inode && (inode->i_generation != i_generation)) { | ||
84 | iput(inode); | ||
85 | inode = NULL; | ||
86 | } | ||
87 | if (!inode) { | ||
88 | i_pos = (loff_t)fh[2] << 8; | ||
89 | i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28); | ||
90 | |||
91 | /* try 2 - see if i_pos is in F-d-c | ||
92 | * require i_logstart to be the same | ||
93 | * Will fail if you truncate and then re-write | ||
94 | */ | ||
95 | |||
96 | inode = fat_iget(sb, i_pos); | ||
97 | if (inode && MSDOS_I(inode)->i_logstart != i_logstart) { | ||
98 | iput(inode); | ||
99 | inode = NULL; | ||
100 | } | ||
101 | } | ||
102 | 69 | ||
103 | /* | 70 | /* |
104 | * For now, do nothing if the inode is not found. | 71 | * Find the parent for a file specified by NFS handle. |
105 | * | 72 | * This requires that the handle contain the i_ino of the parent. |
106 | * What we could do is: | 73 | */ |
107 | * | 74 | struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid, |
108 | * - follow the file starting at fh[4], and record the ".." entry, | 75 | int fh_len, int fh_type) |
109 | * and the name of the fh[2] entry. | 76 | { |
110 | * - then follow the ".." file finding the next step up. | 77 | return generic_fh_to_parent(sb, fid, fh_len, fh_type, |
111 | * | 78 | fat_nfs_get_inode); |
112 | * This way we build a path to the root of the tree. If this works, we | ||
113 | * lookup the path and so get this inode into the cache. Finally try | ||
114 | * the fat_iget lookup again. If that fails, then we are totally out | ||
115 | * of luck. But all that is for another day | ||
116 | */ | ||
117 | return d_obtain_alias(inode); | ||
118 | } | 79 | } |
119 | 80 | ||
120 | /* | 81 | /* |
@@ -128,24 +89,13 @@ struct dentry *fat_get_parent(struct dentry *child_dir) | |||
128 | struct super_block *sb = child_dir->d_sb; | 89 | struct super_block *sb = child_dir->d_sb; |
129 | struct buffer_head *bh = NULL; | 90 | struct buffer_head *bh = NULL; |
130 | struct msdos_dir_entry *de; | 91 | struct msdos_dir_entry *de; |
131 | loff_t i_pos; | 92 | struct inode *parent_inode = NULL; |
132 | struct dentry *parent; | ||
133 | struct inode *inode; | ||
134 | int err; | ||
135 | 93 | ||
136 | lock_super(sb); | 94 | if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) { |
137 | 95 | int parent_logstart = fat_get_start(MSDOS_SB(sb), de); | |
138 | err = fat_get_dotdot_entry(child_dir->d_inode, &bh, &de, &i_pos); | 96 | parent_inode = fat_dget(sb, parent_logstart); |
139 | if (err) { | ||
140 | parent = ERR_PTR(err); | ||
141 | goto out; | ||
142 | } | 97 | } |
143 | inode = fat_build_inode(sb, de, i_pos); | ||
144 | |||
145 | parent = d_obtain_alias(inode); | ||
146 | out: | ||
147 | brelse(bh); | 98 | brelse(bh); |
148 | unlock_super(sb); | ||
149 | 99 | ||
150 | return parent; | 100 | return d_obtain_alias(parent_inode); |
151 | } | 101 | } |