Commit cbada281 authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] PATCH - knfsd in 2.5.6 - fsid= export option

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