]> git.hungrycats.org Git - linux/commitdiff
[PATCH] PATCH - knfsd in 2.5.6 - fsid= export option
authorNeil Brown <neilb@cse.unsw.edu.au>
Tue, 12 Mar 2002 04:47:30 +0000 (20:47 -0800)
committerLinus Torvalds <torvalds@home.transmeta.com>
Tue, 12 Mar 2002 04:47:30 +0000 (20:47 -0800)
Support fsid=<number> export option to be device number independent

This patch was largely supplied by Steven Whitehouse <steve@gw.chygwyn.com>

A new export option "NFSEXP_FSID" indicates that the ex_dev passed down
is a user specified number, not a device number.
It should be used in fsid_type==1 filehandles to identify the
the exportpoint rather than the devid and inode (as in fsid_type == 0).
This allows filehandles to be device-number independent so that when Linux
changes device numbers on you (after reboot), your filesystems wont go stale.

User-space support for this is in the nfs-utils CVS and will be in
the next release (any release > 1.0).

fs/nfsd/export.c
fs/nfsd/nfsfh.c
include/linux/nfsd/export.h

index 3a18be362dc4410d2ba968d70bac5c67eaa81c80..76d7851d169812a8dd16694b55f214f2e8fd868b 100644 (file)
@@ -52,6 +52,7 @@ static int            exp_verify_string(char *cp, int max);
                ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
 /* XXX: is this adequate for 32bit kdev_t ? */
 #define EXPORT_HASH(dev)       (minor(dev) & (NFSCLNT_EXPMAX - 1))
+#define EXPORT_FSID_HASH(fsid) ((fsid) & (NFSCLNT_EXPMAX - 1))
 
 struct svc_clnthash {
        struct svc_clnthash *   h_next;
@@ -82,6 +83,27 @@ exp_get(svc_client *clp, kdev_t dev, ino_t ino)
        return NULL;
 }
 
+/*
+ * Find the client's export entry matching fsid
+ */
+svc_export *
+exp_get_fsid(svc_client *clp, int fsid)
+{
+       struct list_head *head, *p;
+
+
+       if (!clp)
+               return NULL;
+
+       head = &clp->cl_expfsid[EXPORT_FSID_HASH(fsid)];
+       list_for_each(p, head) {
+               svc_export *exp = list_entry(p, svc_export, ex_fsid_hash);
+               if (exp->ex_fsid == fsid)
+                       return exp;
+       }
+       return NULL;
+}
+
 svc_export *
 exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry)
 {
@@ -192,6 +214,25 @@ exp_writeunlock(void)
        up_write(&hash_sem);
 }
 
+static void exp_fsid_unhash(struct svc_export *exp)
+{
+
+       if ((exp->ex_flags & NFSEXP_FSID) == 0)
+               return;
+
+       list_del_init(&exp->ex_fsid_hash);
+}
+
+static void exp_fsid_hash(struct svc_client *clp, struct svc_export *exp)
+{
+       struct list_head *head;
+
+       if ((exp->ex_flags & NFSEXP_FSID) == 0)
+               return;
+       head = clp->cl_expfsid + EXPORT_FSID_HASH(exp->ex_fsid);
+       list_add(&exp->ex_fsid_hash, head);
+}
+
 /*
  * Export a file system.
  */
@@ -199,7 +240,8 @@ int
 exp_export(struct nfsctl_export *nxp)
 {
        svc_client      *clp;
-       svc_export      *exp, *parent;
+       svc_export      *exp = NULL, *parent;
+       svc_export      *fsid_exp;
        struct nameidata nd;
        struct inode    *inode = NULL;
        int             err;
@@ -215,8 +257,6 @@ exp_export(struct nfsctl_export *nxp)
        dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
                        nxp->ex_client, nxp->ex_path,
                        nxp->ex_dev, (long) nxp->ex_ino, nxp->ex_flags);
-       dev = to_kdev_t(nxp->ex_dev);
-       ino = nxp->ex_ino;
 
        /* Try to lock the export table for update */
        exp_writelock();
@@ -225,31 +265,35 @@ exp_export(struct nfsctl_export *nxp)
        if (!(clp = exp_getclientbyname(nxp->ex_client)))
                goto out_unlock;
 
-       /*
-        * If there's already an export for this file, assume this
-        * is just a flag update.
-        */
-       if ((exp = exp_get(clp, dev, ino)) != NULL) {
-               exp->ex_flags    = nxp->ex_flags;
-               exp->ex_anon_uid = nxp->ex_anon_uid;
-               exp->ex_anon_gid = nxp->ex_anon_gid;
-               err = 0;
-               goto out_unlock;
-       }
 
        /* Look up the dentry */
        err = path_lookup(nxp->ex_path, 0, &nd);
        if (err)
                goto out_unlock;
-
        inode = nd.dentry->d_inode;
+       dev = inode->i_dev;
+       ino = inode->i_ino;
        err = -EINVAL;
-       if (!kdev_same(inode->i_dev, dev) || inode->i_ino != nxp->ex_ino) {
-               printk(KERN_DEBUG "exp_export: i_dev = %02x:%02x, dev = %02x:%02x\n",
-                       major(inode->i_dev), minor(inode->i_dev),
-                       major(dev), minor(dev)); 
-               /* I'm just being paranoid... */
-               goto finish;
+
+       exp = exp_get(clp, dev, ino);
+
+       /* must make sure there wont be an ex_fsid clash */
+       if ((nxp->ex_flags & NFSEXP_FSID) &&
+           (fsid_exp = exp_get_fsid(clp, nxp->ex_dev)) &&
+           fsid_exp != exp)
+               goto out_unlock;
+
+       if (exp != NULL) {
+               /* just a flags/id/fsid update */
+
+               exp_fsid_unhash(exp);
+               exp->ex_flags    = nxp->ex_flags;
+               exp->ex_anon_uid = nxp->ex_anon_uid;
+               exp->ex_anon_gid = nxp->ex_anon_gid;
+               exp->ex_fsid     = nxp->ex_dev;
+               exp_fsid_hash(clp, exp);
+               err = 0;
+               goto out_unlock;
        }
 
        /* We currently export only dirs and regular files.
@@ -292,6 +336,8 @@ exp_export(struct nfsctl_export *nxp)
        exp->ex_ino = ino;
        exp->ex_anon_uid = nxp->ex_anon_uid;
        exp->ex_anon_gid = nxp->ex_anon_gid;
+       exp->ex_fsid = nxp->ex_dev;
+
 
        /* Update parent pointers of all exports */
        if (parent)
@@ -300,6 +346,8 @@ exp_export(struct nfsctl_export *nxp)
        list_add(&exp->ex_hash, clp->cl_export + EXPORT_HASH(dev));
        list_add_tail(&exp->ex_list, &clp->cl_list);
 
+       exp_fsid_hash(clp, exp);
+
        err = 0;
 
        /* Unlock hashtable */
@@ -325,6 +373,9 @@ exp_do_unexport(svc_export *unexp)
        struct vfsmount *mnt;
        struct inode    *inode;
 
+       list_del(&unexp->ex_list);
+       list_del(&unexp->ex_hash);
+       exp_fsid_unhash(unexp);
        /* Update parent pointers. */
        exp_change_parents(unexp->ex_client, unexp, unexp->ex_parent);
        dentry = unexp->ex_dentry;
@@ -340,8 +391,6 @@ exp_do_unexport(svc_export *unexp)
 
 /*
  * Revoke all exports for a given client.
- * This may look very awkward, but we have to do it this way in order
- * to avoid race conditions (aka mind the parent pointer).
  */
 static void
 exp_unexport_all(svc_client *clp)
@@ -352,8 +401,6 @@ exp_unexport_all(svc_client *clp)
 
        while (!list_empty(p)) {
                svc_export *exp = list_entry(p->next, svc_export, ex_list);
-               list_del(&exp->ex_list);
-               list_del(&exp->ex_hash);
                exp_do_unexport(exp);
        }
 }
@@ -379,8 +426,6 @@ exp_unexport(struct nfsctl_export *nxp)
                kdev_t ex_dev = to_kdev_t(nxp->ex_dev);
                svc_export *exp = exp_get(clp, ex_dev, nxp->ex_ino);
                if (exp) {
-                       list_del(&exp->ex_hash);
-                       list_del(&exp->ex_list);
                        exp_do_unexport(exp);
                        err = 0;
                }
@@ -574,7 +619,7 @@ struct flags {
        { 0, {"", ""}}
 };
 
-static void exp_flags(struct seq_file *m, int flag)
+static void exp_flags(struct seq_file *m, int flag, int fsid)
 {
        int first = 0;
        struct flags *flg;
@@ -584,6 +629,8 @@ static void exp_flags(struct seq_file *m, int flag)
                if (*flg->name[state])
                        seq_printf(m, "%s%s", first++?",":"", flg->name[state]);
        }
+       if (flag & NFSEXP_FSID)
+               seq_printf(m, "%sfsid=%d", first++?",":"", fsid);
 }
 
 static inline void mangle(struct seq_file *m, const char *s)
@@ -609,7 +656,7 @@ static int e_show(struct seq_file *m, void *p)
        seq_putc(m, '\t');
        mangle(m, clp->cl_ident);
        seq_putc(m, '(');
-       exp_flags(m, exp->ex_flags);
+       exp_flags(m, exp->ex_flags, exp->ex_fsid);
        seq_puts(m, ") # ");
        for (j = 0; j < clp->cl_naddr; j++) {
                struct svc_clnthash **hp, **head, *tmp;
@@ -679,8 +726,10 @@ exp_addclient(struct nfsctl_client *ncp)
                if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
                        goto out_unlock;
                memset(clp, 0, sizeof(*clp));
-               for (i = 0; i < NFSCLNT_EXPMAX; i++)
+               for (i = 0; i < NFSCLNT_EXPMAX; i++) {
                        INIT_LIST_HEAD(&clp->cl_export[i]);
+                       INIT_LIST_HEAD(&clp->cl_expfsid[i]);
+               }
                INIT_LIST_HEAD(&clp->cl_list);
 
                dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
index 97242a262dc6c38562439fdb45277d4481c3c1ec..be06a4014039dc211cd6f3668f6cb267b060e363 100644 (file)
@@ -547,11 +547,13 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
        dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
 
        if (!fhp->fh_dentry) {
-               kdev_t xdev;
-               ino_t xino;
+               kdev_t xdev = NODEV;
+               ino_t xino = 0;
                __u32 *datap=NULL;
                int data_left = fh->fh_size/4;
                int nfsdev;
+               int fsid = 0;
+
                error = nfserr_stale;
                if (rqstp->rq_vers == 3)
                        error = nfserr_badhandle;
@@ -571,6 +573,10 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                                xdev = mk_kdev(nfsdev>>16, nfsdev&0xFFFF);
                                xino = *datap++;
                                break;
+                       case 1:
+                               if ((data_left-=1)<0) goto out;
+                               fsid = *datap++;
+                               break;
                        default:
                                goto out;
                        }
@@ -586,7 +592,10 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                 * Look up the export entry.
                 */
                error = nfserr_stale; 
-               exp = exp_get(rqstp->rq_client, xdev, xino);
+               if (fh->fh_version == 1 && fh->fh_fsid_type == 1)
+                       exp = exp_get_fsid(rqstp->rq_client, fsid);
+               else
+                       exp = exp_get(rqstp->rq_client, xdev, xino);
 
                if (!exp) {
                        /* export entry revoked */
@@ -838,12 +847,20 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st
        } else {
                fhp->fh_handle.fh_version = 1;
                fhp->fh_handle.fh_auth_type = 0;
-               fhp->fh_handle.fh_fsid_type = 0;
                datap = fhp->fh_handle.fh_auth+0;
-               /* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
-               *datap++ = htonl((major(exp->ex_dev)<<16)| minor(exp->ex_dev));
-               *datap++ = ino_t_to_u32(exp->ex_ino);
-               fhp->fh_handle.fh_size = 3*4;
+               if ((exp->ex_flags & NFSEXP_FSID) &&
+                   (!ref_fh || ref_fh->fh_handle.fh_fsid_type == 1)) {
+                       fhp->fh_handle.fh_fsid_type = 1;
+                       /* fsid_type 1 == 4 bytes filesystem id */
+                       *datap++ = exp->ex_fsid;
+                       fhp->fh_handle.fh_size = 2*4;
+               } else {
+                       fhp->fh_handle.fh_fsid_type = 0;
+                       /* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
+                       *datap++ = htonl((major(exp->ex_dev)<<16)| minor(exp->ex_dev));
+                       *datap++ = ino_t_to_u32(exp->ex_ino);
+                       fhp->fh_handle.fh_size = 3*4;
+               }
                if (inode) {
                        int size = fhp->fh_maxsize/4 - 3;
                        fhp->fh_handle.fh_fileid_type =
index 1125d2fa1cbff429bf46167e731ced1c1f1ac75f..ab7b149a3f84ab3916e0b16bb197ea25e2528b8c 100644 (file)
@@ -39,7 +39,8 @@
 #define NFSEXP_NOSUBTREECHECK  0x0400
 #define        NFSEXP_NOAUTHNLM        0x0800          /* Don't authenticate NLM requests - just trust */
 #define NFSEXP_MSNFS           0x1000  /* do silly things that MS clients expect */
-#define NFSEXP_ALLFLAGS                0x1FFF
+#define NFSEXP_FSID            0x2000
+#define NFSEXP_ALLFLAGS                0x3FFF
 
 
 #ifdef __KERNEL__
@@ -55,11 +56,13 @@ struct svc_client {
        struct in_addr          cl_addr[NFSCLNT_ADDRMAX];
        struct svc_uidmap *     cl_umap;
        struct list_head        cl_export[NFSCLNT_EXPMAX];
+       struct list_head        cl_expfsid[NFSCLNT_EXPMAX];
        struct list_head        cl_list;
 };
 
 struct svc_export {
        struct list_head        ex_hash;
+       struct list_head        ex_fsid_hash;
        struct list_head        ex_list;
        char                    ex_path[NFS_MAXPATHLEN+1];
        struct svc_export *     ex_parent;
@@ -71,6 +74,7 @@ struct svc_export {
        ino_t                   ex_ino;
        uid_t                   ex_anon_uid;
        gid_t                   ex_anon_gid;
+       int                     ex_fsid;
 };
 
 #define EX_SECURE(exp)         (!((exp)->ex_flags & NFSEXP_INSECURE_PORT))
@@ -91,6 +95,7 @@ void                  exp_readunlock(void);
 struct svc_client *    exp_getclient(struct sockaddr_in *sin);
 void                   exp_putclient(struct svc_client *clp);
 struct svc_export *    exp_get(struct svc_client *clp, kdev_t dev, ino_t ino);
+struct svc_export *    exp_get_fsid(struct svc_client *clp, int fsid);
 struct svc_export *    exp_get_by_name(struct svc_client *clp,
                                        struct vfsmount *mnt,
                                        struct dentry *dentry);