Hi filesystem folks,
I just had a report that BTRFS doesn't work reliably with NFSv2.
The problem is that NFSv2 doesn't encode filehandle size so it may
report to the filesystem a longer handle that is being expected.
Filesystems should not require a specific length, only at least that
length.
A code audit shows that NILFS2 and UDF suffer the same problem.
Following patches should fix it... well, they compile and look good.
Please consider for inclusion is respective trees.
Admittedly NFSv2 is a bit "last century", but while it is easy to
support, we may as well.
Thanks,
NeilBrown
---
NeilBrown (3):
BTRFS: support NFSv2 export
NILFS2: support NFSv2 export
UDF: support NFSv2 export
fs/btrfs/export.c | 10 +++++-----
fs/nilfs2/namei.c | 6 +++---
fs/udf/namei.c | 16 ++++++++++++----
3 files changed, 20 insertions(+), 12 deletions(-)
--
Signature
The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
that returned by encode_fh - it may be larger.
With NFSv2, the filehandle is fixed length, so it may appear longer
than expected and be zero-padded.
So we must test that fh_len is at least some value, not exactly equal
to it.
Signed-off-by: NeilBrown <[email protected]>
---
fs/nilfs2/namei.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index 22180836ec22..b65fb79d16fd 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -496,8 +496,8 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
{
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
- if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
- fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
+ if ((fh_len < NILFS_FID_SIZE_NON_CONNECTABLE &&
+ fh_len < NILFS_FID_SIZE_CONNECTABLE) ||
(fh_type != FILEID_NILFS_WITH_PARENT &&
fh_type != FILEID_NILFS_WITHOUT_PARENT))
return NULL;
@@ -510,7 +510,7 @@ static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh,
{
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
- if (fh_len != NILFS_FID_SIZE_CONNECTABLE ||
+ if (fh_len < NILFS_FID_SIZE_CONNECTABLE ||
fh_type != FILEID_NILFS_WITH_PARENT)
return NULL;
The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
that returned by encode_fh - it may be larger.
With NFSv2, the filehandle is fixed length, so it may appear longer
than expected and be zero-padded.
So we must test that fh_len is at least some value, not exactly equal
to it.
Signed-off-by: NeilBrown <[email protected]>
---
fs/btrfs/export.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c
index 8d052209f473..2513a7f53334 100644
--- a/fs/btrfs/export.c
+++ b/fs/btrfs/export.c
@@ -112,11 +112,11 @@ static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,
u32 generation;
if (fh_type == FILEID_BTRFS_WITH_PARENT) {
- if (fh_len != BTRFS_FID_SIZE_CONNECTABLE)
+ if (fh_len < BTRFS_FID_SIZE_CONNECTABLE)
return NULL;
root_objectid = fid->root_objectid;
} else if (fh_type == FILEID_BTRFS_WITH_PARENT_ROOT) {
- if (fh_len != BTRFS_FID_SIZE_CONNECTABLE_ROOT)
+ if (fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT)
return NULL;
root_objectid = fid->parent_root_objectid;
} else
@@ -136,11 +136,11 @@ static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
u32 generation;
if ((fh_type != FILEID_BTRFS_WITH_PARENT ||
- fh_len != BTRFS_FID_SIZE_CONNECTABLE) &&
+ fh_len < BTRFS_FID_SIZE_CONNECTABLE) &&
(fh_type != FILEID_BTRFS_WITH_PARENT_ROOT ||
- fh_len != BTRFS_FID_SIZE_CONNECTABLE_ROOT) &&
+ fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT) &&
(fh_type != FILEID_BTRFS_WITHOUT_PARENT ||
- fh_len != BTRFS_FID_SIZE_NON_CONNECTABLE))
+ fh_len < BTRFS_FID_SIZE_NON_CONNECTABLE))
return NULL;
objectid = fid->objectid;
The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
that returned by encode_fh - it may be larger.
With NFSv2, the filehandle is fixed length, so it may appear longer
than expected and be zero-padded.
So we must test that fh_len is at least some value, not exactly equal
to it.
Signed-off-by: NeilBrown <[email protected]>
---
fs/udf/namei.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 5c03f0dfb98b..facc2a840f7b 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -1221,7 +1221,7 @@ static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block,
static struct dentry *udf_fh_to_dentry(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type)
{
- if ((fh_len != 3 && fh_len != 5) ||
+ if (fh_len < 3 ||
(fh_type != FILEID_UDF_WITH_PARENT &&
fh_type != FILEID_UDF_WITHOUT_PARENT))
return NULL;
@@ -1233,7 +1233,7 @@ static struct dentry *udf_fh_to_dentry(struct super_block *sb,
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)
+ if (fh_len < 5 || fh_type != FILEID_UDF_WITH_PARENT)
return NULL;
return udf_nfs_get_inode(sb, fid->udf.parent_block,
On Fri, 08 May 2015 10:16:23 +1000, NeilBrown <[email protected]> wrote:
> The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
> that returned by encode_fh - it may be larger.
>
> With NFSv2, the filehandle is fixed length, so it may appear longer
> than expected and be zero-padded.
>
> So we must test that fh_len is at least some value, not exactly equal
> to it.
>
> Signed-off-by: NeilBrown <[email protected]>
> ---
> fs/nilfs2/namei.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
> index 22180836ec22..b65fb79d16fd 100644
> --- a/fs/nilfs2/namei.c
> +++ b/fs/nilfs2/namei.c
> @@ -496,8 +496,8 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
> {
> struct nilfs_fid *fid = (struct nilfs_fid *)fh;
>
> - if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
> - fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
> + if ((fh_len < NILFS_FID_SIZE_NON_CONNECTABLE &&
> + fh_len < NILFS_FID_SIZE_CONNECTABLE) ||
> (fh_type != FILEID_NILFS_WITH_PARENT &&
> fh_type != FILEID_NILFS_WITHOUT_PARENT))
> return NULL;
A bit weird. "fh_len < NILFS_FID_SIZE_CONNECTABLE" implies "fh_len <
NILFS_FID_SIZE_NON_CONNECTABLE".
How about the following fix ?
if ((fh_type != FILEID_NILFS_WITH_PARENT ||
fh_len < NILFS_FID_SIZE_CONNECTABLE) &&
(fh_type != FILEID_NILFS_WITHOUT_PARENT ||
fh_len < NILFS_FID_SIZE_NON_CONNECTABLE))
return NULL;
Regards,
Ryusuke Konishi
> @@ -510,7 +510,7 @@ static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh,
> {
> struct nilfs_fid *fid = (struct nilfs_fid *)fh;
>
> - if (fh_len != NILFS_FID_SIZE_CONNECTABLE ||
> + if (fh_len < NILFS_FID_SIZE_CONNECTABLE ||
> fh_type != FILEID_NILFS_WITH_PARENT)
> return NULL;
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, 11 May 2015 01:31:43 +0900 (JST) Ryusuke Konishi
<[email protected]> wrote:
> On Fri, 08 May 2015 10:16:23 +1000, NeilBrown <[email protected]> wrote:
> > The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
> > that returned by encode_fh - it may be larger.
> >
> > With NFSv2, the filehandle is fixed length, so it may appear longer
> > than expected and be zero-padded.
> >
> > So we must test that fh_len is at least some value, not exactly equal
> > to it.
> >
> > Signed-off-by: NeilBrown <[email protected]>
> > ---
> > fs/nilfs2/namei.c | 6 +++---
> > 1 file changed, 3 insertions(+), 3 deletions(-)
> >
> > diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
> > index 22180836ec22..b65fb79d16fd 100644
> > --- a/fs/nilfs2/namei.c
> > +++ b/fs/nilfs2/namei.c
> > @@ -496,8 +496,8 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
> > {
> > struct nilfs_fid *fid = (struct nilfs_fid *)fh;
> >
> > - if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
> > - fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
>
> > + if ((fh_len < NILFS_FID_SIZE_NON_CONNECTABLE &&
> > + fh_len < NILFS_FID_SIZE_CONNECTABLE) ||
> > (fh_type != FILEID_NILFS_WITH_PARENT &&
> > fh_type != FILEID_NILFS_WITHOUT_PARENT))
> > return NULL;
>
> A bit weird. "fh_len < NILFS_FID_SIZE_CONNECTABLE" implies "fh_len <
> NILFS_FID_SIZE_NON_CONNECTABLE".
>
> How about the following fix ?
>
> if ((fh_type != FILEID_NILFS_WITH_PARENT ||
> fh_len < NILFS_FID_SIZE_CONNECTABLE) &&
> (fh_type != FILEID_NILFS_WITHOUT_PARENT ||
> fh_len < NILFS_FID_SIZE_NON_CONNECTABLE))
> return NULL;
>
Yes, weird. The code only uses the early parts of the filehandle, so we
only need to complain if the fh_len is less than FILEID_NILFS_WITHOUT_PARENT.
So I'd prefer:
@@ -496,8 +496,7 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
{
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
- if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
- fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
+ if (fh_len < NILFS_FID_SIZE_NON_CONNECTABLE ||
(fh_type != FILEID_NILFS_WITH_PARENT &&
fh_type != FILEID_NILFS_WITHOUT_PARENT))
return NULL;
Would you be OK with that? If so I'll resend.
Thanks,
NeilBrown
On Fri, May 08, 2015 at 10:16:23AM +1000, NeilBrown wrote:
> The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
> that returned by encode_fh - it may be larger.
>
> With NFSv2, the filehandle is fixed length, so it may appear longer
> than expected and be zero-padded.
>
> So we must test that fh_len is at least some value, not exactly equal
> to it.
>
> Signed-off-by: NeilBrown <[email protected]>
Acked-by: David Sterba <[email protected]>
On Fri 08-05-15 10:16:23, NeilBrown wrote:
> The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
> that returned by encode_fh - it may be larger.
>
> With NFSv2, the filehandle is fixed length, so it may appear longer
> than expected and be zero-padded.
>
> So we must test that fh_len is at least some value, not exactly equal
> to it.
>
> Signed-off-by: NeilBrown <[email protected]>
Thanks. The patch looks good to me. I've added it to my tree.
Honza
> ---
> fs/udf/namei.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/fs/udf/namei.c b/fs/udf/namei.c
> index 5c03f0dfb98b..facc2a840f7b 100644
> --- a/fs/udf/namei.c
> +++ b/fs/udf/namei.c
> @@ -1221,7 +1221,7 @@ static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block,
> static struct dentry *udf_fh_to_dentry(struct super_block *sb,
> struct fid *fid, int fh_len, int fh_type)
> {
> - if ((fh_len != 3 && fh_len != 5) ||
> + if (fh_len < 3 ||
> (fh_type != FILEID_UDF_WITH_PARENT &&
> fh_type != FILEID_UDF_WITHOUT_PARENT))
> return NULL;
> @@ -1233,7 +1233,7 @@ static struct dentry *udf_fh_to_dentry(struct super_block *sb,
> 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)
> + if (fh_len < 5 || fh_type != FILEID_UDF_WITH_PARENT)
> return NULL;
>
> return udf_nfs_get_inode(sb, fid->udf.parent_block,
>
>
--
Jan Kara <[email protected]>
SUSE Labs, CR
On Mon, 11 May 2015 17:02:51 +1000, NeilBrown wrote:
> On Mon, 11 May 2015 01:31:43 +0900 (JST) Ryusuke Konishi
> <[email protected]> wrote:
>
>> On Fri, 08 May 2015 10:16:23 +1000, NeilBrown <[email protected]> wrote:
>> > The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
>> > that returned by encode_fh - it may be larger.
>> >
>> > With NFSv2, the filehandle is fixed length, so it may appear longer
>> > than expected and be zero-padded.
>> >
>> > So we must test that fh_len is at least some value, not exactly equal
>> > to it.
>> >
>> > Signed-off-by: NeilBrown <[email protected]>
>> > ---
>> > fs/nilfs2/namei.c | 6 +++---
>> > 1 file changed, 3 insertions(+), 3 deletions(-)
>> >
>> > diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
>> > index 22180836ec22..b65fb79d16fd 100644
>> > --- a/fs/nilfs2/namei.c
>> > +++ b/fs/nilfs2/namei.c
>> > @@ -496,8 +496,8 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
>> > {
>> > struct nilfs_fid *fid = (struct nilfs_fid *)fh;
>> >
>> > - if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
>> > - fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
>>
>> > + if ((fh_len < NILFS_FID_SIZE_NON_CONNECTABLE &&
>> > + fh_len < NILFS_FID_SIZE_CONNECTABLE) ||
>> > (fh_type != FILEID_NILFS_WITH_PARENT &&
>> > fh_type != FILEID_NILFS_WITHOUT_PARENT))
>> > return NULL;
>>
>> A bit weird. "fh_len < NILFS_FID_SIZE_CONNECTABLE" implies "fh_len <
>> NILFS_FID_SIZE_NON_CONNECTABLE".
>>
>> How about the following fix ?
>>
>> if ((fh_type != FILEID_NILFS_WITH_PARENT ||
>> fh_len < NILFS_FID_SIZE_CONNECTABLE) &&
>> (fh_type != FILEID_NILFS_WITHOUT_PARENT ||
>> fh_len < NILFS_FID_SIZE_NON_CONNECTABLE))
>> return NULL;
>>
>
> Yes, weird. The code only uses the early parts of the filehandle, so we
> only need to complain if the fh_len is less than FILEID_NILFS_WITHOUT_PARENT.
>
> So I'd prefer:
>
> @@ -496,8 +496,7 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
> {
> struct nilfs_fid *fid = (struct nilfs_fid *)fh;
>
> - if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
> - fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
> + if (fh_len < NILFS_FID_SIZE_NON_CONNECTABLE ||
> (fh_type != FILEID_NILFS_WITH_PARENT &&
> fh_type != FILEID_NILFS_WITHOUT_PARENT))
> return NULL;
>
>
> Would you be OK with that? If so I'll resend.
>
> Thanks,
> NeilBrown
Thanks. This looks OK to me.
I'll apply it if you will resend.
Regards,
Ryusuke Konishi
The "fh_len" passed to ->fh_to_* is not guaranteed to be that same as
that returned by encode_fh - it may be larger.
With NFSv2, the filehandle is fixed length, so it may appear longer
than expected and be zero-padded.
So we must test that fh_len is at least some value, not exactly equal
to it.
Signed-off-by: NeilBrown <[email protected]>
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index 22180836ec22..37dd6b05b1b5 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -496,8 +496,7 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
{
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
- if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
- fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
+ if (fh_len < NILFS_FID_SIZE_NON_CONNECTABLE ||
(fh_type != FILEID_NILFS_WITH_PARENT &&
fh_type != FILEID_NILFS_WITHOUT_PARENT))
return NULL;
@@ -510,7 +509,7 @@ static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh,
{
struct nilfs_fid *fid = (struct nilfs_fid *)fh;
- if (fh_len != NILFS_FID_SIZE_CONNECTABLE ||
+ if (fh_len < NILFS_FID_SIZE_CONNECTABLE ||
fh_type != FILEID_NILFS_WITH_PARENT)
return NULL;