EXPORTFS: Don't return NULL from fh_to_dentry()/fh_to_parent() [ver #4]
Subsystems (11)
PATCH 1/1 EXPORTFS: Don't return NULL from fh_to_dentry()/fh_to_parent() [ver #4]
Returning NULL from fh_to_dentry() and fh_to_parent() is not permitted, so return -ESTALE instead. exportfs_decode_fh() does not check for NULL, but will try to dereference it as IS_ERR() does not check for it.
The generic_fh_to_dentry() and generic_fh_to_parent() functions also no longer return NULL, but return -ESTALE instead.
---
fs/fat/inode.c | 2 +- fs/fuse/inode.c | 4 ++-- fs/gfs2/ops_export.c | 4 ++-- fs/isofs/export.c | 4 ++-- fs/libfs.c | 4 ++-- fs/ocfs2/export.c | 4 ++-- fs/udf/namei.c | 4 ++-- fs/xfs/linux-2.6/xfs_export.c | 2 +- mm/shmem.c | 14 +++++++------- 9 files changed, 21 insertions(+), 21 deletions(-)
fs/fat/inode.c +1 -1
@@ -657,7 +657,7 @@ static struct dentry *fat_fh_to_dentry(struct super_block *sb,u32 *fh = fid->raw;if (fh_len <5 || fh_type !=3 ) -return NULL ; +return ERR_PTR (-ESTALE ); inode =ilookup (sb, fh[0 ]);if (!inode || inode->i_generation != fh[1 ]) {
fs/fuse/inode.c +2 -2
@@ -653,7 +653,7 @@ static struct dentry *fuse_fh_to_dentry(struct super_block *sb,struct fuse_inode_handle handle;if ((fh_type !=0x81 && fh_type !=0x82 ) || fh_len <3 ) -return NULL ; +return ERR_PTR (-ESTALE ); handle.nodeid = (u64 ) fid->raw[0 ] <<32 ; handle.nodeid |= (u64 ) fid->raw[1 ];
@@ -667,7 +667,7 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb,struct fuse_inode_handle parent;if (fh_type !=0x82 || fh_len <6 ) -return NULL ; +return ERR_PTR (-ESTALE ); parent.nodeid = (u64 ) fid->raw[3 ] <<32 ; parent.nodeid |= (u64 ) fid->raw[4 ];
fs/gfs2/ops_export.c +2 -2
@@ -254,7 +254,7 @@ static struct dentry *gfs2_fh_to_dentry(struct super_block *sb, struct fid *fid, this.no_addr |=be32_to_cpu (fh[3 ]);return gfs2_get_dentry (sb, &this);default : -return NULL ; +return ERR_PTR (-ESTALE ); } }
@@ -273,7 +273,7 @@ static struct dentry *gfs2_fh_to_parent(struct super_block *sb, struct fid *fid, parent.no_addr |=be32_to_cpu (fh[7 ]);return gfs2_get_dentry (sb, &parent);default : -return NULL ; +return ERR_PTR (-ESTALE ); } }
fs/isofs/export.c +2 -2
@@ -164,7 +164,7 @@ static struct dentry *isofs_fh_to_dentry(struct super_block *sb,struct isofs_fid *ifid = (struct isofs_fid *)fid;if (fh_len <3 || fh_type >2 ) -return NULL ; +return ERR_PTR (-ESTALE );return isofs_export_iget (sb, ifid->block, ifid->offset, ifid->generation);
@@ -176,7 +176,7 @@ static struct dentry *isofs_fh_to_parent(struct super_block *sb,struct isofs_fid *ifid = (struct isofs_fid *)fid;if (fh_type !=2 ) -return NULL ; +return ERR_PTR (-ESTALE ); return isofs_export_iget(sb, fh_len > 2 ? ifid->parent_block : 0,
fs/libfs.c +2 -2
@@ -751,7 +751,7 @@ struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid,struct inode *inode =NULL ;if (fh_len <2 ) -return NULL ; +return ERR_PTR (-ESTALE );switch (fh_type) {case FILEID_INO32_GEN :
@@ -784,7 +784,7 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid,struct inode *inode =NULL ;if (fh_len <=2 ) -return NULL ; +return ERR_PTR (-ESTALE );switch (fh_type) {case FILEID_INO32_GEN_PARENT :
fs/ocfs2/export.c +2 -2
@@ -182,7 +182,7 @@ static struct dentry *ocfs2_fh_to_dentry(struct super_block *sb,struct ocfs2_inode_handle handle;if (fh_len <3 || fh_type >2 ) -return NULL ; +return ERR_PTR (-ESTALE ); handle.ih_blkno = (u64 )le32_to_cpu (fid->raw[0 ]) <<32 ; handle.ih_blkno |= (u64 )le32_to_cpu (fid->raw[1 ]);
@@ -196,7 +196,7 @@ static struct dentry *ocfs2_fh_to_parent(struct super_block *sb,struct ocfs2_inode_handle parent;if (fh_type !=2 || fh_len <6 ) -return NULL ; +return ERR_PTR (-ESTALE ); parent.ih_blkno = (u64 )le32_to_cpu (fid->raw[3 ]) <<32 ; parent.ih_blkno |= (u64 )le32_to_cpu (fid->raw[4 ]);
fs/udf/namei.c +2 -2
@@ -1297,7 +1297,7 @@ static struct dentry *udf_fh_to_dentry(struct super_block *sb,if ((fh_len !=3 && fh_len !=5 ) || (fh_type !=FILEID_UDF_WITH_PARENT && fh_type !=FILEID_UDF_WITHOUT_PARENT )) -return NULL ; +return ERR_PTR (-ESTALE );return udf_nfs_get_inode (sb, fid->udf.block, fid->udf.partref, fid->udf.generation);
@@ -1307,7 +1307,7 @@ static struct dentry *udf_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { if (fh_len != 5 || fh_type != FILEID_UDF_WITH_PARENT) - return NULL; + return ERR_PTR(-ESTALE); return udf_nfs_get_inode(sb, fid->udf.parent_block, fid->udf.parent_partref,
fs/xfs/linux-2.6/xfs_export.c +1 -1
@@ -150,7 +150,7 @@ xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid,struct inode *inode =NULL ;if (fh_len <xfs_fileid_length (fileid_type)) -return NULL ; +return ERR_PTR (-ESTALE );switch (fileid_type) {case FILEID_INO32_GEN_PARENT :
mm/shmem.c +7 -7
@@ -2050,21 +2050,21 @@ static struct dentry *shmem_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct inode *inode; -struct dentry *dentry =NULL ; +struct dentry *dentry;u64 inum = fid->raw[2 ]; inum = (inum <<32 ) | fid->raw[1 ];if (fh_len <3 ) -return NULL ; +return ERR_PTR (-ESTALE ); inode =ilookup5 (sb, (unsigned long )(inum + fid->raw[0 ]), shmem_match, fid->raw); -if (inode) { - dentry =d_find_alias (inode); -iput (inode); - } +if (!inode) +return ERR_PTR (-ESTALE ); -return dentry; + dentry =d_find_alias (inode); +iput (inode); +return dentry ?:ERR_PTR (-ESTALE ); } static int shmem_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
Quoted 9 lines · David Howells
Returning NULL from
fh_to_dentry()andfh_to_parent()is not permitted, so return-ESTALEinstead.exportfs_decode_fh()does not check for NULL, but will try to dereference it asIS_ERR()does not check for it.The
generic_fh_to_dentry()andgeneric_fh_to_parent()functions also no longer return NULL, but return-ESTALEinstead.Signed-off-by David Howells <[email protected]>Acked-by J. Bruce Fields <[email protected]>
Phew! Thanks.
Quoted 205 lines
---
fs/fat/inode.c | 2 +- fs/fuse/inode.c | 4 ++-- fs/gfs2/ops_export.c | 4 ++-- fs/isofs/export.c | 4 ++-- fs/libfs.c | 4 ++-- fs/ocfs2/export.c | 4 ++-- fs/udf/namei.c | 4 ++-- fs/xfs/linux-2.6/xfs_export.c | 2 +- mm/shmem.c | 14 +++++++------- 9 files changed, 21 insertions(+), 21 deletions(-)fs/fat/inode.c +1
-1@@ -657,7 +657,7 @@ static struct dentry *fat_fh_to_dentry(struct super_block *sb,u32 *fh = fid->raw;if (fh_len <5 || fh_type !=3 ) -return NULL ; +return ERR_PTR (-ESTALE ); inode =ilookup (sb, fh[0 ]);if (!inode || inode->i_generation != fh[1 ]) {fs/fuse/inode.c +2
-2@@ -653,7 +653,7 @@ static struct dentry *fuse_fh_to_dentry(struct super_block *sb,struct fuse_inode_handle handle;if ((fh_type !=0x81 && fh_type !=0x82 ) || fh_len <3 ) -return NULL ; +return ERR_PTR (-ESTALE ); handle.nodeid = (u64 ) fid->raw[0 ] <<32 ; handle.nodeid |= (u64 ) fid->raw[1 ];@@ -667,7 +667,7 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb,struct fuse_inode_handle parent;if (fh_type !=0x82 || fh_len <6 ) -return NULL ; +return ERR_PTR (-ESTALE ); parent.nodeid = (u64 ) fid->raw[3 ] <<32 ; parent.nodeid |= (u64 ) fid->raw[4 ];fs/gfs2/ops_export.c +2
-2@@ -254,7 +254,7 @@ static struct dentry *gfs2_fh_to_dentry(struct super_block *sb, struct fid *fid, this.no_addr |=be32_to_cpu (fh[3 ]);return gfs2_get_dentry (sb, &this);default : -return NULL ; +return ERR_PTR (-ESTALE ); } }@@ -273,7 +273,7 @@ static struct dentry *gfs2_fh_to_parent(struct super_block *sb, struct fid *fid, parent.no_addr |=be32_to_cpu (fh[7 ]);return gfs2_get_dentry (sb, &parent);default : -return NULL ; +return ERR_PTR (-ESTALE ); } }fs/isofs/export.c +2
-2@@ -164,7 +164,7 @@ static struct dentry *isofs_fh_to_dentry(struct super_block *sb,struct isofs_fid *ifid = (struct isofs_fid *)fid;if (fh_len <3 || fh_type >2 ) -return NULL ; +return ERR_PTR (-ESTALE );return isofs_export_iget (sb, ifid->block, ifid->offset, ifid->generation);@@ -176,7 +176,7 @@ static struct dentry *isofs_fh_to_parent(struct super_block *sb,struct isofs_fid *ifid = (struct isofs_fid *)fid;if (fh_type !=2 ) -return NULL ; +return ERR_PTR (-ESTALE ); return isofs_export_iget(sb, fh_len > 2 ? ifid->parent_block : 0,fs/libfs.c +2
-2@@ -751,7 +751,7 @@ struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid,struct inode *inode =NULL ;if (fh_len <2 ) -return NULL ; +return ERR_PTR (-ESTALE );switch (fh_type) {case FILEID_INO32_GEN :@@ -784,7 +784,7 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid,struct inode *inode =NULL ;if (fh_len <=2 ) -return NULL ; +return ERR_PTR (-ESTALE );switch (fh_type) {case FILEID_INO32_GEN_PARENT :fs/ocfs2/export.c +2
-2@@ -182,7 +182,7 @@ static struct dentry *ocfs2_fh_to_dentry(struct super_block *sb,struct ocfs2_inode_handle handle;if (fh_len <3 || fh_type >2 ) -return NULL ; +return ERR_PTR (-ESTALE ); handle.ih_blkno = (u64 )le32_to_cpu (fid->raw[0 ]) <<32 ; handle.ih_blkno |= (u64 )le32_to_cpu (fid->raw[1 ]);@@ -196,7 +196,7 @@ static struct dentry *ocfs2_fh_to_parent(struct super_block *sb,struct ocfs2_inode_handle parent;if (fh_type !=2 || fh_len <6 ) -return NULL ; +return ERR_PTR (-ESTALE ); parent.ih_blkno = (u64 )le32_to_cpu (fid->raw[3 ]) <<32 ; parent.ih_blkno |= (u64 )le32_to_cpu (fid->raw[4 ]);fs/udf/namei.c +2
-2@@ -1297,7 +1297,7 @@ static struct dentry *udf_fh_to_dentry(struct super_block *sb,if ((fh_len !=3 && fh_len !=5 ) || (fh_type !=FILEID_UDF_WITH_PARENT && fh_type !=FILEID_UDF_WITHOUT_PARENT )) -return NULL ; +return ERR_PTR (-ESTALE );return udf_nfs_get_inode (sb, fid->udf.block, fid->udf.partref, fid->udf.generation);@@ -1307,7 +1307,7 @@ static struct dentry *udf_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { if (fh_len != 5 || fh_type != FILEID_UDF_WITH_PARENT) - return NULL; + return ERR_PTR(-ESTALE); return udf_nfs_get_inode(sb, fid->udf.parent_block, fid->udf.parent_partref,fs/xfs/linux-2.6/xfs_export.c +1
-1@@ -150,7 +150,7 @@ xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid,struct inode *inode =NULL ;if (fh_len <xfs_fileid_length (fileid_type)) -return NULL ; +return ERR_PTR (-ESTALE );switch (fileid_type) {case FILEID_INO32_GEN_PARENT :mm/shmem.c +7
-7@@ -2050,21 +2050,21 @@ static struct dentry *shmem_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct inode *inode; -struct dentry *dentry =NULL ; +struct dentry *dentry;u64 inum = fid->raw[2 ]; inum = (inum <<32 ) | fid->raw[1 ];if (fh_len <3 ) -return NULL ; +return ERR_PTR (-ESTALE ); inode =ilookup5 (sb, (unsigned long )(inum + fid->raw[0 ]), shmem_match, fid->raw); -if (inode) { - dentry =d_find_alias (inode); -iput (inode); - } +if (!inode) +return ERR_PTR (-ESTALE ); -return dentry; + dentry =d_find_alias (inode); +iput (inode); +return dentry ?:ERR_PTR (-ESTALE ); } static int shmem_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
Quoted 7 lines · David Howells
Returning NULL from
fh_to_dentry()andfh_to_parent()is not permitted, so return-ESTALEinstead.exportfs_decode_fh()does not check for NULL, but will try to dereference it asIS_ERR()does not check for it.The
generic_fh_to_dentry()andgeneric_fh_to_parent()functions also no longer return NULL, but return-ESTALEinstead.
I really don't like this one.
So just about every fh_to_dentry() returns NULL for some error case. Instead of considering that a problem, maybe it should tell us something.
There is _one_ single caller of that function afaik, why shouldn't we just teach the caller that dammit, lots of people want to just return NULL.
It's not like returning NULL doesn't make sense. Quite frankly, I think it makes a lot more sense than returning -ESTALE, which is a very unnatural error for most filesystems.
So quite frankly, I'd much rather just see the simpler patch. Have exportfs handle the case.
Are there other callers?
Linus
--- fs/exportfs/expfs.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
fs/exportfs/expfs.c +2 -0
@@ -369,6 +369,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, result = nop->fh_to_dentry (mnt->mnt_sb, fid, fh_len, fileid_type);if (IS_ERR (result))return result; +if (!result) +return ERR_PTR (-ESTALE );if (S_ISDIR (result->d_inode->i_mode)) {/*
Quoted 19 lines · Linus Torvalds
Quoted 7 lines · David Howells
Returning NULL from
fh_to_dentry()andfh_to_parent()is not permitted, so return-ESTALEinstead.exportfs_decode_fh()does not check for NULL, but will try to dereference it asIS_ERR()does not check for it.The
generic_fh_to_dentry()andgeneric_fh_to_parent()functions also no longer return NULL, but return-ESTALEinstead.I really don't like this one.
So just about every
fh_to_dentry()returns NULL for some error case. Instead of considering that a problem, maybe it should tell us something.There is _one_ single caller of that function afaik, why shouldn't we just teach the caller that dammit, lots of people want to just return NULL.
Quoted 8 lines
It's not like returning NULL doesn't make sense. Quite frankly, I think it makes a lot more sense than returning
-ESTALE, which is a very unnatural error for most filesystems.So quite frankly, I'd much rather just see the simpler patch. Have exportfs handle the case.
Are there other callers?
Not of fh_to_dentry(), but it looks like fh_to_parent() is the same. So you'd want the below.
This is partially reverting 440037287c5, which appears to have also do some changing of NULLs to ESTALEs which we might also want to reconsider at some point. Maybe Christoph could explain what the rationale was.
--b.
fs/exportfs/expfs.c +4 -0
@@ -367,6 +367,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,* Try to get any dentry for the given file handle from the filesystem. */ result = nop->fh_to_dentry (mnt->mnt_sb, fid, fh_len, fileid_type); +if (!result) + result =ERR_PTR (-ESTALE );if (IS_ERR (result))return result;
@@ -420,6 +422,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, target_dir = nop->fh_to_parent (mnt->mnt_sb, fid, fh_len, fileid_type); +if (!target_dir) + goto err_result; err =PTR_ERR (target_dir);if (IS_ERR (target_dir)) goto err_result;
Quoted 6 lines · J. Bruce Fields
Not of
fh_to_dentry(), but it looks likefh_to_parent()is the same. So you'd want the below.This is partially reverting
440037287c5, which appears to have also do some changing of NULLs to ESTALEs which we might also want to reconsider at some point. Maybe Christoph could explain what the rationale was.
Miklos suggested always returning ERR_PTR values instead of a a min or NULLs and ERR_PTR values to make the interface cleaner. I tend to agree, but botched up the conversion.
Quoted 3 lines · Linus Torvalds
It's not like returning NULL doesn't make sense. Quite frankly, I think it makes a lot more sense than returning
-ESTALE, which is a very unnatural error for most filesystems.
The d_obtain_alias() function will immediately return -ESTALE if given a NULL inode, though, and sometimes it'll return some other error.
It would also seem odd to sometimes return NULL to indicate an error, and sometimes return a -ve error code to indicate an error. Perhaps one or the other should be selected for consistency.
David
Quoted 7 lines · David Howells
The
d_obtain_alias()function will immediately return-ESTALEif given a NULL inode, though, and sometimes it'll return some other error.It would also seem odd to sometimes return NULL to indicate an error, and sometimes return a -ve error code to indicate an error. Perhaps one or the other should be selected for consistency.
Well, the thing is, returning NULL is probably the most natural thing to do for a filesystem that doesn't really _have_ a valid error code for the situation. It's more of a "I can't do this" thing, than an error.
Sure, we can make all filesystems return -ESTALE, but not only is the patch fairly big, it really doesn't look at all better or make any more sense. ESTALE is really strictly a NFS thing - it doesn't tend to make sense for other filesystems (well, sure, other filesystems may have inode versions too and decide to use ESTALE for things, so I'm not claiming that it's _purely_ a NFS thing, but I think you see my point).
So it really seems to make more sense to just make the ESTALE handling be a NFS issue.
And notice that I'm not arguing that "fh_to_dentr/parent()" should _always_ return NULL for errors. There may be real reasons why a filesystem might want to return some actual error, like EIO. And maybe a filesystem actually wants to return an explicit ESTALE when that makes sense (ie when the filesystem _does_ find an inode that matches, but the generation doesn't match).
But I'm just looking at your patch, and seeing things like
if (fh_len <= 2)
- return NULL;
+ return ERR_PTR(-ESTALE);
and going "That really didn't make the code any prettier or easier to understand".
Linus
PATCH 1/1 EXPORTFS: handle NULL returns from fh_to_dentry()/fh_to_parent()
From: J. Bruce Fields <[email protected]>
While 440037287c5 "[PATCH] switch all filesystems over to d_obtain_alias" removed some cases where fh_to_dentry() and fh_to_parent() could return NULL, there are still a few NULL returns left in individual filesystems. Thus it was a mistake for that commit to remove the handling of NULL returns in the callers.
Revert those parts of 440037287c5 which removed the NULL handling.
(We could, alternatively, modify all implementations to return -ESTALE instead of NULL, but that proves to require fixing a number of filesystems, and in some cases it's arguably more natural to return NULL.)
Thanks to David for original patch and Linus, Christoph, and Hugh for review.
--- fs/exportfs/expfs.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
Quoted 2 lines · Linus Torvalds
So it really seems to make more sense to just make the ESTALE handling be a NFS issue.
This thread seems to have died out, and I didn't see any objections to handling the error in the caller as Linus suggested, so here's the patch.
Also available from the for-2.6.28 branch at
git://linux-nfs.org/~bfields/linux.git for-2.6.28
--b.
fs/exportfs/expfs.c +4 -0
@@ -367,6 +367,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,* Try to get any dentry for the given file handle from the filesystem. */ result = nop->fh_to_dentry (mnt->mnt_sb, fid, fh_len, fileid_type); +if (!result) + result =ERR_PTR (-ESTALE );if (IS_ERR (result))return result;
@@ -420,6 +422,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, target_dir = nop->fh_to_parent (mnt->mnt_sb, fid, fh_len, fileid_type); +if (!target_dir) + goto err_result; err =PTR_ERR (target_dir);if (IS_ERR (target_dir)) goto err_result;
Signature · 1 line
1.5.5.rc1
Re: [PATCH] EXPORTFS: handle NULL returns from fh_to_dentry()/fh_to_parent()
fs/exportfs/expfs.c struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, · line 373
Quoted 4 lines · J. Bruce Fields
fs/exportfs/expfs.c +2
-0+if (!result) + result =ERR_PTR (-ESTALE );if (IS_ERR (result))return result;
This is why I think this is a bad idea. This adds an extra conditional test and branch. In fact, the way you've done it means you have to do the extra branch whatever[*]; it might be better to have the if-statement you added return directly.
[*] assuming gcc doesn't manage to be clever enough to omit the second test if
the first is true.
David