Commit ecebe0fc authored by Trond Myklebust's avatar Trond Myklebust

Merge http://nfsclient.bkbits.net/linux-2.6

into fys.uio.no:/home/linux/bitkeeper/nfsclient-2.6
parents f18badd1 e143b1c8
...@@ -1415,8 +1415,8 @@ config NFS_V3 ...@@ -1415,8 +1415,8 @@ config NFS_V3
bool "Provide NFSv3 client support" bool "Provide NFSv3 client support"
depends on NFS_FS depends on NFS_FS
help help
Say Y here if you want your NFS client to be able to speak the newer Say Y here if you want your NFS client to be able to speak version
version 3 of the NFS protocol. 3 of the NFS protocol.
If unsure, say Y. If unsure, say Y.
...@@ -1560,6 +1560,22 @@ config RPCSEC_GSS_KRB5 ...@@ -1560,6 +1560,22 @@ config RPCSEC_GSS_KRB5
If unsure, say N. If unsure, say N.
config RPCSEC_GSS_SPKM3
tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL
select SUNRPC_GSS
select CRYPTO
select CRYPTO_MD5
select CRYPTO_DES
help
Provides for secure RPC calls by means of a gss-api
mechanism based on the SPKM3 public-key mechanism.
Note: Requires an auxiliary userspace daemon which may be found on
http://www.citi.umich.edu/projects/nfsv4/
If unsure, say N.
config SMB_FS config SMB_FS
tristate "SMB file system support (to mount Windows shares etc.)" tristate "SMB file system support (to mount Windows shares etc.)"
depends on INET depends on INET
......
...@@ -237,8 +237,13 @@ nlmsvc_delete_block(struct nlm_block *block, int unlock) ...@@ -237,8 +237,13 @@ nlmsvc_delete_block(struct nlm_block *block, int unlock)
/* Remove block from list */ /* Remove block from list */
nlmsvc_remove_block(block); nlmsvc_remove_block(block);
if (fl->fl_next)
posix_unblock_lock(file->f_file, fl); posix_unblock_lock(file->f_file, fl);
if (unlock) {
fl->fl_type = F_UNLCK;
posix_lock_file(file->f_file, fl);
block->b_granted = 0; block->b_granted = 0;
}
/* If the block is in the middle of a GRANT callback, /* If the block is in the middle of a GRANT callback,
* don't kill it yet. */ * don't kill it yet. */
......
...@@ -67,7 +67,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, ...@@ -67,7 +67,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
down(&nlm_file_sema); down(&nlm_file_sema);
for (file = nlm_files[hash]; file; file = file->f_next) for (file = nlm_files[hash]; file; file = file->f_next)
if (!memcmp(&file->f_handle, f, sizeof(*f))) if (!nfs_compare_fh(&file->f_handle, f))
goto found; goto found;
dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n", dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n",
......
...@@ -610,7 +610,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -610,7 +610,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
verifier = nfs_save_change_attribute(dir); verifier = nfs_save_change_attribute(dir);
error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
if (!error) { if (!error) {
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad; goto out_bad;
if (nfs_lookup_verify_inode(inode, isopen)) if (nfs_lookup_verify_inode(inode, isopen))
goto out_zap_parent; goto out_zap_parent;
...@@ -623,7 +623,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -623,7 +623,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error) if (error)
goto out_bad; goto out_bad;
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad; goto out_bad;
if ((error = nfs_refresh_inode(inode, &fattr)) != 0) if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad; goto out_bad;
...@@ -850,22 +850,22 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -850,22 +850,22 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
unsigned long verifier; unsigned long verifier;
int openflags, ret = 0; int openflags, ret = 0;
/* NFS only supports OPEN for regular files */
if (inode && !S_ISREG(inode->i_mode))
goto no_open;
parent = dget_parent(dentry); parent = dget_parent(dentry);
dir = parent->d_inode; dir = parent->d_inode;
if (!is_atomic_open(dir, nd)) if (!is_atomic_open(dir, nd))
goto no_open; goto no_open;
openflags = nd->intent.open.flags; /* We can't create new files in nfs_open_revalidate(), so we
if (openflags & O_CREAT) { * optimize away revalidation of negative dentries.
/* If this is a negative dentry, just drop it */ */
if (!inode) if (inode == NULL)
goto out; goto out;
/* If this is exclusive open, just revalidate */ /* NFS only supports OPEN on regular files */
if (openflags & O_EXCL) if (!S_ISREG(inode->i_mode))
goto no_open;
openflags = nd->intent.open.flags;
/* We cannot do exclusive creation on a positive dentry */
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
goto no_open; goto no_open;
}
/* We can't create new files, or truncate existing ones here */ /* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC); openflags &= ~(O_CREAT|O_TRUNC);
...@@ -1299,19 +1299,6 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ...@@ -1299,19 +1299,6 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
dir->i_ino, dentry->d_name.name, symname); dir->i_ino, dentry->d_name.name, symname);
error = -ENAMETOOLONG;
switch (NFS_PROTO(dir)->version) {
case 2:
if (strlen(symname) > NFS2_MAXPATHLEN)
goto out;
break;
case 3:
if (strlen(symname) > NFS3_MAXPATHLEN)
goto out;
default:
break;
}
#ifdef NFS_PARANOIA #ifdef NFS_PARANOIA
if (dentry->d_inode) if (dentry->d_inode)
printk("nfs_proc_symlink: %s/%s not negative!\n", printk("nfs_proc_symlink: %s/%s not negative!\n",
...@@ -1341,8 +1328,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -1341,8 +1328,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
d_drop(dentry); d_drop(dentry);
} }
unlock_kernel(); unlock_kernel();
out:
return error; return error;
} }
...@@ -1498,10 +1483,56 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1498,10 +1483,56 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error; return error;
} }
int static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
nfs_permission(struct inode *inode, int mask, struct nameidata *nd) {
struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
if (cache->cred != cred
|| time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
|| (NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR))
return -ENOENT;
memcpy(res, cache, sizeof(*res));
return 0;
}
static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
{
struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
if (cache->cred != set->cred) {
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = get_rpccred(set->cred);
}
cache->jiffies = set->jiffies;
cache->mask = set->mask;
}
static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
{
struct nfs_access_entry cache;
int status;
status = nfs_access_get_cached(inode, cred, &cache);
if (status == 0)
goto out;
/* Be clever: ask server to check for all possible rights */
cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
cache.cred = cred;
cache.jiffies = jiffies;
status = NFS_PROTO(inode)->access(inode, &cache);
if (status != 0)
return status;
nfs_access_add_cache(inode, &cache);
out:
if ((cache.mask & mask) == mask)
return 0;
return -EACCES;
}
int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
{ {
struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
struct rpc_cred *cred; struct rpc_cred *cred;
int mode = inode->i_mode; int mode = inode->i_mode;
int res; int res;
...@@ -1542,24 +1573,7 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1542,24 +1573,7 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
goto out_notsup; goto out_notsup;
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (cache->cred == cred res = nfs_do_access(inode, cred, mask);
&& time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
&& !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
if (!(res = cache->err)) {
/* Is the mask a subset of an accepted mask? */
if ((cache->mask & mask) == mask)
goto out;
} else {
/* ...or is it a superset of a rejected mask? */
if ((cache->mask & mask) == cache->mask)
goto out;
}
}
res = NFS_PROTO(inode)->access(inode, cred, mask);
if (!res || res == -EACCES)
goto add_cache;
out:
put_rpccred(cred); put_rpccred(cred);
unlock_kernel(); unlock_kernel();
return res; return res;
...@@ -1568,15 +1582,6 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1568,15 +1582,6 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
res = vfs_permission(inode, mask); res = vfs_permission(inode, mask);
unlock_kernel(); unlock_kernel();
return res; return res;
add_cache:
cache->jiffies = jiffies;
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = cred;
cache->mask = mask;
cache->err = res;
unlock_kernel();
return res;
} }
/* /*
......
...@@ -57,7 +57,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb); ...@@ -57,7 +57,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb);
static void nfs_destroy_inode(struct inode *); static void nfs_destroy_inode(struct inode *);
static void nfs_write_inode(struct inode *,int); static void nfs_write_inode(struct inode *,int);
static void nfs_delete_inode(struct inode *); static void nfs_delete_inode(struct inode *);
static void nfs_put_super(struct super_block *);
static void nfs_clear_inode(struct inode *); static void nfs_clear_inode(struct inode *);
static void nfs_umount_begin(struct super_block *); static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct super_block *, struct kstatfs *); static int nfs_statfs(struct super_block *, struct kstatfs *);
...@@ -68,7 +67,6 @@ static struct super_operations nfs_sops = { ...@@ -68,7 +67,6 @@ static struct super_operations nfs_sops = {
.destroy_inode = nfs_destroy_inode, .destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode, .write_inode = nfs_write_inode,
.delete_inode = nfs_delete_inode, .delete_inode = nfs_delete_inode,
.put_super = nfs_put_super,
.statfs = nfs_statfs, .statfs = nfs_statfs,
.clear_inode = nfs_clear_inode, .clear_inode = nfs_clear_inode,
.umount_begin = nfs_umount_begin, .umount_begin = nfs_umount_begin,
...@@ -151,27 +149,6 @@ nfs_clear_inode(struct inode *inode) ...@@ -151,27 +149,6 @@ nfs_clear_inode(struct inode *inode)
BUG_ON(atomic_read(&nfsi->data_updates) != 0); BUG_ON(atomic_read(&nfsi->data_updates) != 0);
} }
void
nfs_put_super(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL)
rpc_shutdown_client(server->client);
if (server->client_sys != NULL)
rpc_shutdown_client(server->client_sys);
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
kfree(server->hostname);
}
void void
nfs_umount_begin(struct super_block *sb) nfs_umount_begin(struct super_block *sb)
{ {
...@@ -293,14 +270,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) ...@@ -293,14 +270,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
server->rsize = nfs_block_size(fsinfo.rtpref, NULL); server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
if (server->wsize == 0) if (server->wsize == 0)
server->wsize = nfs_block_size(fsinfo.wtpref, NULL); server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
if (sb->s_blocksize == 0) {
if (fsinfo.wtmult == 0) {
sb->s_blocksize = 512;
sb->s_blocksize_bits = 9;
} else
sb->s_blocksize = nfs_block_bits(fsinfo.wtmult,
&sb->s_blocksize_bits);
}
if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax) if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax)
server->rsize = nfs_block_size(fsinfo.rtmax, NULL); server->rsize = nfs_block_size(fsinfo.rtmax, NULL);
...@@ -319,6 +288,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) ...@@ -319,6 +288,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
server->wsize = server->wpages << PAGE_CACHE_SHIFT; server->wsize = server->wpages << PAGE_CACHE_SHIFT;
} }
if (sb->s_blocksize == 0)
sb->s_blocksize = nfs_block_bits(server->wsize,
&sb->s_blocksize_bits);
server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL);
server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
if (server->dtsize > PAGE_CACHE_SIZE) if (server->dtsize > PAGE_CACHE_SIZE)
server->dtsize = PAGE_CACHE_SIZE; server->dtsize = PAGE_CACHE_SIZE;
...@@ -405,7 +379,6 @@ static int ...@@ -405,7 +379,6 @@ static int
nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
{ {
struct nfs_server *server; struct nfs_server *server;
int err = -EIO;
rpc_authflavor_t authflavor; rpc_authflavor_t authflavor;
server = NFS_SB(sb); server = NFS_SB(sb);
...@@ -424,10 +397,14 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) ...@@ -424,10 +397,14 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->acdirmin = data->acdirmin*HZ; server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ; server->acdirmax = data->acdirmax*HZ;
/* Start lockd here, before we might error out */
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_up();
server->namelen = data->namlen; server->namelen = data->namlen;
server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL); server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
if (!server->hostname) if (!server->hostname)
goto out_fail; return -ENOMEM;
strcpy(server->hostname, data->hostname); strcpy(server->hostname, data->hostname);
/* Check NFS protocol revision and initialize RPC op vector /* Check NFS protocol revision and initialize RPC op vector
...@@ -438,11 +415,11 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) ...@@ -438,11 +415,11 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->caps |= NFS_CAP_READDIRPLUS; server->caps |= NFS_CAP_READDIRPLUS;
if (data->version < 4) { if (data->version < 4) {
printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n"); printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
goto out_fail; return -EIO;
} }
#else #else
printk(KERN_NOTICE "NFS: NFSv3 not supported.\n"); printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
goto out_fail; return -EIO;
#endif #endif
} else { } else {
server->rpc_ops = &nfs_v2_clientops; server->rpc_ops = &nfs_v2_clientops;
...@@ -457,30 +434,19 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) ...@@ -457,30 +434,19 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
/* Create RPC client handles */ /* Create RPC client handles */
server->client = nfs_create_client(server, data); server->client = nfs_create_client(server, data);
if (IS_ERR(server->client)) if (IS_ERR(server->client))
goto out_fail; return PTR_ERR(server->client);
/* RFC 2623, sec 2.3.2 */ /* RFC 2623, sec 2.3.2 */
if (authflavor != RPC_AUTH_UNIX) { if (authflavor != RPC_AUTH_UNIX) {
server->client_sys = rpc_clone_client(server->client); server->client_sys = rpc_clone_client(server->client);
if (server->client_sys == NULL) if (IS_ERR(server->client_sys))
goto out_shutdown; return PTR_ERR(server->client_sys);
if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys)) if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys))
goto out_shutdown; return -ENOMEM;
} else { } else {
atomic_inc(&server->client->cl_count); atomic_inc(&server->client->cl_count);
server->client_sys = server->client; server->client_sys = server->client;
} }
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
goto out_shutdown;
}
sb->s_op = &nfs_sops;
err = nfs_sb_init(sb, authflavor);
if (err != 0)
goto out_noinit;
if (server->flags & NFS_MOUNT_VER3) { if (server->flags & NFS_MOUNT_VER3) {
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN; server->namelen = NFS3_MAXNAMLEN;
...@@ -489,21 +455,8 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) ...@@ -489,21 +455,8 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->namelen = NFS2_MAXNAMLEN; server->namelen = NFS2_MAXNAMLEN;
} }
/* Check whether to start the lockd process */ sb->s_op = &nfs_sops;
if (!(server->flags & NFS_MOUNT_NONLM)) return nfs_sb_init(sb, authflavor);
lockd_up();
return 0;
out_noinit:
rpciod_down();
out_shutdown:
if (server->client)
rpc_shutdown_client(server->client);
if (server->client_sys)
rpc_shutdown_client(server->client_sys);
out_fail:
if (server->hostname)
kfree(server->hostname);
return err;
} }
static int static int
...@@ -526,6 +479,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf) ...@@ -526,6 +479,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf)
if (error < 0) if (error < 0)
goto out_err; goto out_err;
buf->f_frsize = server->wtmult;
buf->f_bsize = sb->s_blocksize; buf->f_bsize = sb->s_blocksize;
blockbits = sb->s_blocksize_bits; blockbits = sb->s_blocksize_bits;
blockres = (1 << blockbits) - 1; blockres = (1 << blockbits) - 1;
...@@ -642,7 +596,7 @@ nfs_find_actor(struct inode *inode, void *opaque) ...@@ -642,7 +596,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
if (NFS_FILEID(inode) != fattr->fileid) if (NFS_FILEID(inode) != fattr->fileid)
return 0; return 0;
if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0) if (nfs_compare_fh(NFS_FH(inode), fh))
return 0; return 0;
if (is_bad_inode(inode)) if (is_bad_inode(inode))
return 0; return 0;
...@@ -653,11 +607,10 @@ static int ...@@ -653,11 +607,10 @@ static int
nfs_init_locked(struct inode *inode, void *opaque) nfs_init_locked(struct inode *inode, void *opaque)
{ {
struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque; struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
struct nfs_fh *fh = desc->fh;
struct nfs_fattr *fattr = desc->fattr; struct nfs_fattr *fattr = desc->fattr;
NFS_FILEID(inode) = fattr->fileid; NFS_FILEID(inode) = fattr->fileid;
memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh)); nfs_copy_fh(NFS_FH(inode), desc->fh);
return 0; return 0;
} }
...@@ -1305,7 +1258,7 @@ static int nfs_compare_super(struct super_block *sb, void *data) ...@@ -1305,7 +1258,7 @@ static int nfs_compare_super(struct super_block *sb, void *data)
return 0; return 0;
if (old->addr.sin_port != server->addr.sin_port) if (old->addr.sin_port != server->addr.sin_port)
return 0; return 0;
return !memcmp(&old->fh, &server->fh, sizeof(struct nfs_fh)); return !nfs_compare_fh(&old->fh, &server->fh);
} }
static struct super_block *nfs_get_sb(struct file_system_type *fs_type, static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
...@@ -1330,9 +1283,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, ...@@ -1330,9 +1283,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
init_nfsv4_state(server); init_nfsv4_state(server);
root = &server->fh; root = &server->fh;
memcpy(root, &data->root, sizeof(*root)); nfs_copy_fh(root, (struct nfs_fh *) &data->root);
if (root->size < sizeof(root->data))
memset(root->data+root->size, 0, sizeof(root->data)-root->size);
if (data->version != NFS_MOUNT_VERSION) { if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n", printk("nfs warning: mount version %s than kernel\n",
...@@ -1343,7 +1294,6 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, ...@@ -1343,7 +1294,6 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
data->bsize = 0; data->bsize = 0;
if (data->version < 4) { if (data->version < 4) {
data->flags &= ~NFS_MOUNT_VER3; data->flags &= ~NFS_MOUNT_VER3;
memset(root, 0, sizeof(*root));
root->size = NFS2_FHSIZE; root->size = NFS2_FHSIZE;
memcpy(root->data, data->old_root.data, NFS2_FHSIZE); memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
} }
...@@ -1373,6 +1323,13 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, ...@@ -1373,6 +1323,13 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
s->s_flags = flags; s->s_flags = flags;
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
kfree(server);
return ERR_PTR(-EIO);
}
error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) { if (error) {
up_write(&s->s_umount); up_write(&s->s_umount);
...@@ -1386,7 +1343,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, ...@@ -1386,7 +1343,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
static void nfs_kill_super(struct super_block *s) static void nfs_kill_super(struct super_block *s)
{ {
struct nfs_server *server = NFS_SB(s); struct nfs_server *server = NFS_SB(s);
kill_anon_super(s); kill_anon_super(s);
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL && !IS_ERR(server->client))
rpc_shutdown_client(server->client);
if (server->client_sys != NULL && !IS_ERR(server->client_sys))
rpc_shutdown_client(server->client_sys);
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
if (server->hostname != NULL)
kfree(server->hostname);
kfree(server); kfree(server);
} }
...@@ -1407,7 +1382,6 @@ static struct super_operations nfs4_sops = { ...@@ -1407,7 +1382,6 @@ static struct super_operations nfs4_sops = {
.destroy_inode = nfs_destroy_inode, .destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode, .write_inode = nfs_write_inode,
.delete_inode = nfs_delete_inode, .delete_inode = nfs_delete_inode,
.put_super = nfs_put_super,
.statfs = nfs_statfs, .statfs = nfs_statfs,
.clear_inode = nfs4_clear_inode, .clear_inode = nfs4_clear_inode,
.umount_begin = nfs_umount_begin, .umount_begin = nfs_umount_begin,
...@@ -1498,7 +1472,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, ...@@ -1498,7 +1472,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
clp = nfs4_get_client(&server->addr.sin_addr); clp = nfs4_get_client(&server->addr.sin_addr);
if (!clp) { if (!clp) {
printk(KERN_WARNING "NFS: failed to create NFS4 client.\n"); printk(KERN_WARNING "NFS: failed to create NFS4 client.\n");
goto out_fail; return -EIO;
} }
/* Now create transport and client */ /* Now create transport and client */
...@@ -1547,45 +1521,29 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, ...@@ -1547,45 +1521,29 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
if (IS_ERR(clnt)) { if (IS_ERR(clnt)) {
printk(KERN_WARNING "NFS: cannot create RPC client.\n"); printk(KERN_WARNING "NFS: cannot create RPC client.\n");
err = PTR_ERR(clnt); return PTR_ERR(clnt);
goto out_remove_list;
} }
clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0;
clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0;
server->client = clnt; server->client = clnt;
err = -ENOMEM;
if (server->nfs4_state->cl_idmap == NULL) { if (server->nfs4_state->cl_idmap == NULL) {
printk(KERN_WARNING "NFS: failed to create idmapper.\n"); printk(KERN_WARNING "NFS: failed to create idmapper.\n");
goto out_shutdown; return -ENOMEM;
} }
if (clnt->cl_auth->au_flavor != authflavour) { if (clnt->cl_auth->au_flavor != authflavour) {
if (rpcauth_create(authflavour, clnt) == NULL) { if (rpcauth_create(authflavour, clnt) == NULL) {
printk(KERN_WARNING "NFS: couldn't create credcache!\n"); printk(KERN_WARNING "NFS: couldn't create credcache!\n");
goto out_shutdown; return -ENOMEM;
}
} }
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
goto out_shutdown;
} }
sb->s_op = &nfs4_sops; sb->s_op = &nfs4_sops;
err = nfs_sb_init(sb, authflavour); err = nfs_sb_init(sb, authflavour);
if (err == 0) if (err == 0)
return 0; return 0;
rpciod_down();
out_shutdown:
rpc_shutdown_client(server->client);
out_remove_list:
down_write(&server->nfs4_state->cl_sem);
list_del_init(&server->nfs4_siblings);
up_write(&server->nfs4_state->cl_sem);
destroy_nfsv4_state(server);
out_fail: out_fail:
if (clp) if (clp)
nfs4_put_client(clp); nfs4_put_client(clp);
...@@ -1691,6 +1649,13 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, ...@@ -1691,6 +1649,13 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
s->s_flags = flags; s->s_flags = flags;
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
s = ERR_PTR(-EIO);
goto out_free;
}
error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) { if (error) {
up_write(&s->s_umount); up_write(&s->s_umount);
...@@ -1764,6 +1729,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) ...@@ -1764,6 +1729,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) { SLAB_CTOR_CONSTRUCTOR) {
inode_init_once(&nfsi->vfs_inode); inode_init_once(&nfsi->vfs_inode);
spin_lock_init(&nfsi->req_lock);
INIT_LIST_HEAD(&nfsi->dirty); INIT_LIST_HEAD(&nfsi->dirty);
INIT_LIST_HEAD(&nfsi->commit); INIT_LIST_HEAD(&nfsi->commit);
INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
......
...@@ -108,7 +108,6 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res) ...@@ -108,7 +108,6 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{ {
struct nfs_fh *fh = res->fh; struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) { if ((res->status = ntohl(*p++)) == 0) {
fh->size = NFS2_FHSIZE; fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE); memcpy(fh->data, p, NFS2_FHSIZE);
...@@ -121,7 +120,6 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res) ...@@ -121,7 +120,6 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{ {
struct nfs_fh *fh = res->fh; struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) { if ((res->status = ntohl(*p++)) == 0) {
int size = ntohl(*p++); int size = ntohl(*p++);
if (size <= NFS3_FHSIZE) { if (size <= NFS3_FHSIZE) {
......
...@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle) ...@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
static inline u32 * static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle) xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
{ {
/* Zero handle first to allow comparisons */
memset(fhandle, 0, sizeof(*fhandle));
/* NFSv2 handles have a fixed length */ /* NFSv2 handles have a fixed length */
fhandle->size = NFS2_FHSIZE; fhandle->size = NFS2_FHSIZE;
memcpy(fhandle->data, p, NFS2_FHSIZE); memcpy(fhandle->data, p, NFS2_FHSIZE);
...@@ -94,6 +92,23 @@ xdr_encode_time(u32 *p, struct timespec *timep) ...@@ -94,6 +92,23 @@ xdr_encode_time(u32 *p, struct timespec *timep)
return p; return p;
} }
static inline u32*
xdr_encode_current_server_time(u32 *p, struct timespec *timep)
{
/*
* Passing the invalid value useconds=1000000 is a
* Sun convention for "set to current server time".
* It's needed to make permissions checks for the
* "touch" program across v2 mounts to Solaris and
* Irix boxes work correctly. See description of
* sattr in section 6.1 of "NFS Illustrated" by
* Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
*/
*p++ = htonl(timep->tv_sec);
*p++ = htonl(1000000);
return p;
}
static inline u32* static inline u32*
xdr_decode_time(u32 *p, struct timespec *timep) xdr_decode_time(u32 *p, struct timespec *timep)
{ {
...@@ -142,15 +157,19 @@ xdr_encode_sattr(u32 *p, struct iattr *attr) ...@@ -142,15 +157,19 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
SATTR(p, attr, ATTR_GID, ia_gid); SATTR(p, attr, ATTR_GID, ia_gid);
SATTR(p, attr, ATTR_SIZE, ia_size); SATTR(p, attr, ATTR_SIZE, ia_size);
if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) { if (attr->ia_valid & ATTR_ATIME_SET) {
p = xdr_encode_time(p, &attr->ia_atime); p = xdr_encode_time(p, &attr->ia_atime);
} else if (attr->ia_valid & ATTR_ATIME) {
p = xdr_encode_current_server_time(p, &attr->ia_atime);
} else { } else {
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
} }
if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) { if (attr->ia_valid & ATTR_MTIME_SET) {
p = xdr_encode_time(p, &attr->ia_mtime); p = xdr_encode_time(p, &attr->ia_mtime);
} else if (attr->ia_valid & ATTR_MTIME) {
p = xdr_encode_current_server_time(p, &attr->ia_mtime);
} else { } else {
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
......
...@@ -164,8 +164,7 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name, ...@@ -164,8 +164,7 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
return status; return status;
} }
static int static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
{ {
struct nfs_fattr fattr; struct nfs_fattr fattr;
struct nfs3_accessargs arg = { struct nfs3_accessargs arg = {
...@@ -178,8 +177,9 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) ...@@ -178,8 +177,9 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
.rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS], .rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS],
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_resp = &res, .rpc_resp = &res,
.rpc_cred = cred .rpc_cred = entry->cred
}; };
int mode = entry->mask;
int status; int status;
dprintk("NFS call access\n"); dprintk("NFS call access\n");
...@@ -200,10 +200,16 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) ...@@ -200,10 +200,16 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
} }
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, &fattr); nfs_refresh_inode(inode, &fattr);
dprintk("NFS reply access\n"); if (status == 0) {
entry->mask = 0;
if (status == 0 && (arg.access & res.access) != arg.access) if (res.access & NFS3_ACCESS_READ)
status = -EACCES; entry->mask |= MAY_READ;
if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
entry->mask |= MAY_WRITE;
if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
dprintk("NFS reply access, status = %d\n", status);
return status; return status;
} }
...@@ -534,6 +540,8 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, ...@@ -534,6 +540,8 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
}; };
int status; int status;
if (path->len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name); dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
dir_attr.valid = 0; dir_attr.valid = 0;
fattr->valid = 0; fattr->valid = 0;
......
...@@ -109,10 +109,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh) ...@@ -109,10 +109,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
static inline u32 * static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fh) xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
{ {
/*
* Zero all nonused bytes
*/
memset((u8 *)fh, 0, sizeof(*fh));
if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) { if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
memcpy(fh->data, p, fh->size); memcpy(fh->data, p, fh->size);
return p + XDR_QUADLEN(fh->size); return p + XDR_QUADLEN(fh->size);
......
...@@ -734,9 +734,8 @@ static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, ...@@ -734,9 +734,8 @@ static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
return nfs4_map_errors(status); return nfs4_map_errors(status);
} }
static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{ {
int status;
struct nfs4_accessargs args = { struct nfs4_accessargs args = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
}; };
...@@ -745,8 +744,10 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode ...@@ -745,8 +744,10 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
.rpc_argp = &args, .rpc_argp = &args,
.rpc_resp = &res, .rpc_resp = &res,
.rpc_cred = cred, .rpc_cred = entry->cred,
}; };
int mode = entry->mask;
int status;
/* /*
* Determine which access bits we want to ask for... * Determine which access bits we want to ask for...
...@@ -758,8 +759,7 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode ...@@ -758,8 +759,7 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE; args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE;
if (mode & MAY_EXEC) if (mode & MAY_EXEC)
args.access |= NFS4_ACCESS_LOOKUP; args.access |= NFS4_ACCESS_LOOKUP;
} } else {
else {
if (mode & MAY_WRITE) if (mode & MAY_WRITE)
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND; args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND;
if (mode & MAY_EXEC) if (mode & MAY_EXEC)
...@@ -767,11 +767,13 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode ...@@ -767,11 +767,13 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
} }
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (!status) { if (!status) {
if (args.access != res.supported) { entry->mask = 0;
printk(KERN_NOTICE "NFS: server didn't support all access bits!\n"); if (res.access & NFS4_ACCESS_READ)
status = -ENOTSUPP; entry->mask |= MAY_READ;
} else if ((args.access & res.access) != args.access) if (res.access & (NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
status = -EACCES; entry->mask |= MAY_WRITE;
if (res.access & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
} }
return nfs4_map_errors(status); return nfs4_map_errors(status);
} }
...@@ -1090,12 +1092,14 @@ static int nfs4_proc_symlink(struct inode *dir, struct qstr *name, ...@@ -1090,12 +1092,14 @@ static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
.fattr = fattr, .fattr = fattr,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK],
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_resp = &res, .rpc_resp = &res,
}; };
int status; int status;
if (path->len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG;
arg.u.symlink = path; arg.u.symlink = path;
fattr->valid = 0; fattr->valid = 0;
......
...@@ -84,6 +84,7 @@ static int nfs_stat_to_errno(int); ...@@ -84,6 +84,7 @@ static int nfs_stat_to_errno(int);
((3+NFS4_FHSIZE) >> 2)) ((3+NFS4_FHSIZE) >> 2))
#define encode_getattr_maxsz (op_encode_hdr_maxsz + 3) #define encode_getattr_maxsz (op_encode_hdr_maxsz + 3)
#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2)) #define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
#define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz) #define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz)
#define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \ #define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \
nfs4_fattr_bitmap_maxsz) nfs4_fattr_bitmap_maxsz)
...@@ -118,8 +119,13 @@ static int nfs_stat_to_errno(int); ...@@ -118,8 +119,13 @@ static int nfs_stat_to_errno(int);
#define encode_link_maxsz (op_encode_hdr_maxsz + \ #define encode_link_maxsz (op_encode_hdr_maxsz + \
nfs4_name_maxsz) nfs4_name_maxsz)
#define decode_link_maxsz (op_decode_hdr_maxsz + 5) #define decode_link_maxsz (op_decode_hdr_maxsz + 5)
#define encode_symlink_maxsz (op_encode_hdr_maxsz + \
1 + nfs4_name_maxsz + \
nfs4_path_maxsz + \
nfs4_fattr_bitmap_maxsz)
#define decode_symlink_maxsz (op_decode_hdr_maxsz + 8)
#define encode_create_maxsz (op_encode_hdr_maxsz + \ #define encode_create_maxsz (op_encode_hdr_maxsz + \
2 + 2 * nfs4_name_maxsz + \ 2 + nfs4_name_maxsz + \
nfs4_fattr_bitmap_maxsz) nfs4_fattr_bitmap_maxsz)
#define decode_create_maxsz (op_decode_hdr_maxsz + 8) #define decode_create_maxsz (op_decode_hdr_maxsz + 8)
#define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */
...@@ -313,6 +319,16 @@ static int nfs_stat_to_errno(int); ...@@ -313,6 +319,16 @@ static int nfs_stat_to_errno(int);
decode_savefh_maxsz + \ decode_savefh_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_link_maxsz) decode_link_maxsz)
#define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_symlink_maxsz + \
encode_getattr_maxsz + \
encode_getfh_maxsz)
#define NFS4_dec_symlink_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
decode_symlink_maxsz + \
decode_getattr_maxsz + \
decode_getfh_maxsz)
#define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_create_maxsz + \ encode_create_maxsz + \
...@@ -927,7 +943,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg ...@@ -927,7 +943,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
WRITE32(OP_READDIR); WRITE32(OP_READDIR);
WRITE64(readdir->cookie); WRITE64(readdir->cookie);
WRITEMEM(readdir->verifier.data, sizeof(readdir->verifier.data)); WRITEMEM(readdir->verifier.data, sizeof(readdir->verifier.data));
WRITE32(readdir->count >> 5); /* meaningless "dircount" field */ WRITE32(readdir->count >> 1); /* We're not doing readdirplus */
WRITE32(readdir->count); WRITE32(readdir->count);
WRITE32(2); WRITE32(2);
WRITE32(FATTR4_WORD0_FILEID); WRITE32(FATTR4_WORD0_FILEID);
...@@ -1243,6 +1259,14 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, uint32_t *p, const struct n ...@@ -1243,6 +1259,14 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, uint32_t *p, const struct n
return status; return status;
} }
/*
* Encode SYMLINK request
*/
static int nfs4_xdr_enc_symlink(struct rpc_rqst *req, uint32_t *p, const struct nfs4_create_arg *args)
{
return nfs4_xdr_enc_create(req, p, args);
}
/* /*
* Encode GETATTR request * Encode GETATTR request
*/ */
...@@ -2817,8 +2841,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n ...@@ -2817,8 +2841,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
struct kvec *iov = rcvbuf->head; struct kvec *iov = rcvbuf->head;
unsigned int nr, pglen = rcvbuf->page_len; unsigned int nr, pglen = rcvbuf->page_len;
uint32_t *end, *entry, *p, *kaddr; uint32_t *end, *entry, *p, *kaddr;
uint32_t len, attrlen, word; uint32_t len, attrlen;
int i, hdrlen, recvd, status; int hdrlen, recvd, status;
status = decode_op_hdr(xdr, OP_READDIR); status = decode_op_hdr(xdr, OP_READDIR);
if (status) if (status)
...@@ -2845,36 +2869,18 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n ...@@ -2845,36 +2869,18 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len); printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len);
goto err_unmap; goto err_unmap;
} }
p += XDR_QUADLEN(len); p += XDR_QUADLEN(len);
if (p + 1 > end) if (p + 1 > end)
goto short_pkt; goto short_pkt;
len = ntohl(*p++); /* bitmap length */ len = ntohl(*p++); /* bitmap length */
if (len > 10) { p += len;
printk(KERN_WARNING "NFS: giant bitmap in readdir (len 0x%x)\n", len);
goto err_unmap;
}
if (p + len + 1 > end)
goto short_pkt;
attrlen = 0;
for (i = 0; i < len; i++) {
word = ntohl(*p++);
if (!word)
continue;
else if (i == 0 && word == FATTR4_WORD0_FILEID) {
attrlen = 8;
continue;
}
printk(KERN_WARNING "NFS: unexpected bitmap word in readdir (0x%x)\n", word);
goto err_unmap;
}
if (ntohl(*p++) != attrlen) {
printk(KERN_WARNING "NFS: unexpected attrlen in readdir\n");
goto err_unmap;
}
p += XDR_QUADLEN(attrlen);
if (p + 1 > end) if (p + 1 > end)
goto short_pkt; goto short_pkt;
attrlen = XDR_QUADLEN(ntohl(*p++));
p += attrlen; /* attributes */
if (p + 2 > end)
goto short_pkt;
entry = p;
} }
if (!nr && (entry[0] != 0 || entry[1] == 0)) if (!nr && (entry[0] != 0 || entry[1] == 0))
goto short_pkt; goto short_pkt;
...@@ -3221,6 +3227,14 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_ ...@@ -3221,6 +3227,14 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_
return status; return status;
} }
/*
* Decode SYMLINK response
*/
static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res)
{
return nfs4_xdr_dec_create(rqstp, p, res);
}
/* /*
* Decode GETATTR response * Decode GETATTR response
*/ */
...@@ -3667,6 +3681,7 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s ...@@ -3667,6 +3681,7 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s
uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
{ {
uint32_t bitmap[1] = {0};
uint32_t len; uint32_t len;
if (!*p++) { if (!*p++) {
...@@ -3690,10 +3705,16 @@ uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) ...@@ -3690,10 +3705,16 @@ uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
entry->ino = 1; entry->ino = 1;
len = ntohl(*p++); /* bitmap length */ len = ntohl(*p++); /* bitmap length */
if (len > 0) {
bitmap[0] = ntohl(*p);
p += len; p += len;
len = ntohl(*p++); /* attribute buffer length */ }
if (len) len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */
p = xdr_decode_hyper(p, &entry->ino); if (len > 0) {
if (bitmap[0] == FATTR4_WORD0_FILEID)
xdr_decode_hyper(p, &entry->ino);
p += len;
}
entry->eof = !p[0] && p[1]; entry->eof = !p[0] && p[1];
return p; return p;
...@@ -3756,7 +3777,7 @@ nfs_stat_to_errno(int stat) ...@@ -3756,7 +3777,7 @@ nfs_stat_to_errno(int stat)
if (nfs_errtbl[i].stat == stat) if (nfs_errtbl[i].stat == stat)
return nfs_errtbl[i].errno; return nfs_errtbl[i].errno;
} }
if (stat < 0) { if (stat <= 10000 || stat > 10100) {
/* The server is looney tunes. */ /* The server is looney tunes. */
return ESERVERFAULT; return ESERVERFAULT;
} }
...@@ -3804,6 +3825,7 @@ struct rpc_procinfo nfs4_procedures[] = { ...@@ -3804,6 +3825,7 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(REMOVE, enc_remove, dec_remove), PROC(REMOVE, enc_remove, dec_remove),
PROC(RENAME, enc_rename, dec_rename), PROC(RENAME, enc_rename, dec_rename),
PROC(LINK, enc_link, dec_link), PROC(LINK, enc_link, dec_link),
PROC(SYMLINK, enc_symlink, dec_symlink),
PROC(CREATE, enc_create, dec_create), PROC(CREATE, enc_create, dec_create),
PROC(PATHCONF, enc_pathconf, dec_pathconf), PROC(PATHCONF, enc_pathconf, dec_pathconf),
PROC(STATFS, enc_statfs, dec_statfs), PROC(STATFS, enc_statfs, dec_statfs),
......
...@@ -495,10 +495,8 @@ static int __init root_nfs_get_handle(void) ...@@ -495,10 +495,8 @@ static int __init root_nfs_get_handle(void)
if (status < 0) if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d " printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path); "while mounting %s\n", status, nfs_path);
else { else
nfs_data.root.size = fh.size; nfs_copy_fh(nfs_data.root, fh);
memcpy(nfs_data.root.data, fh.data, fh.size);
}
return status; return status;
} }
......
...@@ -21,11 +21,6 @@ ...@@ -21,11 +21,6 @@
#define NFS_PARANOIA 1 #define NFS_PARANOIA 1
/*
* Spinlock
*/
spinlock_t nfs_wreq_lock = SPIN_LOCK_UNLOCKED;
static kmem_cache_t *nfs_page_cachep; static kmem_cache_t *nfs_page_cachep;
static inline struct nfs_page * static inline struct nfs_page *
...@@ -95,7 +90,7 @@ nfs_create_request(struct file *file, struct inode *inode, ...@@ -95,7 +90,7 @@ nfs_create_request(struct file *file, struct inode *inode,
req->wb_pgbase = offset; req->wb_pgbase = offset;
req->wb_bytes = count; req->wb_bytes = count;
req->wb_inode = inode; req->wb_inode = inode;
req->wb_count = 1; atomic_set(&req->wb_count, 1);
server->rpc_ops->request_init(req, file); server->rpc_ops->request_init(req, file);
return req; return req;
...@@ -137,12 +132,8 @@ void nfs_clear_request(struct nfs_page *req) ...@@ -137,12 +132,8 @@ void nfs_clear_request(struct nfs_page *req)
void void
nfs_release_request(struct nfs_page *req) nfs_release_request(struct nfs_page *req)
{ {
spin_lock(&nfs_wreq_lock); if (!atomic_dec_and_test(&req->wb_count))
if (--req->wb_count) {
spin_unlock(&nfs_wreq_lock);
return; return;
}
spin_unlock(&nfs_wreq_lock);
#ifdef NFS_PARANOIA #ifdef NFS_PARANOIA
BUG_ON (!list_empty(&req->wb_list)); BUG_ON (!list_empty(&req->wb_list));
...@@ -254,7 +245,7 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, ...@@ -254,7 +245,7 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
* If the number of requests is set to 0, the entire address_space * If the number of requests is set to 0, the entire address_space
* starting at index idx_start, is scanned. * starting at index idx_start, is scanned.
* The requests are *not* checked to ensure that they form a contiguous set. * The requests are *not* checked to ensure that they form a contiguous set.
* You must be holding the nfs_wreq_lock when calling this function * You must be holding the inode's req_lock when calling this function
*/ */
int int
nfs_scan_list(struct list_head *head, struct list_head *dst, nfs_scan_list(struct list_head *head, struct list_head *dst,
......
...@@ -400,6 +400,8 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, ...@@ -400,6 +400,8 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
}; };
int status; int status;
if (path->len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name); dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
fattr->valid = 0; fattr->valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0); status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
......
...@@ -389,7 +389,7 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req) ...@@ -389,7 +389,7 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
nfs_begin_data_update(inode); nfs_begin_data_update(inode);
} }
nfsi->npages++; nfsi->npages++;
req->wb_count++; atomic_inc(&req->wb_count);
return 0; return 0;
} }
...@@ -399,21 +399,20 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req) ...@@ -399,21 +399,20 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
static void static void
nfs_inode_remove_request(struct nfs_page *req) nfs_inode_remove_request(struct nfs_page *req)
{ {
struct nfs_inode *nfsi; struct inode *inode = req->wb_inode;
struct inode *inode; struct nfs_inode *nfsi = NFS_I(inode);
BUG_ON (!NFS_WBACK_BUSY(req)); BUG_ON (!NFS_WBACK_BUSY(req));
spin_lock(&nfs_wreq_lock);
inode = req->wb_inode; spin_lock(&nfsi->req_lock);
nfsi = NFS_I(inode);
radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
nfsi->npages--; nfsi->npages--;
if (!nfsi->npages) { if (!nfsi->npages) {
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
nfs_end_data_update_defer(inode); nfs_end_data_update_defer(inode);
iput(inode); iput(inode);
} else } else
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
nfs_clear_request(req); nfs_clear_request(req);
nfs_release_request(req); nfs_release_request(req);
} }
...@@ -429,7 +428,7 @@ _nfs_find_request(struct inode *inode, unsigned long index) ...@@ -429,7 +428,7 @@ _nfs_find_request(struct inode *inode, unsigned long index)
req = (struct nfs_page*)radix_tree_lookup(&nfsi->nfs_page_tree, index); req = (struct nfs_page*)radix_tree_lookup(&nfsi->nfs_page_tree, index);
if (req) if (req)
req->wb_count++; atomic_inc(&req->wb_count);
return req; return req;
} }
...@@ -437,10 +436,11 @@ static struct nfs_page * ...@@ -437,10 +436,11 @@ static struct nfs_page *
nfs_find_request(struct inode *inode, unsigned long index) nfs_find_request(struct inode *inode, unsigned long index)
{ {
struct nfs_page *req; struct nfs_page *req;
struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, index); req = _nfs_find_request(inode, index);
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
return req; return req;
} }
...@@ -453,10 +453,10 @@ nfs_mark_request_dirty(struct nfs_page *req) ...@@ -453,10 +453,10 @@ nfs_mark_request_dirty(struct nfs_page *req)
struct inode *inode = req->wb_inode; struct inode *inode = req->wb_inode;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->dirty); nfs_list_add_request(req, &nfsi->dirty);
nfsi->ndirty++; nfsi->ndirty++;
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
inc_page_state(nr_dirty); inc_page_state(nr_dirty);
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
...@@ -481,10 +481,10 @@ nfs_mark_request_commit(struct nfs_page *req) ...@@ -481,10 +481,10 @@ nfs_mark_request_commit(struct nfs_page *req)
struct inode *inode = req->wb_inode; struct inode *inode = req->wb_inode;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->commit); nfs_list_add_request(req, &nfsi->commit);
nfsi->ncommit++; nfsi->ncommit++;
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
inc_page_state(nr_unstable); inc_page_state(nr_unstable);
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
...@@ -509,7 +509,7 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int ...@@ -509,7 +509,7 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int
else else
idx_end = idx_start + npages - 1; idx_end = idx_start + npages - 1;
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
next = idx_start; next = idx_start;
while (radix_tree_gang_lookup(&nfsi->nfs_page_tree, (void **)&req, next, 1)) { while (radix_tree_gang_lookup(&nfsi->nfs_page_tree, (void **)&req, next, 1)) {
if (req->wb_index > idx_end) if (req->wb_index > idx_end)
...@@ -519,16 +519,16 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int ...@@ -519,16 +519,16 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int
if (!NFS_WBACK_BUSY(req)) if (!NFS_WBACK_BUSY(req))
continue; continue;
req->wb_count++; atomic_inc(&req->wb_count);
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req); error = nfs_wait_on_request(req);
nfs_release_request(req); nfs_release_request(req);
if (error < 0) if (error < 0)
return error; return error;
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
res++; res++;
} }
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
return res; return res;
} }
...@@ -624,6 +624,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, ...@@ -624,6 +624,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
unsigned int offset, unsigned int bytes) unsigned int offset, unsigned int bytes)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req, *new = NULL; struct nfs_page *req, *new = NULL;
unsigned long rqend, end; unsigned long rqend, end;
...@@ -635,19 +636,19 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, ...@@ -635,19 +636,19 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
/* Loop over all inode entries and see if we find /* Loop over all inode entries and see if we find
* A request for the page we wish to update * A request for the page we wish to update
*/ */
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, page->index); req = _nfs_find_request(inode, page->index);
if (req) { if (req) {
if (!nfs_lock_request_dontget(req)) { if (!nfs_lock_request_dontget(req)) {
int error; int error;
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req); error = nfs_wait_on_request(req);
nfs_release_request(req); nfs_release_request(req);
if (error < 0) if (error < 0)
return ERR_PTR(error); return ERR_PTR(error);
continue; continue;
} }
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
if (new) if (new)
nfs_release_request(new); nfs_release_request(new);
break; break;
...@@ -658,15 +659,15 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, ...@@ -658,15 +659,15 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
nfs_lock_request_dontget(new); nfs_lock_request_dontget(new);
error = nfs_inode_add_request(inode, new); error = nfs_inode_add_request(inode, new);
if (error) { if (error) {
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
nfs_unlock_request(new); nfs_unlock_request(new);
return ERR_PTR(error); return ERR_PTR(error);
} }
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
nfs_mark_request_dirty(new); nfs_mark_request_dirty(new);
return new; return new;
} }
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
new = nfs_create_request(file, inode, page, offset, bytes); new = nfs_create_request(file, inode, page, offset, bytes);
if (IS_ERR(new)) if (IS_ERR(new))
...@@ -1347,13 +1348,14 @@ nfs_commit_done(struct rpc_task *task) ...@@ -1347,13 +1348,14 @@ nfs_commit_done(struct rpc_task *task)
int nfs_flush_inode(struct inode *inode, unsigned long idx_start, int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how) unsigned int npages, int how)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head); LIST_HEAD(head);
int res, int res,
error = 0; error = 0;
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
res = nfs_scan_dirty(inode, &head, idx_start, npages); res = nfs_scan_dirty(inode, &head, idx_start, npages);
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
if (res) if (res)
error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how); error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
if (error < 0) if (error < 0)
...@@ -1365,18 +1367,19 @@ int nfs_flush_inode(struct inode *inode, unsigned long idx_start, ...@@ -1365,18 +1367,19 @@ int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
int nfs_commit_inode(struct inode *inode, unsigned long idx_start, int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how) unsigned int npages, int how)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head); LIST_HEAD(head);
int res, int res,
error = 0; error = 0;
spin_lock(&nfs_wreq_lock); spin_lock(&nfsi->req_lock);
res = nfs_scan_commit(inode, &head, idx_start, npages); res = nfs_scan_commit(inode, &head, idx_start, npages);
if (res) { if (res) {
res += nfs_scan_commit(inode, &head, 0, 0); res += nfs_scan_commit(inode, &head, 0, 0);
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
error = nfs_commit_list(&head, how); error = nfs_commit_list(&head, how);
} else } else
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfsi->req_lock);
if (error < 0) if (error < 0)
return error; return error;
return res; return res;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define _LINUX_NFS_H #define _LINUX_NFS_H
#include <linux/sunrpc/msg_prot.h> #include <linux/sunrpc/msg_prot.h>
#include <linux/string.h>
#define NFS_PROGRAM 100003 #define NFS_PROGRAM 100003
#define NFS_PORT 2049 #define NFS_PORT 2049
...@@ -138,6 +139,22 @@ struct nfs_fh { ...@@ -138,6 +139,22 @@ struct nfs_fh {
unsigned char data[NFS_MAXFHSIZE]; unsigned char data[NFS_MAXFHSIZE];
}; };
/*
* Returns a zero iff the size and data fields match.
* Checks only "size" bytes in the data field.
*/
static inline int nfs_compare_fh(const struct nfs_fh *a, const struct nfs_fh *b)
{
return a->size != b->size || memcmp(a->data, b->data, a->size) != 0;
}
static inline void nfs_copy_fh(struct nfs_fh *target, const struct nfs_fh *source)
{
target->size = source->size;
memcpy(target->data, source->data, source->size);
}
/* /*
* This is really a general kernel constant, but since nothing like * This is really a general kernel constant, but since nothing like
* this is defined in the kernel headers, I have to do it here. * this is defined in the kernel headers, I have to do it here.
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#define NFS4_VERIFIER_SIZE 8 #define NFS4_VERIFIER_SIZE 8
#define NFS4_FHSIZE 128 #define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX #define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_ACCESS_READ 0x0001 #define NFS4_ACCESS_READ 0x0001
...@@ -372,6 +373,7 @@ enum { ...@@ -372,6 +373,7 @@ enum {
NFSPROC4_CLNT_REMOVE, NFSPROC4_CLNT_REMOVE,
NFSPROC4_CLNT_RENAME, NFSPROC4_CLNT_RENAME,
NFSPROC4_CLNT_LINK, NFSPROC4_CLNT_LINK,
NFSPROC4_CLNT_SYMLINK,
NFSPROC4_CLNT_CREATE, NFSPROC4_CLNT_CREATE,
NFSPROC4_CLNT_PATHCONF, NFSPROC4_CLNT_PATHCONF,
NFSPROC4_CLNT_STATFS, NFSPROC4_CLNT_STATFS,
......
...@@ -75,13 +75,12 @@ ...@@ -75,13 +75,12 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
/* /*
* NFSv3 Access mode cache * NFSv3/v4 Access mode cache entry
*/ */
struct nfs_access_cache { struct nfs_access_entry {
unsigned long jiffies; unsigned long jiffies;
struct rpc_cred * cred; struct rpc_cred * cred;
int mask; int mask;
int err;
}; };
/* /*
...@@ -137,7 +136,7 @@ struct nfs_inode { ...@@ -137,7 +136,7 @@ struct nfs_inode {
*/ */
atomic_t data_updates; atomic_t data_updates;
struct nfs_access_cache cache_access; struct nfs_access_entry cache_access;
/* /*
* This is the cookie verifier used for NFSv3 readdir * This is the cookie verifier used for NFSv3 readdir
...@@ -148,6 +147,7 @@ struct nfs_inode { ...@@ -148,6 +147,7 @@ struct nfs_inode {
/* /*
* This is the list of dirty unwritten pages. * This is the list of dirty unwritten pages.
*/ */
spinlock_t req_lock;
struct list_head dirty; struct list_head dirty;
struct list_head commit; struct list_head commit;
struct radix_tree_root nfs_page_tree; struct radix_tree_root nfs_page_tree;
......
...@@ -18,6 +18,7 @@ struct nfs_server { ...@@ -18,6 +18,7 @@ struct nfs_server {
unsigned int rpages; /* read size (in pages) */ unsigned int rpages; /* read size (in pages) */
unsigned int wsize; /* write size */ unsigned int wsize; /* write size */
unsigned int wpages; /* write size (in pages) */ unsigned int wpages; /* write size (in pages) */
unsigned int wtmult; /* server disk block size */
unsigned int dtsize; /* readdir size */ unsigned int dtsize; /* readdir size */
unsigned int bsize; /* server block size */ unsigned int bsize; /* server block size */
unsigned int acregmin; /* attr cache timeouts */ unsigned int acregmin; /* attr cache timeouts */
......
...@@ -40,8 +40,8 @@ struct nfs_page { ...@@ -40,8 +40,8 @@ struct nfs_page {
unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */ unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */
unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */ unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */
wb_pgbase, /* Start of page data */ wb_pgbase, /* Start of page data */
wb_bytes, /* Length of request */ wb_bytes; /* Length of request */
wb_count; /* reference count */ atomic_t wb_count; /* reference count */
unsigned long wb_flags; unsigned long wb_flags;
struct nfs_writeverf wb_verf; /* Commit cookie */ struct nfs_writeverf wb_verf; /* Commit cookie */
}; };
...@@ -65,8 +65,6 @@ extern int nfs_coalesce_requests(struct list_head *, struct list_head *, ...@@ -65,8 +65,6 @@ extern int nfs_coalesce_requests(struct list_head *, struct list_head *,
unsigned int); unsigned int);
extern int nfs_wait_on_request(struct nfs_page *); extern int nfs_wait_on_request(struct nfs_page *);
extern spinlock_t nfs_wreq_lock;
/* /*
* Lock the page of an asynchronous request without incrementing the wb_count * Lock the page of an asynchronous request without incrementing the wb_count
*/ */
...@@ -86,7 +84,7 @@ nfs_lock_request(struct nfs_page *req) ...@@ -86,7 +84,7 @@ nfs_lock_request(struct nfs_page *req)
{ {
if (test_and_set_bit(PG_BUSY, &req->wb_flags)) if (test_and_set_bit(PG_BUSY, &req->wb_flags))
return 0; return 0;
req->wb_count++; atomic_inc(&req->wb_count);
return 1; return 1;
} }
......
...@@ -657,6 +657,8 @@ struct nfs_write_data { ...@@ -657,6 +657,8 @@ struct nfs_write_data {
void (*complete) (struct nfs_write_data *, int); void (*complete) (struct nfs_write_data *, int);
}; };
struct nfs_access_entry;
/* /*
* RPC procedure vector for NFSv2/NFSv3 demuxing * RPC procedure vector for NFSv2/NFSv3 demuxing
*/ */
...@@ -672,7 +674,7 @@ struct nfs_rpc_ops { ...@@ -672,7 +674,7 @@ struct nfs_rpc_ops {
struct iattr *); struct iattr *);
int (*lookup) (struct inode *, struct qstr *, int (*lookup) (struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *); struct nfs_fh *, struct nfs_fattr *);
int (*access) (struct inode *, struct rpc_cred *, int); int (*access) (struct inode *, struct nfs_access_entry *);
int (*readlink)(struct inode *, struct page *); int (*readlink)(struct inode *, struct page *);
int (*read) (struct nfs_read_data *, struct file *); int (*read) (struct nfs_read_data *, struct file *);
int (*write) (struct nfs_write_data *, struct file *); int (*write) (struct nfs_write_data *, struct file *);
......
...@@ -69,7 +69,6 @@ u32 g_verify_token_header( ...@@ -69,7 +69,6 @@ u32 g_verify_token_header(
struct xdr_netobj *mech, struct xdr_netobj *mech,
int *body_size, int *body_size,
unsigned char **buf_in, unsigned char **buf_in,
int tok_type,
int toksize); int toksize);
u32 g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf); u32 g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf);
......
/*
* linux/include/linux/sunrpc/gss_spkm3.h
*
* Copyright (c) 2000 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*/
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/gss_err.h>
#include <linux/sunrpc/gss_asn1.h>
struct spkm3_ctx {
struct xdr_netobj ctx_id; /* per message context id */
int qop; /* negotiated qop */
struct xdr_netobj mech_used;
unsigned int ret_flags ;
unsigned int req_flags ;
struct xdr_netobj share_key;
int conf_alg;
struct crypto_tfm* derived_conf_key;
int intg_alg;
struct crypto_tfm* derived_integ_key;
int keyestb_alg; /* alg used to get share_key */
int owf_alg; /* one way function */
};
/* from openssl/objects.h */
/* XXX need SEAL_ALG_NONE */
#define NID_md5 4
#define NID_dhKeyAgreement 28
#define NID_des_cbc 31
#define NID_sha1 64
#define NID_cast5_cbc 108
/* SPKM InnerContext Token types */
#define SPKM_ERROR_TOK 3
#define SPKM_MIC_TOK 4
#define SPKM_WRAP_TOK 5
#define SPKM_DEL_TOK 6
u32 spkm3_make_token(struct spkm3_ctx *ctx, int qop_req, struct xdr_buf * text, struct xdr_netobj * token, int toktype);
u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, struct xdr_buf *message_buffer, int *qop_state, int toktype);
#define CKSUMTYPE_RSA_MD5 0x0007
s32 make_checksum(s32 cksumtype, char *header, int hdrlen, struct xdr_buf *body,
struct xdr_netobj *cksum);
void asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits);
int decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen,
int explen);
void spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen,
unsigned char *ctxhdr, int elen, int zbit);
void spkm3_make_mic_token(unsigned char **tokp, int toklen,
struct xdr_netobj *mic_hdr,
struct xdr_netobj *md5cksum, int md5elen, int md5zbit);
u32 spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen,
unsigned char **cksum);
...@@ -12,3 +12,7 @@ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o ...@@ -12,3 +12,7 @@ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \ rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
gss_krb5_seqnum.o gss_krb5_seqnum.o
obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \
gss_spkm3_token.o
...@@ -397,7 +397,7 @@ gss_upcall(struct rpc_clnt *clnt, struct rpc_task *task, struct rpc_cred *cred) ...@@ -397,7 +397,7 @@ gss_upcall(struct rpc_clnt *clnt, struct rpc_task *task, struct rpc_cred *cred)
spin_unlock(&gss_auth->lock); spin_unlock(&gss_auth->lock);
} }
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
dprintk("RPC: %4u gss_upcall for uid %u result %d", task->tk_pid, dprintk("RPC: %4u gss_upcall for uid %u result %d\n", task->tk_pid,
uid, res); uid, res);
return res; return res;
out_sleep: out_sleep:
......
...@@ -179,7 +179,7 @@ EXPORT_SYMBOL(g_make_token_header); ...@@ -179,7 +179,7 @@ EXPORT_SYMBOL(g_make_token_header);
*/ */
u32 u32
g_verify_token_header(struct xdr_netobj *mech, int *body_size, g_verify_token_header(struct xdr_netobj *mech, int *body_size,
unsigned char **buf_in, int tok_type, int toksize) unsigned char **buf_in, int toksize)
{ {
unsigned char *buf = *buf_in; unsigned char *buf = *buf_in;
int seqsize; int seqsize;
......
...@@ -96,7 +96,7 @@ krb5_read_token(struct krb5_ctx *ctx, ...@@ -96,7 +96,7 @@ krb5_read_token(struct krb5_ctx *ctx,
dprintk("RPC: krb5_read_token\n"); dprintk("RPC: krb5_read_token\n");
if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype, if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr,
read_token->len)) read_token->len))
goto out; goto out;
......
/*
* linux/net/sunrpc/gss_spkm3_mech.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
* J. Bruce Fields <bfields@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/sunrpc/auth.h>
#include <linux/in.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/sunrpc/xdr.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
struct xdr_netobj gss_mech_spkm3_oid =
{7, "\053\006\001\005\005\001\003"};
static inline int
get_bytes(char **ptr, const char *end, void *res, int len)
{
char *p, *q;
p = *ptr;
q = p + len;
if (q > end || q < p)
return -1;
memcpy(res, p, len);
*ptr = q;
return 0;
}
static inline int
get_netobj(char **ptr, const char *end, struct xdr_netobj *res)
{
char *p, *q;
p = *ptr;
if (get_bytes(&p, end, &res->len, sizeof(res->len)))
return -1;
q = p + res->len;
if(res->len == 0)
goto out_nocopy;
if (q > end || q < p)
return -1;
if (!(res->data = kmalloc(res->len, GFP_KERNEL)))
return -1;
memcpy(res->data, p, res->len);
out_nocopy:
*ptr = q;
return 0;
}
static inline int
get_key(char **p, char *end, struct crypto_tfm **res, int *resalg)
{
struct xdr_netobj key = {
.len = 0,
.data = NULL,
};
int alg_mode,setkey = 0;
char *alg_name;
if (get_bytes(p, end, resalg, sizeof(int)))
goto out_err;
if ((get_netobj(p, end, &key)))
goto out_err;
switch (*resalg) {
case NID_des_cbc:
alg_name = "des";
alg_mode = CRYPTO_TFM_MODE_CBC;
setkey = 1;
break;
case NID_md5:
if (key.len == 0) {
dprintk("RPC: SPKM3 get_key: NID_md5 zero Key length\n");
}
alg_name = "md5";
alg_mode = 0;
setkey = 0;
break;
case NID_cast5_cbc:
dprintk("RPC: SPKM3 get_key: case cast5_cbc, UNSUPPORTED \n");
goto out_err;
break;
default:
dprintk("RPC: SPKM3 get_key: unsupported algorithm %d", *resalg);
goto out_err_free_key;
}
if (!(*res = crypto_alloc_tfm(alg_name, alg_mode)))
goto out_err_free_key;
if (setkey) {
if (crypto_cipher_setkey(*res, key.data, key.len))
goto out_err_free_tfm;
}
if(key.len > 0)
kfree(key.data);
return 0;
out_err_free_tfm:
crypto_free_tfm(*res);
out_err_free_key:
if(key.len > 0)
kfree(key.data);
out_err:
return -1;
}
static u32
gss_import_sec_context_spkm3(struct xdr_netobj *inbuf,
struct gss_ctx *ctx_id)
{
char *p = inbuf->data;
char *end = inbuf->data + inbuf->len;
struct spkm3_ctx *ctx;
if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)))
goto out_err;
memset(ctx, 0, sizeof(*ctx));
if (get_netobj(&p, end, &ctx->ctx_id))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->qop, sizeof(ctx->qop)))
goto out_err_free_ctx_id;
if (get_netobj(&p, end, &ctx->mech_used))
goto out_err_free_mech;
if (get_bytes(&p, end, &ctx->ret_flags, sizeof(ctx->ret_flags)))
goto out_err_free_mech;
if (get_bytes(&p, end, &ctx->req_flags, sizeof(ctx->req_flags)))
goto out_err_free_mech;
if (get_netobj(&p, end, &ctx->share_key))
goto out_err_free_s_key;
if (get_key(&p, end, &ctx->derived_conf_key, &ctx->conf_alg)) {
dprintk("RPC: SPKM3 confidentiality key will be NULL\n");
}
if (get_key(&p, end, &ctx->derived_integ_key, &ctx->intg_alg)) {
dprintk("RPC: SPKM3 integrity key will be NULL\n");
}
if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
goto out_err_free_s_key;
if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
goto out_err_free_s_key;
if (p != end)
goto out_err_free_s_key;
ctx_id->internal_ctx_id = ctx;
dprintk("Succesfully imported new spkm context.\n");
return 0;
out_err_free_s_key:
kfree(ctx->share_key.data);
out_err_free_mech:
kfree(ctx->mech_used.data);
out_err_free_ctx_id:
kfree(ctx->ctx_id.data);
out_err_free_ctx:
kfree(ctx);
out_err:
return GSS_S_FAILURE;
}
void
gss_delete_sec_context_spkm3(void *internal_ctx) {
struct spkm3_ctx *sctx = internal_ctx;
if(sctx->derived_integ_key)
crypto_free_tfm(sctx->derived_integ_key);
if(sctx->derived_conf_key)
crypto_free_tfm(sctx->derived_conf_key);
if(sctx->share_key.data)
kfree(sctx->share_key.data);
if(sctx->mech_used.data)
kfree(sctx->mech_used.data);
kfree(sctx);
}
u32
gss_verify_mic_spkm3(struct gss_ctx *ctx,
struct xdr_buf *signbuf,
struct xdr_netobj *checksum,
u32 *qstate) {
u32 maj_stat = 0;
int qop_state = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
dprintk("RPC: gss_verify_mic_spkm3 calling spkm3_read_token\n");
maj_stat = spkm3_read_token(sctx, checksum, signbuf, &qop_state,
SPKM_MIC_TOK);
if (!maj_stat && qop_state)
*qstate = qop_state;
dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat);
return maj_stat;
}
u32
gss_get_mic_spkm3(struct gss_ctx *ctx,
u32 qop,
struct xdr_buf *message_buffer,
struct xdr_netobj *message_token) {
u32 err = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
dprintk("RPC: gss_get_mic_spkm3\n");
err = spkm3_make_token(sctx, qop, message_buffer,
message_token, SPKM_MIC_TOK);
return err;
}
static struct gss_api_ops gss_spkm3_ops = {
.gss_import_sec_context = gss_import_sec_context_spkm3,
.gss_get_mic = gss_get_mic_spkm3,
.gss_verify_mic = gss_verify_mic_spkm3,
.gss_delete_sec_context = gss_delete_sec_context_spkm3,
};
static struct pf_desc gss_spkm3_pfs[] = {
{RPC_AUTH_GSS_SPKM, 0, RPC_GSS_SVC_NONE, "spkm3"},
{RPC_AUTH_GSS_SPKMI, 0, RPC_GSS_SVC_INTEGRITY, "spkm3i"},
};
static struct gss_api_mech gss_spkm3_mech = {
.gm_name = "spkm3",
.gm_owner = THIS_MODULE,
.gm_ops = &gss_spkm3_ops,
.gm_pf_num = ARRAY_SIZE(gss_spkm3_pfs),
.gm_pfs = gss_spkm3_pfs,
};
static int __init init_spkm3_module(void)
{
int status;
status = gss_mech_register(&gss_spkm3_mech);
if (status)
printk("Failed to register spkm3 gss mechanism!\n");
return 0;
}
static void __exit cleanup_spkm3_module(void)
{
gss_mech_unregister(&gss_spkm3_mech);
}
MODULE_LICENSE("GPL");
module_init(init_spkm3_module);
module_exit(cleanup_spkm3_module);
/*
* linux/net/sunrpc/gss_spkm3_seal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* spkm3_make_token()
*
* Only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_make_token(struct spkm3_ctx *ctx, int qop_req,
struct xdr_buf * text, struct xdr_netobj * token,
int toktype)
{
s32 checksum_type;
char tokhdrbuf[25];
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf};
int tmsglen, tokenlen = 0;
unsigned char *ptr;
s32 now;
int ctxelen = 0, ctxzbit = 0;
int md5elen = 0, md5zbit = 0;
dprintk("RPC: spkm3_make_token\n");
now = jiffies;
if (qop_req != 0)
goto out_err;
if (ctx->ctx_id.len != 16) {
dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n",
ctx->ctx_id.len);
goto out_err;
}
switch (ctx->intg_alg) {
case NID_md5:
checksum_type = CKSUMTYPE_RSA_MD5;
break;
default:
dprintk("RPC: gss_spkm3_seal: ctx->signalg %d not"
" supported\n", ctx->intg_alg);
goto out_err;
}
/* XXX since we don't support WRAP, perhaps we don't care... */
if (ctx->conf_alg != NID_cast5_cbc) {
dprintk("RPC: gss_spkm3_seal: ctx->sealalg %d not supported\n",
ctx->conf_alg);
goto out_err;
}
if (toktype == SPKM_MIC_TOK) {
tmsglen = 0;
/* Calculate checksum over the mic-header */
asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
ctxelen, ctxzbit);
if (make_checksum(checksum_type, mic_hdr.data, mic_hdr.len,
text, &md5cksum))
goto out_err;
asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
tokenlen = 10 + ctxelen + 1 + 2 + md5elen + 1;
/* Create token header using generic routines */
token->len = g_token_size(&ctx->mech_used, tokenlen + tmsglen);
ptr = token->data;
g_make_token_header(&ctx->mech_used, tokenlen + tmsglen, &ptr);
spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit);
} else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */
dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK not supported\n");
goto out_err;
}
kfree(md5cksum.data);
/* XXX need to implement sequence numbers, and ctx->expired */
return GSS_S_COMPLETE;
out_err:
if (md5cksum.data)
kfree(md5cksum.data);
token->data = 0;
token->len = 0;
return GSS_S_FAILURE;
}
/*
* linux/net/sunrpc/gss_spkm3_token.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* asn1_bitstring_len()
*
* calculate the asn1 bitstring length of the xdr_netobject
*/
void
asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
{
int i, zbit = 0,elen = in->len;
char *ptr;
ptr = &in->data[in->len -1];
/* count trailing 0's */
for(i = in->len; i > 0; i--) {
if (*ptr == 0) {
ptr--;
elen--;
} else
break;
}
/* count number of 0 bits in final octet */
ptr = &in->data[elen - 1];
for(i = 0; i < 8; i++) {
short mask = 0x01;
if (!((mask << i) & *ptr))
zbit++;
else
break;
}
*enclen = elen;
*zerobits = zbit;
}
/*
* decode_asn1_bitstring()
*
* decode a bitstring into a buffer of the expected length.
* enclen = bit string length
* explen = expected length (define in rfc)
*/
int
decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
{
if (!(out->data = kmalloc(explen,GFP_KERNEL)))
return 0;
out->len = explen;
memset(out->data, 0, explen);
memcpy(out->data, in, enclen);
return 1;
}
/*
* SPKMInnerContextToken choice SPKM_MIC asn1 token layout
*
* contextid is always 16 bytes plain data. max asn1 bitstring len = 17.
*
* tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum)
*
* pos value
* ----------
* [0] a4 SPKM-MIC tag
* [1] ?? innertoken length (max 44)
*
*
* tok_hdr piece of checksum data starts here
*
* the maximum mic-header len = 9 + 17 = 26
* mic-header
* ----------
* [2] 30 SEQUENCE tag
* [3] ?? mic-header length: (max 23) = TokenID + ContextID
*
* TokenID - all fields constant and can be hardcoded
* -------
* [4] 02 Type 2
* [5] 02 Length 2
* [6][7] 01 01 TokenID (SPKM_MIC_TOK)
*
* ContextID - encoded length not constant, calculated
* ---------
* [8] 03 Type 3
* [9] ?? encoded length
* [10] ?? ctxzbit
* [11] contextid
*
* mic_header piece of checksum data ends here.
*
* int-cksum - encoded length not constant, calculated
* ---------
* [??] 03 Type 3
* [??] ?? encoded length
* [??] ?? md5zbit
* [??] int-cksum (NID_md5 = 16)
*
* maximum SPKM-MIC innercontext token length =
* 10 + encoded contextid_size(17 max) + 2 + encoded
* cksum_size (17 maxfor NID_md5) = 46
*/
/*
* spkm3_mic_header()
*
* Prepare the SPKM_MIC_TOK mic-header for check-sum calculation
* elen: 16 byte context id asn1 bitstring encoded length
*/
void
spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit)
{
char *hptr = *hdrbuf;
char *top = *hdrbuf;
*(u8 *)hptr++ = 0x30;
*(u8 *)hptr++ = elen + 7; /* on the wire header length */
/* tokenid */
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x01;
*(u8 *)hptr++ = 0x01;
/* coniextid */
*(u8 *)hptr++ = 0x03;
*(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */
*(u8 *)hptr++ = zbit;
memcpy(hptr, ctxdata, elen);
hptr += elen;
*hdrlen = hptr - top;
}
/*
* spkm3_mic_innercontext_token()
*
* *tokp points to the beginning of the SPKM_MIC token described
* in rfc 2025, section 3.2.1:
*
*/
void
spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit)
{
unsigned char *ict = *tokp;
*(u8 *)ict++ = 0xa4;
*(u8 *)ict++ = toklen - 2;
memcpy(ict, mic_hdr->data, mic_hdr->len);
ict += mic_hdr->len;
*(u8 *)ict++ = 0x03;
*(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */
*(u8 *)ict++ = md5zbit;
memcpy(ict, md5cksum->data, md5elen);
}
u32
spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum)
{
struct xdr_netobj spkm3_ctx_id = {.len =0, .data = NULL};
unsigned char *ptr = *tokp;
int ctxelen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
/* spkm3 innercontext token preamble */
if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) {
dprintk("RPC: BAD SPKM ictoken preamble\n");
goto out;
}
*mic_hdrlen = ptr[3];
/* token type */
if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) {
dprintk("RPC: BAD asn1 SPKM3 token type\n");
goto out;
}
/* only support SPKM_MIC_TOK */
if((ptr[6] != 0x01) || (ptr[7] != 0x01)) {
dprintk("RPC: ERROR unsupported SPKM3 token \n");
goto out;
}
/* contextid */
if (ptr[8] != 0x03) {
dprintk("RPC: BAD SPKM3 asn1 context-id type\n");
goto out;
}
ctxelen = ptr[9];
if (ctxelen > 17) { /* length includes asn1 zbit octet */
dprintk("RPC: BAD SPKM3 contextid len %d\n", ctxelen);
goto out;
}
/* ignore ptr[10] */
if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16))
goto out;
/*
* in the current implementation: the optional int-alg is not present
* so the default int-alg (md5) is used the optional snd-seq field is
* also not present
*/
if (*mic_hdrlen != 6 + ctxelen) {
dprintk("RPC: BAD SPKM_ MIC_TOK header len %d: we only support default int-alg (should be absent) and do not support snd-seq\n", *mic_hdrlen);
goto out;
}
/* checksum */
*cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */
ret = GSS_S_COMPLETE;
out:
if (spkm3_ctx_id.data)
kfree(spkm3_ctx_id.data);
return ret;
}
/*
* linux/net/sunrpc/gss_spkm3_unseal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* spkm3_read_token()
*
* only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_read_token(struct spkm3_ctx *ctx,
struct xdr_netobj *read_token, /* checksum */
struct xdr_buf *message_buffer, /* signbuf */
int *qop_state, int toktype)
{
s32 code;
struct xdr_netobj wire_cksum = {.len =0, .data = NULL};
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
unsigned char *ptr = (unsigned char *)read_token->data;
unsigned char *cksum;
int bodysize, md5elen;
int mic_hdrlen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
dprintk("RPC: spkm3_read_token read_token->len %d\n", read_token->len);
if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
&bodysize, &ptr, read_token->len))
goto out;
/* decode the token */
if (toktype == SPKM_MIC_TOK) {
if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
goto out;
if (*cksum++ != 0x03) {
dprintk("RPC: spkm3_read_token BAD checksum type\n");
goto out;
}
md5elen = *cksum++;
cksum++; /* move past the zbit */
if(!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
goto out;
/* HARD CODED FOR MD5 */
/* compute the checksum of the message.
* ptr + 2 = start of header piece of checksum
* mic_hdrlen + 2 = length of header piece of checksum
*/
ret = GSS_S_DEFECTIVE_TOKEN;
code = make_checksum(CKSUMTYPE_RSA_MD5, ptr + 2,
mic_hdrlen + 2,
message_buffer, &md5cksum);
if (code)
goto out;
dprintk("RPC: spkm3_read_token: digest wire_cksum.len %d:\n",
wire_cksum.len);
dprintk(" md5cksum.data\n");
print_hexl((u32 *) md5cksum.data, 16, 0);
dprintk(" cksum.data:\n");
print_hexl((u32 *) wire_cksum.data, wire_cksum.len, 0);
ret = GSS_S_BAD_SIG;
code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
if (code)
goto out;
} else {
dprintk("RPC: BAD or UNSUPPORTED SPKM3 token type: %d\n",toktype);
goto out;
}
/* XXX: need to add expiration and sequencing */
ret = GSS_S_COMPLETE;
out:
if (md5cksum.data)
kfree(md5cksum.data);
if (wire_cksum.data)
kfree(wire_cksum.data);
return ret;
}
...@@ -196,7 +196,15 @@ rpc_clone_client(struct rpc_clnt *clnt) ...@@ -196,7 +196,15 @@ rpc_clone_client(struct rpc_clnt *clnt)
memcpy(new, clnt, sizeof(*new)); memcpy(new, clnt, sizeof(*new));
atomic_set(&new->cl_count, 1); atomic_set(&new->cl_count, 1);
atomic_set(&new->cl_users, 0); atomic_set(&new->cl_users, 0);
atomic_inc(&new->cl_parent->cl_count); new->cl_parent = clnt;
atomic_inc(&clnt->cl_count);
/* Duplicate portmapper */
rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
/* Turn off autobind on clones */
new->cl_autobind = 0;
new->cl_oneshot = 0;
new->cl_dead = 0;
rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
if (new->cl_auth) if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count); atomic_inc(&new->cl_auth->au_count);
return new; return new;
...@@ -335,7 +343,7 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset) ...@@ -335,7 +343,7 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
*/ */
int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
{ {
struct rpc_task my_task, *task = &my_task; struct rpc_task *task;
sigset_t oldset; sigset_t oldset;
int status; int status;
...@@ -343,15 +351,15 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) ...@@ -343,15 +351,15 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
if (clnt->cl_dead) if (clnt->cl_dead)
return -EIO; return -EIO;
if (flags & RPC_TASK_ASYNC) { BUG_ON(flags & RPC_TASK_ASYNC);
printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
flags &= ~RPC_TASK_ASYNC;
}
rpc_clnt_sigmask(clnt, &oldset); rpc_clnt_sigmask(clnt, &oldset);
/* Create/initialize a new RPC task */ status = -ENOMEM;
rpc_init_task(task, clnt, NULL, flags); task = rpc_new_task(clnt, NULL, flags);
if (task == NULL)
goto out;
rpc_call_setup(task, msg, 0); rpc_call_setup(task, msg, 0);
/* Set up the call info struct and execute the task */ /* Set up the call info struct and execute the task */
...@@ -362,6 +370,7 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) ...@@ -362,6 +370,7 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
rpc_release_task(task); rpc_release_task(task);
} }
out:
rpc_clnt_sigunmask(clnt, &oldset); rpc_clnt_sigunmask(clnt, &oldset);
return status; return status;
...@@ -958,8 +967,12 @@ call_header(struct rpc_task *task) ...@@ -958,8 +967,12 @@ call_header(struct rpc_task *task)
static u32 * static u32 *
call_verify(struct rpc_task *task) call_verify(struct rpc_task *task)
{ {
u32 *p = task->tk_rqstp->rq_rcv_buf.head[0].iov_base, n; struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0];
int len = task->tk_rqstp->rq_rcv_buf.len >> 2;
u32 *p = iov->iov_base, n;
if ((len -= 3) < 0)
goto garbage;
p += 1; /* skip XID */ p += 1; /* skip XID */
if ((n = ntohl(*p++)) != RPC_REPLY) { if ((n = ntohl(*p++)) != RPC_REPLY) {
...@@ -969,9 +982,11 @@ call_verify(struct rpc_task *task) ...@@ -969,9 +982,11 @@ call_verify(struct rpc_task *task)
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
int error = -EACCES; int error = -EACCES;
if (--len < 0)
goto garbage;
if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) { if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n); printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
} else } else if (--len < 0)
switch ((n = ntohl(*p++))) { switch ((n = ntohl(*p++))) {
case RPC_AUTH_REJECTEDCRED: case RPC_AUTH_REJECTEDCRED:
case RPC_AUTH_REJECTEDVERF: case RPC_AUTH_REJECTEDVERF:
...@@ -1002,7 +1017,8 @@ call_verify(struct rpc_task *task) ...@@ -1002,7 +1017,8 @@ call_verify(struct rpc_task *task)
default: default:
printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n); printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
error = -EIO; error = -EIO;
} } else
goto garbage;
dprintk("RPC: %4d call_verify: call rejected %d\n", dprintk("RPC: %4d call_verify: call rejected %d\n",
task->tk_pid, n); task->tk_pid, n);
rpc_exit(task, error); rpc_exit(task, error);
...@@ -1012,6 +1028,9 @@ call_verify(struct rpc_task *task) ...@@ -1012,6 +1028,9 @@ call_verify(struct rpc_task *task)
printk(KERN_WARNING "call_verify: auth check failed\n"); printk(KERN_WARNING "call_verify: auth check failed\n");
goto garbage; /* bad verifier, retry */ goto garbage; /* bad verifier, retry */
} }
len = p - (u32 *)iov->iov_base - 1;
if (len < 0)
goto garbage;
switch ((n = ntohl(*p++))) { switch ((n = ntohl(*p++))) {
case RPC_SUCCESS: case RPC_SUCCESS:
return p; return p;
......
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