NFSv4: Ensure we request the ordinary fileid when doing readdirplus
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 27 Apr 2011 17:47:52 +0000 (13:47 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 27 Apr 2011 19:57:16 +0000 (15:57 -0400)
When readdir() returns a directory entry for the root of a mounted
filesystem, Linux follows the old convention of returning the inode
number of the covered directory (despite newer versions of POSIX declaring
that this is a bug).
To ensure this continues to work, the NFSv4 readdir implementation requests
the 'mounted-on-fileid' from the server.

However, readdirplus also needs to instantiate an inode for this entry, and
for that, we also need to request the real fileid as per this patch.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/nfs4xdr.c
include/linux/nfs_xdr.h

index ba952bd..7310d2e 100644 (file)
@@ -1452,26 +1452,25 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args,
 
 static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
 {
-       uint32_t attrs[2] = {0, 0};
+       uint32_t attrs[2] = {
+               FATTR4_WORD0_RDATTR_ERROR,
+               FATTR4_WORD1_MOUNTED_ON_FILEID,
+       };
        uint32_t dircount = readdir->count >> 1;
        __be32 *p;
 
        if (readdir->plus) {
                attrs[0] |= FATTR4_WORD0_TYPE|FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE|
-                       FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE;
+                       FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE|FATTR4_WORD0_FILEID;
                attrs[1] |= FATTR4_WORD1_MODE|FATTR4_WORD1_NUMLINKS|FATTR4_WORD1_OWNER|
                        FATTR4_WORD1_OWNER_GROUP|FATTR4_WORD1_RAWDEV|
                        FATTR4_WORD1_SPACE_USED|FATTR4_WORD1_TIME_ACCESS|
                        FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
                dircount >>= 1;
        }
-       attrs[0] |= FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID;
-       attrs[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;
-       /* Switch to mounted_on_fileid if the server supports it */
-       if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
-               attrs[0] &= ~FATTR4_WORD0_FILEID;
-       else
-               attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
+       /* Use mounted_on_fileid only if the server supports it */
+       if (!(readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID))
+               attrs[0] |= FATTR4_WORD0_FILEID;
 
        p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20);
        *p++ = cpu_to_be32(OP_READDIR);
@@ -3140,7 +3139,7 @@ static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitma
                        goto out_overflow;
                xdr_decode_hyper(p, fileid);
                bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
-               ret = NFS_ATTR_FATTR_FILEID;
+               ret = NFS_ATTR_FATTR_MOUNTED_ON_FILEID;
        }
        dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
        return ret;
@@ -4002,7 +4001,6 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 {
        int status;
        umode_t fmode = 0;
-       uint64_t fileid;
        uint32_t type;
 
        status = decode_attr_type(xdr, bitmap, &type);
@@ -4101,13 +4099,10 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                goto xdr_error;
        fattr->valid |= status;
 
-       status = decode_attr_mounted_on_fileid(xdr, bitmap, &fileid);
+       status = decode_attr_mounted_on_fileid(xdr, bitmap, &fattr->mounted_on_fileid);
        if (status < 0)
                goto xdr_error;
-       if (status != 0 && !(fattr->valid & status)) {
-               fattr->fileid = fileid;
-               fattr->valid |= status;
-       }
+       fattr->valid |= status;
 
 xdr_error:
        dprintk("%s: xdr returned %d\n", __func__, -status);
@@ -6411,7 +6406,9 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
        if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
                                        entry->server, 1) < 0)
                goto out_overflow;
-       if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
+       if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
+               entry->ino = entry->fattr->mounted_on_fileid;
+       else if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
                entry->ino = entry->fattr->fileid;
 
        entry->d_type = DT_UNKNOWN;
index 78b101e..890dce2 100644 (file)
@@ -50,6 +50,7 @@ struct nfs_fattr {
        } du;
        struct nfs_fsid         fsid;
        __u64                   fileid;
+       __u64                   mounted_on_fileid;
        struct timespec         atime;
        struct timespec         mtime;
        struct timespec         ctime;
@@ -83,6 +84,7 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_PRECHANGE       (1U << 18)
 #define NFS_ATTR_FATTR_V4_REFERRAL     (1U << 19)      /* NFSv4 referral */
 #define NFS_ATTR_FATTR_MOUNTPOINT      (1U << 20)      /* Treat as mountpoint */
+#define NFS_ATTR_FATTR_MOUNTED_ON_FILEID               (1U << 21)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
                | NFS_ATTR_FATTR_MODE \