Commit 9e213524 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] prepare for 32-bit dev_t: NFS

	NFS made dev_t-agnostic.  Aside of minor fixes in debugging printks,
and adding old_encode_dev()/old_decode_dev(), the main part is in handling
of exports with large dev_t.  New fhandle format introduced, fh_verify(),
fh_compose() and exports cache taught to deal with it.  Format is used when
->s_dev of exported fs doesn't fit into 256:256; in that case we put major
and minor in separate words in fhandle; ->fh_fsid_type is set to 2.
parent 7f904771
......@@ -651,7 +651,8 @@ nfs3_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
default: return -EINVAL;
}
dprintk("NFS call mknod %s %x\n", name->name, (unsigned)rdev);
dprintk("NFS call mknod %s %u:%u\n", name->name,
MAJOR(rdev), MINOR(rdev));
dir_attr.valid = 0;
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, 0);
......
......@@ -1503,7 +1503,7 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr,
READ32(major);
READ32(minor);
nfp->rdev = MKDEV(major, minor);
dprintk("read_attrs: rdev=0x%x\n", nfp->rdev);
dprintk("read_attrs: rdev=%u:%u\n", major, minor);
}
if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
READ_BUF(8);
......
......@@ -277,7 +277,7 @@ nfs_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
sattr->ia_valid &= ~ATTR_SIZE;
} else if (S_ISCHR(mode) || S_ISBLK(mode)) {
sattr->ia_valid |= ATTR_SIZE;
sattr->ia_size = rdev; /* get out your barf bag */
sattr->ia_size = old_encode_dev(rdev);/* get out your barf bag */
}
fattr->valid = 0;
......
......@@ -55,11 +55,16 @@ static int exp_verify_string(char *cp, int max);
#define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1)
static struct cache_head *expkey_table[EXPKEY_HASHMAX];
static inline int key_len(int type)
{
return type == 0 ? 8 : type == 1 ? 4 : 12;
}
static inline int svc_expkey_hash(struct svc_expkey *item)
{
int hash = item->ek_fsidtype;
char * cp = (char*)item->ek_fsid;
int len = (item->ek_fsidtype==0)?8:4;
int len = key_len(item->ek_fsidtype);
hash ^= hash_mem(cp, len, EXPKEY_HASHBITS);
hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS);
......@@ -89,7 +94,7 @@ void expkey_request(struct cache_detail *cd,
qword_add(bpp, blen, ek->ek_client->name);
snprintf(type, 5, "%d", ek->ek_fsidtype);
qword_add(bpp, blen, type);
qword_addhex(bpp, blen, (char*)ek->ek_fsid, ek->ek_fsidtype==0?8:4);
qword_addhex(bpp, blen, (char*)ek->ek_fsid, key_len(ek->ek_fsidtype));
(*bpp)[-1] = '\n';
}
......@@ -130,12 +135,12 @@ int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
if (*ep)
goto out;
dprintk("found fsidtype %d\n", fsidtype);
if (fsidtype > 1)
if (fsidtype > 2)
goto out;
if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)
goto out;
dprintk("found fsid length %d\n", len);
if (len != ((fsidtype==0)?8:4))
if (len != key_len(fsidtype))
goto out;
/* OK, we seem to have a valid key */
......@@ -206,8 +211,10 @@ static int expkey_show(struct seq_file *m,
ek = container_of(h, struct svc_expkey, h);
seq_printf(m, "%s %d 0x%08x", ek->ek_client->name,
ek->ek_fsidtype, ek->ek_fsid[0]);
if (ek->ek_fsid == 0)
seq_printf(m, "%08x", ek->ek_fsid[0]);
if (ek->ek_fsidtype != 1)
seq_printf(m, "%08x", ek->ek_fsid[1]);
if (ek->ek_fsidtype == 2)
seq_printf(m, "%08x", ek->ek_fsid[2]);
if (test_bit(CACHE_VALID, &h->flags) &&
!test_bit(CACHE_NEGATIVE, &h->flags)) {
seq_printf(m, " ");
......@@ -230,13 +237,8 @@ struct cache_detail svc_expkey_cache = {
static inline int svc_expkey_match (struct svc_expkey *a, struct svc_expkey *b)
{
if (a->ek_fsidtype != b->ek_fsidtype ||
a->ek_client != b->ek_client)
return 0;
if (a->ek_fsid[0] != b->ek_fsid[0])
return 0;
if (a->ek_fsidtype == 1)
return 1;
if (a->ek_fsid[1] != b->ek_fsid[1])
a->ek_client != b->ek_client ||
memcmp(a->ek_fsid, b->ek_fsid, key_len(a->ek_fsidtype)) != 0)
return 0;
return 1;
}
......@@ -248,6 +250,7 @@ static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *it
new->ek_fsidtype = item->ek_fsidtype;
new->ek_fsid[0] = item->ek_fsid[0];
new->ek_fsid[1] = item->ek_fsid[1];
new->ek_fsid[2] = item->ek_fsid[2];
}
static inline void svc_expkey_update(struct svc_expkey *new, struct svc_expkey *item)
......@@ -502,8 +505,7 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
key.ek_client = clp;
key.ek_fsidtype = fsid_type;
key.ek_fsid[0] = fsidv[0];
key.ek_fsid[1] = fsidv[1];
memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
ek = svc_expkey_lookup(&key, 0);
if (ek != NULL)
......@@ -519,8 +521,7 @@ int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv,
key.ek_client = clp;
key.ek_fsidtype = fsid_type;
key.ek_fsid[0] = fsidv[0];
key.ek_fsid[1] = fsidv[1];
memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
key.ek_export = exp;
key.h.expiry_time = NEVER;
key.h.flags = 0;
......@@ -539,10 +540,14 @@ int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv,
static inline struct svc_expkey *
exp_get_key(svc_client *clp, dev_t dev, ino_t ino)
{
u32 fsidv[2];
u32 fsidv[3];
mk_fsid_v0(fsidv, dev, ino);
return exp_find_key(clp, 0, fsidv, NULL);
if (old_valid_dev(dev)) {
mk_fsid_v0(fsidv, dev, ino);
return exp_find_key(clp, 0, fsidv, NULL);
}
mk_fsid_v2(fsidv, dev, ino);
return exp_find_key(clp, 2, fsidv, NULL);
}
/*
......@@ -671,11 +676,15 @@ static int exp_fsid_hash(svc_client *clp, struct svc_export *exp)
static int exp_hash(struct auth_domain *clp, struct svc_export *exp)
{
u32 fsid[2];
struct inode *inode;
struct inode *inode = exp->ex_dentry->d_inode;
dev_t dev = inode->i_sb->s_dev;
inode = exp->ex_dentry->d_inode;
mk_fsid_v0(fsid, inode->i_sb->s_dev, inode->i_ino);
return exp_set_key(clp, 0, fsid, exp);
if (old_valid_dev(dev)) {
mk_fsid_v0(fsid, dev, inode->i_ino);
return exp_set_key(clp, 0, fsid, exp);
}
mk_fsid_v2(fsid, dev, inode->i_ino);
return exp_set_key(clp, 2, fsid, exp);
}
static void exp_unhash(struct svc_export *exp)
......@@ -819,32 +828,42 @@ int
exp_unexport(struct nfsctl_export *nxp)
{
struct auth_domain *dom;
svc_export *exp;
struct nameidata nd;
int err;
/* Consistency check */
if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
return -EINVAL;
exp_writelock();
err = -EINVAL;
dom = auth_domain_find(nxp->ex_client);
if (dom) {
struct svc_expkey *key = exp_get_key(dom, nxp->ex_dev, nxp->ex_ino);
if (key && !IS_ERR(key) && key->ek_export) {
exp_do_unexport(key->ek_export);
err = 0;
expkey_put(&key->h, &svc_expkey_cache);
} else
dprintk("nfsd: no export %x/%lx for %s\n",
(unsigned)nxp->ex_dev,
(unsigned long) nxp->ex_ino, nxp->ex_client);
auth_domain_put(dom);
cache_flush();
} else
if (!dom) {
dprintk("nfsd: unexport couldn't find %s\n", nxp->ex_client);
goto out_unlock;
}
err = path_lookup(nxp->ex_path, 0, &nd);
if (err)
goto out_domain;
err = -EINVAL;
exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL);
path_release(&nd);
if (!exp)
goto out_domain;
exp_do_unexport(exp);
exp_put(exp);
err = 0;
out_domain:
auth_domain_put(dom);
cache_flush();
out_unlock:
exp_writeunlock();
return err;
}
......
......@@ -186,12 +186,10 @@ encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
p = xdr_encode_hyper(p, ((u64)stat.blocks) << 9);
*p++ = htonl((u32) MAJOR(stat.rdev));
*p++ = htonl((u32) MINOR(stat.rdev));
if (rqstp->rq_reffh->fh_version == 1
&& rqstp->rq_reffh->fh_fsid_type == 1
&& (fhp->fh_export->ex_flags & NFSEXP_FSID))
if (is_fsid(fhp, rqstp->rq_reffh))
p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
else
p = xdr_encode_hyper(p, (u64) stat.dev);
p = xdr_encode_hyper(p, (u64) old_encode_dev(stat.dev));
p = xdr_encode_hyper(p, (u64) stat.ino);
p = encode_time3(p, &stat.atime);
lease_get_mtime(dentry->d_inode, &time);
......@@ -222,12 +220,10 @@ encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
p = xdr_encode_hyper(p, ((u64)fhp->fh_post_blocks) << 9);
*p++ = fhp->fh_post_rdev[0];
*p++ = fhp->fh_post_rdev[1];
if (rqstp->rq_reffh->fh_version == 1
&& rqstp->rq_reffh->fh_fsid_type == 1
&& (fhp->fh_export->ex_flags & NFSEXP_FSID))
if (is_fsid(fhp, rqstp->rq_reffh))
p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
else
p = xdr_encode_hyper(p, (u64) inode->i_sb->s_dev);
p = xdr_encode_hyper(p, (u64)old_encode_dev(inode->i_sb->s_dev));
p = xdr_encode_hyper(p, (u64) inode->i_ino);
p = encode_time3(p, &fhp->fh_post_atime);
p = encode_time3(p, &fhp->fh_post_mtime);
......
......@@ -125,6 +125,9 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
case 1:
len = 1;
break;
case 2:
len = 3;
break;
default:
goto out;
}
......@@ -137,7 +140,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
if (fh->fh_size != NFS_FHSIZE)
goto out;
/* assume old filehandle format */
xdev = u32_to_dev_t(fh->ofh_xdev);
xdev = old_decode_dev(fh->ofh_xdev);
xino = u32_to_ino_t(fh->ofh_xino);
mk_fsid_v0(tfh, xdev, xino);
exp = exp_find(rqstp->rq_client, 0, tfh, &rqstp->rq_chandle);
......@@ -329,12 +332,21 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st
parent->d_name.name, dentry->d_name.name,
(inode ? inode->i_ino : 0));
if (ref_fh) {
/* for large devnums rules are simple */
if (!old_valid_dev(ex_dev)) {
ref_fh_version = 1;
if (exp->ex_flags & NFSEXP_FSID)
ref_fh_fsid_type = 1;
else
ref_fh_fsid_type = 2;
} else if (ref_fh) {
ref_fh_version = ref_fh->fh_handle.fh_version;
ref_fh_fsid_type = ref_fh->fh_handle.fh_fsid_type;
if (ref_fh == fhp)
fh_put(ref_fh);
if (!(exp->ex_flags & NFSEXP_FSID) || ref_fh_fsid_type == 2)
ref_fh_fsid_type = 0;
}
if (ref_fh == fhp)
fh_put(ref_fh);
if (fhp->fh_locked || fhp->fh_dentry) {
printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
......@@ -353,7 +365,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st
memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
fhp->fh_handle.fh_size = NFS_FHSIZE;
fhp->fh_handle.ofh_dcookie = 0xfeebbaca;
fhp->fh_handle.ofh_dev = htonl((MAJOR(ex_dev)<<16)| MINOR(ex_dev));
fhp->fh_handle.ofh_dev = old_encode_dev(ex_dev);
fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_dentry->d_inode->i_ino);
fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
......@@ -363,19 +375,33 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st
fhp->fh_handle.fh_version = 1;
fhp->fh_handle.fh_auth_type = 0;
datap = fhp->fh_handle.fh_auth+0;
if ((exp->ex_flags & NFSEXP_FSID) &&
(ref_fh_fsid_type == 1)) {
fhp->fh_handle.fh_fsid_type = 1;
/* fsid_type 1 == 4 bytes filesystem id */
mk_fsid_v1(datap, exp->ex_fsid);
datap += 1;
fhp->fh_handle.fh_size = 2*4;
} else {
fhp->fh_handle.fh_fsid_type = 0;
/* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
mk_fsid_v0(datap, ex_dev, exp->ex_dentry->d_inode->i_ino);
datap += 2;
fhp->fh_handle.fh_size = 3*4;
fhp->fh_handle.fh_fsid_type = ref_fh_fsid_type;
switch (ref_fh_fsid_type) {
case 1:
/* fsid_type 1 == 4 bytes filesystem id */
mk_fsid_v1(datap, exp->ex_fsid);
datap += 1;
fhp->fh_handle.fh_size = 2*4;
break;
case 2:
/*
* fsid_type 2:
* 4byte major, 4byte minor, 4byte inode
*/
mk_fsid_v2(datap, ex_dev,
exp->ex_dentry->d_inode->i_ino);
datap += 3;
fhp->fh_handle.fh_size = 4*4;
break;
default:
/*
* fsid_type 0:
* 2byte major, 2byte minor, 4byte inode
*/
mk_fsid_v0(datap, ex_dev,
exp->ex_dentry->d_inode->i_ino);
datap += 2;
fhp->fh_handle.fh_size = 3*4;
}
if (inode) {
int size = fhp->fh_maxsize/4 - 3;
......
......@@ -184,7 +184,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
struct inode *inode;
struct dentry *dchild;
int nfserr, type, mode;
dev_t rdev = 0;
dev_t rdev = 0, wanted = old_decode_dev(attr->ia_size);
dprintk("nfsd: CREATE %s %.*s\n",
SVCFH_fmt(dirfhp), argp->len, argp->name);
......@@ -230,7 +230,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
inode = newfhp->fh_dentry->d_inode;
/* Unfudge the mode bits */
if (attr->ia_valid & ATTR_MODE) {
if (attr->ia_valid & ATTR_MODE) {
type = attr->ia_mode & S_IFMT;
mode = attr->ia_mode & ~S_IFMT;
if (!type) {
......@@ -242,7 +242,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
case S_IFCHR:
case S_IFBLK:
/* reserve rdev for later checking */
attr->ia_size = inode->i_rdev;
rdev = inode->i_rdev;
attr->ia_valid |= ATTR_SIZE;
/* FALLTHROUGH */
......@@ -277,22 +277,21 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
*/
if (type != S_IFREG) {
int is_borc = 0;
u32 size = attr->ia_size;
/* may need to change when we widen dev_t */
rdev = old_decode_dev(size);
if (type != S_IFBLK && type != S_IFCHR) {
rdev = 0;
} else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) {
/* If you think you've seen the worst, grok this. */
type = S_IFIFO;
} else if (size != rdev) {
} else if (!rdev && attr->ia_size != old_encode_dev(wanted)) {
/* dev got truncated because of 16bit Linux dev_t */
/* may need to change when we widen dev_t */
nfserr = nfserr_inval;
goto out_unlock;
} else {
/* Okay, char or block special */
is_borc = 1;
if (!rdev)
rdev = wanted;
}
/* we've used the SIZE information, so discard it */
......@@ -300,11 +299,10 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
/* Make sure the type and device matches */
nfserr = nfserr_exist;
if (inode && (type != (inode->i_mode & S_IFMT) ||
(is_borc && inode->i_rdev != rdev)))
if (inode && type != (inode->i_mode & S_IFMT))
goto out_unlock;
}
nfserr = 0;
if (!inode) {
/* File doesn't exist. Create it and set attrs */
......
......@@ -159,16 +159,14 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
}
*p++ = htonl((u32) stat.blksize);
if (S_ISCHR(type) || S_ISBLK(type))
*p++ = htonl((u32) stat.rdev);
*p++ = htonl((u32) old_encode_dev(stat.rdev));
else
*p++ = htonl(0xffffffff);
*p++ = htonl((u32) stat.blocks);
if (rqstp->rq_reffh->fh_version == 1
&& rqstp->rq_reffh->fh_fsid_type == 1
&& (fhp->fh_export->ex_flags & NFSEXP_FSID))
if (is_fsid(fhp, rqstp->rq_reffh))
*p++ = htonl((u32) fhp->fh_export->ex_fsid);
else
*p++ = htonl((u32) stat.dev);
*p++ = htonl((u32) old_encode_dev(stat.dev));
*p++ = htonl((u32) stat.ino);
*p++ = htonl((u32) stat.atime.tv_sec);
*p++ = htonl(stat.atime.tv_nsec ? stat.atime.tv_nsec / 1000 : 0);
......
......@@ -65,7 +65,7 @@ struct svc_expkey {
struct auth_domain * ek_client;
int ek_fsidtype;
u32 ek_fsid[2];
u32 ek_fsid[3];
struct svc_export * ek_export;
};
......
......@@ -15,6 +15,7 @@
#include <linux/unistd.h>
#include <linux/dirent.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/nfsd/debug.h>
#include <linux/nfsd/nfsfh.h>
......@@ -209,6 +210,17 @@ void nfsd_lockd_shutdown(void);
*/
extern struct timeval nfssvc_boot;
static inline int is_fsid(struct svc_fh *fh, struct knfsd_fh *reffh)
{
if (fh->fh_export->ex_flags & NFSEXP_FSID) {
struct vfsmount *mnt = fh->fh_export->ex_mnt;
if (!old_valid_dev(mnt->mnt_sb->s_dev) ||
(reffh->fh_version == 1 && reffh->fh_fsid_type == 1))
return 1;
}
return 0;
}
#ifdef CONFIG_NFSD_V4
......
......@@ -117,26 +117,6 @@ struct knfsd_fh {
#ifdef __KERNEL__
/*
* Conversion macros for the filehandle fields.
*
* Keep the device numbers in "backwards compatible
* format", ie the low 16 bits contain the low 8 bits
* of the 20-bit minor and the 12-bit major number.
*
* The high 16 bits contain the rest (4 bits major
* and 12 bits minor),
*/
static inline dev_t u32_to_dev_t(__u32 udev)
{
unsigned int minor, major;
minor = (udev & 0xff) | ((udev >> 8) & 0xfff00);
major = ((udev >> 8) & 0xff) | ((udev >> 20) & 0xf00);
return MKDEV(major, minor);
}
static inline __u32 ino_t_to_u32(ino_t ino)
{
return (__u32) ino;
......@@ -196,6 +176,12 @@ static inline void mk_fsid_v1(u32 *fsidv, u32 fsid)
fsidv[0] = fsid;
}
static inline void mk_fsid_v2(u32 *fsidv, dev_t dev, ino_t ino)
{
fsidv[0] = htonl(MAJOR(dev));
fsidv[1] = htonl(MINOR(dev));
fsidv[2] = ino_t_to_u32(ino);
}
/*
* Shorthand for dprintk()'s
......
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