diff options
| -rw-r--r-- | fs/nfsd/export.c | 109 | ||||
| -rw-r--r-- | fs/nfsd/nfsfh.c | 33 | ||||
| -rw-r--r-- | include/linux/nfsd/export.h | 7 |
3 files changed, 110 insertions, 39 deletions
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 3a18be362dc4..76d7851d1698 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -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); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 97242a262dc6..be06a4014039 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -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 = diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 1125d2fa1cbf..ab7b149a3f84 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -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); |
