[GFS2] Fix bug where lock not held
[powerpc.git] / fs / nfsd / nfs4xdr.c
index df34195..41fc241 100644 (file)
 
 #define NFSDDBG_FACILITY               NFSDDBG_XDR
 
+/*
+ * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
+ * directory in order to indicate to the client that a filesystem boundary is present
+ * We use a fixed fsid for a referral
+ */
+#define NFS4_REFERRAL_FSID_MAJOR       0x8000000ULL
+#define NFS4_REFERRAL_FSID_MINOR       0x8000000ULL
+
 static int
 check_filename(char *str, int len, int err)
 {
@@ -1223,6 +1231,119 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                        stateowner->so_replay.rp_buflen);       \
        } } while (0);
 
+/* Encode as an array of strings the string given with components
+ * seperated @sep.
+ */
+static int nfsd4_encode_components(char sep, char *components,
+                                  u32 **pp, int *buflen)
+{
+       u32 *p = *pp;
+       u32 *countp = p;
+       int strlen, count=0;
+       char *str, *end;
+
+       dprintk("nfsd4_encode_components(%s)\n", components);
+       if ((*buflen -= 4) < 0)
+               return nfserr_resource;
+       WRITE32(0); /* We will fill this in with @count later */
+       end = str = components;
+       while (*end) {
+               for (; *end && (*end != sep); end++)
+                       ; /* Point to end of component */
+               strlen = end - str;
+               if (strlen) {
+                       if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
+                               return nfserr_resource;
+                       WRITE32(strlen);
+                       WRITEMEM(str, strlen);
+                       count++;
+               }
+               else
+                       end++;
+               str = end;
+       }
+       *pp = p;
+       p = countp;
+       WRITE32(count);
+       return 0;
+}
+
+/*
+ * encode a location element of a fs_locations structure
+ */
+static int nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
+                                   u32 **pp, int *buflen)
+{
+       int status;
+       u32 *p = *pp;
+
+       status = nfsd4_encode_components(':', location->hosts, &p, buflen);
+       if (status)
+               return status;
+       status = nfsd4_encode_components('/', location->path, &p, buflen);
+       if (status)
+               return status;
+       *pp = p;
+       return 0;
+}
+
+/*
+ * Return the path to an export point in the pseudo filesystem namespace
+ * Returned string is safe to use as long as the caller holds a reference
+ * to @exp.
+ */
+static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp)
+{
+       struct svc_fh tmp_fh;
+       char *path, *rootpath;
+       int stat;
+
+       fh_init(&tmp_fh, NFS4_FHSIZE);
+       stat = exp_pseudoroot(rqstp->rq_client, &tmp_fh, &rqstp->rq_chandle);
+       if (stat)
+               return ERR_PTR(stat);
+       rootpath = tmp_fh.fh_export->ex_path;
+
+       path = exp->ex_path;
+
+       if (strncmp(path, rootpath, strlen(rootpath))) {
+               printk("nfsd: fs_locations failed;"
+                       "%s is not contained in %s\n", path, rootpath);
+               return ERR_PTR(-EOPNOTSUPP);
+       }
+
+       return path + strlen(rootpath);
+}
+
+/*
+ *  encode a fs_locations structure
+ */
+static int nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
+                                    struct svc_export *exp,
+                                    u32 **pp, int *buflen)
+{
+       int status, i;
+       u32 *p = *pp;
+       struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
+       char *root = nfsd4_path(rqstp, exp);
+
+       if (IS_ERR(root))
+               return PTR_ERR(root);
+       status = nfsd4_encode_components('/', root, &p, buflen);
+       if (status)
+               return status;
+       if ((*buflen -= 4) < 0)
+               return nfserr_resource;
+       WRITE32(fslocs->locations_count);
+       for (i=0; i<fslocs->locations_count; i++) {
+               status = nfsd4_encode_fs_location4(&fslocs->locations[i],
+                                                  &p, buflen);
+               if (status)
+                       return status;
+       }
+       *pp = p;
+       return 0;
+}
 
 static u32 nfs4_ftypes[16] = {
         NF4BAD,  NF4FIFO, NF4CHR, NF4BAD,
@@ -1272,6 +1393,25 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
        return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
 }
 
+#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
+                             FATTR4_WORD0_RDATTR_ERROR)
+#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
+
+static int fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
+{
+       /* As per referral draft:  */
+       if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
+           *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
+               if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
+                   *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
+                       *rdattr_err = NFSERR_MOVED;
+               else
+                       return nfserr_moved;
+       }
+       *bmval0 &= WORD0_ABSENT_FS_ATTRS;
+       *bmval1 &= WORD1_ABSENT_FS_ATTRS;
+       return 0;
+}
 
 /*
  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
@@ -1294,6 +1434,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        u32 *attrlenp;
        u32 dummy;
        u64 dummy64;
+       u32 rdattr_err = 0;
        u32 *p = buffer;
        int status;
        int aclsupport = 0;
@@ -1303,6 +1444,12 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
        BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1);
 
+       if (exp->ex_fslocs.migrated) {
+               status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err);
+               if (status)
+                       goto out;
+       }
+
        status = vfs_getattr(exp->ex_mnt, dentry, &stat);
        if (status)
                goto out_nfserr;
@@ -1334,6 +1481,11 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                                goto out_nfserr;
                }
        }
+       if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
+               if (exp->ex_fslocs.locations == NULL) {
+                       bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS;
+               }
+       }
        if ((buflen -= 16) < 0)
                goto out_resource;
 
@@ -1343,12 +1495,15 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        attrlenp = p++;                /* to be backfilled later */
 
        if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
+               u32 word0 = NFSD_SUPPORTED_ATTRS_WORD0;
                if ((buflen -= 12) < 0)
                        goto out_resource;
+               if (!aclsupport)
+                       word0 &= ~FATTR4_WORD0_ACL;
+               if (!exp->ex_fslocs.locations)
+                       word0 &= ~FATTR4_WORD0_FS_LOCATIONS;
                WRITE32(2);
-               WRITE32(aclsupport ?
-                       NFSD_SUPPORTED_ATTRS_WORD0 :
-                       NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL);
+               WRITE32(word0);
                WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
        }
        if (bmval0 & FATTR4_WORD0_TYPE) {
@@ -1402,7 +1557,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_FSID) {
                if ((buflen -= 16) < 0)
                        goto out_resource;
-               if (is_fsid(fhp, rqstp->rq_reffh)) {
+               if (exp->ex_fslocs.migrated) {
+                       WRITE64(NFS4_REFERRAL_FSID_MAJOR);
+                       WRITE64(NFS4_REFERRAL_FSID_MINOR);
+               } else if (is_fsid(fhp, rqstp->rq_reffh)) {
                        WRITE64((u64)exp->ex_fsid);
                        WRITE64((u64)0);
                } else {
@@ -1425,7 +1583,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
-               WRITE32(0);
+               WRITE32(rdattr_err);
        }
        if (bmval0 & FATTR4_WORD0_ACL) {
                struct nfs4_ace *ace;
@@ -1513,6 +1671,13 @@ out_acl:
                        goto out_resource;
                WRITE64((u64) statfs.f_files);
        }
+       if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
+               status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
+               if (status == nfserr_resource)
+                       goto out_resource;
+               if (status)
+                       goto out;
+       }
        if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
@@ -1536,12 +1701,12 @@ out_acl:
        if (bmval0 & FATTR4_WORD0_MAXREAD) {
                if ((buflen -= 8) < 0)
                        goto out_resource;
-               WRITE64((u64) NFSSVC_MAXBLKSIZE);
+               WRITE64((u64) svc_max_payload(rqstp));
        }
        if (bmval0 & FATTR4_WORD0_MAXWRITE) {
                if ((buflen -= 8) < 0)
                        goto out_resource;
-               WRITE64((u64) NFSSVC_MAXBLKSIZE);
+               WRITE64((u64) svc_max_payload(rqstp));
        }
        if (bmval1 & FATTR4_WORD1_MODE) {
                if ((buflen -= 4) < 0)
@@ -1845,7 +2010,6 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_ge
        nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry,
                                    resp->p, &buflen, getattr->ga_bmval,
                                    resp->rqstp);
-
        if (!nfserr)
                resp->p += buflen;
        return nfserr;
@@ -2055,7 +2219,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr,
 
        RESERVE_SPACE(8); /* eof flag and byte count */
 
-       maxcount = NFSSVC_MAXBLKSIZE;
+       maxcount = svc_max_payload(resp->rqstp);
        if (maxcount > read->rd_length)
                maxcount = read->rd_length;