patch for allowing msdos/vfat nfs exports
Linus was nice enough to change the kernel this morning for me to allow msdos/vfat NFS exports. Since this is totally new code and there is no guarantee it won't destroy everything on your fat filesystem he asked me to post the diff to the kernel mailing list so more people could look at it/test it out before it goes back into the standard kernel.
The patch below is relative to Linus' current working tree (which should be the same 2.4.7 in theory).
I've been using it for half a day now and so far it hasn't done anything bad, but please be careful if you decide to test it and backup your data and after testing, be sure to compare your data to your backup.
Note: this patch will NOT work for older kernels. dentry_to_fh and fh_to_dentry are provided (just as in reiserfs).
Please send me email directly with your experiences using this patch since I offered to collect them for Linus.
-- Nathan Laredo
[email protected]
fs/fat/inode.c +48 -0
@@ -403,12 +403,60 @@ inode->i_nlink =fat_subdirs (inode)+2 ; } +static int fat_dentry_to_fh (struct dentry *dentry,__u32 *data,int *lenp,int need_parent) +{ +struct inode *inode = dentry->d_inode; +unsigned int i_pos =MSDOS_I (inode)->i_location; + + *data = i_pos; + *lenp =1 ; +return 1 ; +} + +static struct dentry *fat_fh_to_dentry (struct super_block *sb,__u32 *data,int len,int fhtype,int parent) +{ +struct list_head *lp; +struct dentry *result; +unsigned int i_pos = data[0 ]; +struct inode *inode; + + inode =fat_iget (sb, i_pos); + +if (!inode) +return ERR_PTR (-ESTALE ); + +/* now to find a dentry. +* If possible, get a well-connected one +*/ +spin_lock (&dcache_lock); +for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { + result = list_entry(lp,struct dentry, d_alias); +if (! (result->d_flags &DCACHE_NFSD_DISCONNECTED )) { +dget_locked (result); + result->d_vfs_flags |=DCACHE_REFERENCED ; +spin_unlock (&dcache_lock); +iput (inode); +return result; + } + } +spin_unlock (&dcache_lock); + result =d_alloc_root (inode); +if (result ==NULL ) { +iput (inode); +return ERR_PTR (-ENOMEM ); + } + result->d_flags |=DCACHE_NFSD_DISCONNECTED ; +return result; +} +static struct super_operations fat_sops = { write_inode: fat_write_inode, delete_inode: fat_delete_inode, put_super: fat_put_super, statfs: fat_statfs, clear_inode: fat_clear_inode, + fh_to_dentry: fat_fh_to_dentry, + dentry_to_fh: fat_dentry_to_fh };/*
fs/fat/inode.c line 449
Quoted 7 lines · Nathan Laredo
fs/fat/inode.c +7
-0+ result =d_alloc_root (inode); +if (result ==NULL ) { +iput (inode); +return ERR_PTR (-ENOMEM ); + } + result->d_flags |=DCACHE_NFSD_DISCONNECTED ; +return result;
Erm... AFAICS it got a race - think of doing that for directory when dentry is already gone, but inode still in cache. You will get nfsd_findparent() called and that will give funny results on FAT.
The worst thing being, it _will_ get a decently-looking inode. Inode that will point to the same blocks as parent directory, but will be completely independent (different location).
Notice that we will end up reading directories that might be changing under us - struct inode is different, so exclusion simply doesn't work.
IOW, we need
if (S_ISDIR(inode->i_mode)) return ERR_PTR(-ESTALE);
immediately before the chunk above.
Quoted 14 lines · Alexander Viro
fs/fat/inode.c line 449
Quoted 7 lines · Nathan Laredo
fs/fat/inode.c +7
-0+ result =d_alloc_root (inode); +if (result ==NULL ) { +iput (inode); +return ERR_PTR (-ENOMEM ); + } + result->d_flags |=DCACHE_NFSD_DISCONNECTED ; +return result;Erm... AFAICS it got a race - think of doing that for directory when dentry is already gone, but inode still in cache. You will get
nfsd_findparent()called and that will give funny results on FAT.
Note that the code was definitely cribbed from reiserfs, and when I stole it I had this very strong feeling that "This really should be supported in the VFS layer instead of hidden in the low-level filesystems".
We are, after all, working with very internal knowledge of not only the way the dentry lists are attached to the inode, but also about the whole DISCONNECTED thing etc.
Quoted 3 lines
The worst thing being, it _will_ get a decently-looking inode. Inode that will point to the same blocks as parent directory, but will be completely independent (different location).
Absolutely. And some of the NFS-export problems won't even be avoidable at
all: FAT does not have a notion of inode versions etc, and we do not have a way to fix that.
However, what we could do is add more sanity-testing, and for example also add the parent inode location to the dentry_to_fh code. We have space.
The patch was a fairly quick hack. What it does show is that (a) the dentry_to_fh approach is actually a very usable one in itself and wasn't just an ugly reiserfs hack, and (b) FAT _can_ be exported.
The reason I asked Nathan to post the damn thing instead of just applying it to the tree was to get these kinds of comments, and hopefully move the VFS stuff to a VFS location, ie start exporting a vfs_inode_to_dentry() function that reiserfs, FAT and quite possibly others can sanely share.
Al, Neil, care to work something out between you? I saw that Neil had something already..
Linus