Commit 46628744 authored by Linus Torvalds's avatar Linus Torvalds

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

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 6ffa96a3 c5230112
......@@ -8,6 +8,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \
proc.o read.o symlink.o unlink.o write.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
idmap.o
nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
nfs-objs := $(nfs-y)
......@@ -35,6 +35,7 @@
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
static int nfs_opendir(struct inode *, struct file *);
static int nfs_readdir(struct file *, void *, filldir_t);
static struct dentry *nfs_lookup(struct inode *, struct dentry *);
static int nfs_cached_lookup(struct inode *, struct dentry *,
......@@ -52,7 +53,7 @@ static int nfs_rename(struct inode *, struct dentry *,
struct file_operations nfs_dir_operations = {
.read = generic_read_dir,
.readdir = nfs_readdir,
.open = nfs_open,
.open = nfs_opendir,
.release = nfs_release,
};
......@@ -71,6 +72,26 @@ struct inode_operations nfs_dir_inode_operations = {
.setattr = nfs_setattr,
};
/*
* Open file
*/
static int
nfs_opendir(struct inode *inode, struct file *filp)
{
struct nfs_server *server = NFS_SERVER(inode);
int res = 0;
lock_kernel();
/* Do cto revalidation */
if (server->flags & NFS_MOUNT_NOCTO)
res = __nfs_revalidate_inode(server, inode);
/* Call generic open code in order to cache credentials */
if (!res)
res = nfs_open(inode, filp);
unlock_kernel();
return res;
}
typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
typedef struct {
struct file *file;
......
......@@ -34,6 +34,7 @@
#define NFSDBG_FACILITY NFSDBG_FILE
static int nfs_file_open(struct inode *, struct file *);
static int nfs_file_mmap(struct file *, struct vm_area_struct *);
static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
static ssize_t nfs_file_read(struct kiocb *, char *, size_t, loff_t);
......@@ -48,7 +49,7 @@ struct file_operations nfs_file_operations = {
.aio_read = nfs_file_read,
.aio_write = nfs_file_write,
.mmap = nfs_file_mmap,
.open = nfs_open,
.open = nfs_file_open,
.flush = nfs_file_flush,
.release = nfs_release,
.fsync = nfs_fsync,
......@@ -67,6 +68,30 @@ struct inode_operations nfs_file_inode_operations = {
# define IS_SWAPFILE(inode) (0)
#endif
/*
* Open file
*/
static int
nfs_file_open(struct inode *inode, struct file *filp)
{
struct nfs_server *server = NFS_SERVER(inode);
int (*open)(struct inode *, struct file *);
int res = 0;
lock_kernel();
/* Do NFSv4 open() call */
if ((open = server->rpc_ops->file_open) != NULL)
res = open(inode, filp);
/* Do cto revalidation */
else if (server->flags & NFS_MOUNT_NOCTO)
res = __nfs_revalidate_inode(server, inode);
/* Call generic open code in order to cache credentials */
if (!res)
res = nfs_open(inode, filp);
unlock_kernel();
return res;
}
/*
* Flush all dirty pages, and check for write errors.
*
......
/*
* fs/nfs/idmap.c
*
* UID and GID to name mapping for clients.
*
* Copyright (c) 2002 The Regents of the University of Michigan.
* All rights reserved.
*
* Marius Aamodt Eriksen <marius@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/socket.h>
#include <linux/in.h>
#include <linux/sched.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/nfs_fs_sb.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h>
#define IDMAP_HASH_SZ 128
#define IDMAP_HASH_TYPE_NAME 0x01
#define IDMAP_HASH_TYPE_ID 0x02
#define IDMAP_HASH_TYPE_INSERT 0x04
struct idmap_hashent {
uid_t ih_id;
char ih_name[IDMAP_NAMESZ];
u_int32_t ih_namelen;
};
struct idmap {
char idmap_path[48];
struct dentry *idmap_dentry;
wait_queue_head_t idmap_wq;
struct idmap_msg idmap_im;
struct nfs_server *idmap_server;
struct semaphore idmap_lock;
struct semaphore idmap_im_lock;
struct semaphore idmap_hash_lock;
struct idmap_hashent idmap_id_hash[IDMAP_HASH_SZ];
struct idmap_hashent idmap_name_hash[IDMAP_HASH_SZ];
};
static ssize_t idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *, char *,
size_t);
static ssize_t idmap_pipe_downcall(struct file *, const char *, size_t);
void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
static int validate_ascii(char *, u_int32_t);
static u_int32_t fnvhash32(void *, u_int32_t);
static int idmap_cache_lookup(struct idmap *, int, char *, u_int32_t *, uid_t *);
static struct rpc_pipe_ops idmap_upcall_ops = {
.upcall = idmap_pipe_upcall,
.downcall = idmap_pipe_downcall,
.destroy_msg = idmap_pipe_destroy_msg,
};
void *
nfs_idmap_new(struct nfs_server *server)
{
struct idmap *idmap;
if ((idmap = kmalloc(sizeof(*idmap), GFP_KERNEL)) == NULL)
return (NULL);
memset(idmap, 0, sizeof(*idmap));
idmap->idmap_server = server;
snprintf(idmap->idmap_path, sizeof(idmap->idmap_path),
"%s/idmap", idmap->idmap_server->client->cl_pathname);
idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path,
idmap->idmap_server, &idmap_upcall_ops);
if (IS_ERR(idmap->idmap_dentry))
goto err_free;
init_MUTEX(&idmap->idmap_lock);
init_MUTEX(&idmap->idmap_im_lock);
init_MUTEX(&idmap->idmap_hash_lock);
return (idmap);
err_free:
kfree(idmap);
return (NULL);
}
void
nfs_idmap_delete(struct nfs_server *server)
{
struct idmap *idmap = server->idmap;
rpc_unlink(idmap->idmap_path);
kfree(idmap);
}
/*
* Name -> ID
*/
int
nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name,
u_int namelen, uid_t *id)
{
struct rpc_pipe_msg msg;
struct idmap *idmap = server->idmap;
struct idmap_msg *im;
DECLARE_WAITQUEUE(wq, current);
int ret = -1, hashtype = IDMAP_HASH_TYPE_NAME, xnamelen = namelen;
if (idmap == NULL)
return (-1);
im = &idmap->idmap_im;
if (namelen > IDMAP_NAMESZ || namelen == 0)
return (-1);
down(&idmap->idmap_lock);
down(&idmap->idmap_im_lock);
if (name[xnamelen - 1] == '\0')
xnamelen--;
if (idmap_cache_lookup(idmap, hashtype, name, &xnamelen, id) == 0) {
ret = 0;
goto out;
}
memset(im, 0, sizeof(*im));
memcpy(im->im_name, name, namelen);
/* Make sure the string is NULL terminated */
if (namelen != xnamelen) {
/* We cannot fit a NULL character */
if (namelen == IDMAP_NAMESZ) {
ret = -1;
goto out;
}
im->im_name[namelen] = '\0';
}
im->im_type = type;
im->im_conv = IDMAP_CONV_NAMETOID;
memset(&msg, 0, sizeof(msg));
msg.data = im;
msg.len = sizeof(*im);
init_waitqueue_head(&idmap->idmap_wq);
add_wait_queue(&idmap->idmap_wq, &wq);
set_current_state(TASK_UNINTERRUPTIBLE);
if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) {
set_current_state(TASK_RUNNING);
goto out;
}
up(&idmap->idmap_im_lock);
schedule();
down(&idmap->idmap_im_lock);
/*
* XXX Race condition here, with testing for status. Go ahead
* and and do the cace lookup anyway.
*/
if (im->im_status & IDMAP_STATUS_SUCCESS) {
ret = 0;
*id = im->im_id;
hashtype |= IDMAP_HASH_TYPE_INSERT;
ret = idmap_cache_lookup(idmap, hashtype, name, &xnamelen, id);
}
out:
memset(im, 0, sizeof(*im));
up(&idmap->idmap_im_lock);
up(&idmap->idmap_lock);
return (ret);
}
/*
* ID -> Name
*/
int
nfs_idmap_name(struct nfs_server *server, u_int8_t type, uid_t id,
char *name, u_int *namelen)
{
struct rpc_pipe_msg msg;
struct idmap *idmap = server->idmap;
struct idmap_msg *im;
DECLARE_WAITQUEUE(wq, current);
int ret = -1, hashtype = IDMAP_HASH_TYPE_ID;
u_int len;
if (idmap == NULL)
return (-1);
im = &idmap->idmap_im;
if (*namelen < IDMAP_NAMESZ || *namelen == 0)
return (-1);
down(&idmap->idmap_lock);
down(&idmap->idmap_im_lock);
if (idmap_cache_lookup(idmap, hashtype, name, namelen, &id) == 0) {
ret = 0;
goto out;
}
memset(im, 0, sizeof(*im));
im->im_type = type;
im->im_conv = IDMAP_CONV_IDTONAME;
im->im_id = id;
memset(&msg, 0, sizeof(msg));
msg.data = im;
msg.len = sizeof(*im);
init_waitqueue_head(&idmap->idmap_wq);
add_wait_queue(&idmap->idmap_wq, &wq);
set_current_state(TASK_UNINTERRUPTIBLE);
if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) {
set_current_state(TASK_RUNNING);
goto out;
}
/*
* XXX add timeouts here
*/
up(&idmap->idmap_im_lock);
schedule();
down(&idmap->idmap_im_lock);
if (im->im_status & IDMAP_STATUS_SUCCESS) {
if ((len = validate_ascii(im->im_name, IDMAP_NAMESZ)) == -1)
goto out;
ret = 0;
memcpy(name, im->im_name, len);
*namelen = len;
hashtype |= IDMAP_HASH_TYPE_INSERT;
ret = idmap_cache_lookup(idmap, hashtype, name, namelen, &id);
}
out:
memset(im, 0, sizeof(*im));
up(&idmap->idmap_im_lock);
up(&idmap->idmap_lock);
return (ret);
}
static ssize_t
idmap_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
char *dst, size_t buflen)
{
char *data = (char *)msg->data + msg->copied;
ssize_t mlen = msg->len - msg->copied;
ssize_t left;
if (mlen > buflen)
mlen = buflen;
left = copy_to_user(dst, data, mlen);
return (mlen - left);
}
static ssize_t
idmap_pipe_downcall(struct file *filp, const char *src, size_t mlen)
{
struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode);
struct nfs_server *server = rpci->private;
struct idmap *idmap = server->idmap;
struct idmap_msg im_in, *im = &idmap->idmap_im;
int match = 0, hashtype, badmsg = 0, namelen_in, namelen;
if (mlen != sizeof(im_in))
return (-ENOSPC);
if (copy_from_user(&im_in, src, mlen) != 0)
return (-EFAULT);
down(&idmap->idmap_im_lock);
namelen_in = validate_ascii(im_in.im_name, IDMAP_NAMESZ);
namelen = validate_ascii(im->im_name, IDMAP_NAMESZ);
badmsg = !(im_in.im_status & IDMAP_STATUS_SUCCESS) || namelen_in <= 0;
switch (im_in.im_conv) {
case IDMAP_CONV_IDTONAME:
match = im->im_id == im_in.im_id;
break;
case IDMAP_CONV_NAMETOID:
match = namelen == namelen_in &&
memcmp(im->im_name, im_in.im_name, namelen) == 0;
break;
default:
badmsg = 1;
break;
}
match = match && im->im_type == im_in.im_type;
if (match) {
memcpy(im, &im_in, sizeof(*im));
wake_up(&idmap->idmap_wq);
__rpc_purge_current_upcall(filp);
} else if (!badmsg) {
hashtype = im_in.im_conv == IDMAP_CONV_IDTONAME ?
IDMAP_HASH_TYPE_ID : IDMAP_HASH_TYPE_NAME;
hashtype |= IDMAP_HASH_TYPE_INSERT;
idmap_cache_lookup(idmap, hashtype, im_in.im_name, &namelen_in,
&im_in.im_id);
}
up(&idmap->idmap_im_lock);
return (mlen);
}
void
idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
struct idmap_msg *im = msg->data;
struct idmap *idmap = container_of(im, struct idmap, idmap_im);
down(&idmap->idmap_im_lock);
im->im_status = IDMAP_STATUS_LOOKUPFAIL;
wake_up(&idmap->idmap_wq);
up(&idmap->idmap_im_lock);
}
static int
validate_ascii(char *string, u_int32_t len)
{
int i;
for (i = 0; i < len; i++) {
if (string[i] == '\0')
break;
if (string[i] & 0x80)
return (-1);
}
if (string[i] != '\0')
return (-1);
return (i);
}
/*
* Fowler/Noll/Vo hash
* http://www.isthe.com/chongo/tech/comp/fnv/
*/
#define FNV_P_32 ((u_int32_t)0x01000193) /* 16777619 */
#define FNV_1_32 ((u_int32_t)0x811c9dc5) /* 2166136261 */
static u_int32_t
fnvhash32(void *buf, u_int32_t buflen)
{
u_char *p, *end = (u_char *)buf + buflen;
u_int32_t hash = FNV_1_32;
for (p = buf; p < end; p++) {
hash *= FNV_P_32;
hash ^= (u_int32_t)*p;
}
return (hash);
}
/*
* ->ih_namelen == 0 indicates negative entry
*/
static int
idmap_cache_lookup(struct idmap *idmap, int type, char *name, u_int32_t *namelen,
uid_t *id)
{
u_int32_t hash;
struct idmap_hashent *he = NULL;
int insert = type & IDMAP_HASH_TYPE_INSERT;
int ret = -1;
/*
* XXX technically, this is not needed, since we will always
* hold idmap_im_lock when altering the hash tables. but
* semantically that just hurts.
*
* XXX cache negative responses
*/
down(&idmap->idmap_hash_lock);
if (*namelen > IDMAP_NAMESZ || *namelen == 0)
goto out;
if (type & IDMAP_HASH_TYPE_NAME) {
hash = fnvhash32(name, *namelen) % IDMAP_HASH_SZ;
he = &idmap->idmap_name_hash[hash];
/*
* Testing he->ih_namelen == *namelen implicitly tests
* namelen != 0, and thus a non-negative entry.
*/
if (!insert && he->ih_namelen == *namelen &&
memcmp(he->ih_name, name, *namelen) == 0) {
*id = he->ih_id;
ret = 0;
goto out;
}
}
if (type & IDMAP_HASH_TYPE_ID) {
hash = fnvhash32(id, sizeof(*id)) % IDMAP_HASH_SZ;
he = &idmap->idmap_id_hash[hash];
if (!insert && *id == he->ih_id && he->ih_namelen != 0 &&
*namelen >= he->ih_namelen) {
memcpy(name, he->ih_name, he->ih_namelen);
*namelen = he->ih_namelen;
ret = 0;
goto out;
}
}
if (insert && he != NULL) {
he->ih_id = *id;
memcpy(he->ih_name, name, *namelen);
he->ih_namelen = *namelen;
ret = 0;
}
out:
up(&idmap->idmap_hash_lock);
return (ret);
}
......@@ -33,6 +33,7 @@
#include <linux/smp_lock.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include <linux/nfs_idmap.h>
#include <linux/vfs.h>
#include <asm/system.h>
......@@ -140,6 +141,10 @@ nfs_clear_inode(struct inode *inode)
cred = nfsi->cache_access.cred;
if (cred)
put_rpccred(cred);
/* Clean up the V4 state */
nfs4_put_shareowner(inode, nfsi->wo_owner);
nfs4_put_shareowner(inode, nfsi->ro_owner);
nfs4_put_shareowner(inode, nfsi->rw_owner);
}
void
......@@ -148,6 +153,11 @@ nfs_put_super(struct super_block *sb)
struct nfs_server *server = NFS_SB(sb);
struct rpc_clnt *rpc;
#ifdef CONFIG_NFS_V4
if (server->idmap != NULL)
nfs_idmap_delete(server);
#endif /* CONFIG_NFS_V4 */
if ((rpc = server->client) != NULL)
rpc_shutdown_client(rpc);
......@@ -156,6 +166,7 @@ nfs_put_super(struct super_block *sb)
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
kfree(server->hostname);
}
......@@ -855,23 +866,13 @@ int nfs_open(struct inode *inode, struct file *filp)
{
struct rpc_auth *auth;
struct rpc_cred *cred;
int err = 0;
lock_kernel();
/* Ensure that we revalidate the data cache */
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOCTO) {
err = __nfs_revalidate_inode(NFS_SERVER(inode),inode);
if (err)
goto out;
}
auth = NFS_CLIENT(inode)->cl_auth;
cred = rpcauth_lookupcred(auth, 0);
filp->private_data = cred;
if (filp->f_mode & FMODE_WRITE)
nfs_set_mmcred(inode, cred);
out:
unlock_kernel();
return err;
return 0;
}
int nfs_release(struct inode *inode, struct file *filp)
......@@ -1368,11 +1369,16 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
if (create_nfsv4_state(server, data))
goto out_shutdown;
if ((server->idmap = nfs_idmap_new(server)) == NULL)
printk(KERN_WARNING "NFS: couldn't start IDmap\n");
err = nfs_sb_init(sb);
if (err == 0)
return 0;
rpciod_down();
destroy_nfsv4_state(server);
if (server->idmap != NULL)
nfs_idmap_delete(server);
out_shutdown:
rpc_shutdown_client(server->client);
out_fail:
......@@ -1502,9 +1508,18 @@ static struct file_system_type nfs4_fs_type = {
.kill_sb = nfs_kill_super,
.fs_flags = FS_ODD_RENAME,
};
#define nfs4_zero_state(nfsi) \
do { \
(nfsi)->wo_owner = NULL; \
(nfsi)->ro_owner = NULL; \
(nfsi)->rw_owner = NULL; \
} while(0)
#define register_nfs4fs() register_filesystem(&nfs4_fs_type)
#define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type)
#else
#define nfs4_zero_state(nfsi) \
do { } while (0)
#define register_nfs4fs() (0)
#define unregister_nfs4fs()
#endif
......@@ -1526,6 +1541,7 @@ static struct inode *nfs_alloc_inode(struct super_block *sb)
return NULL;
nfsi->flags = 0;
nfsi->mm_cred = NULL;
nfs4_zero_state(nfsi);
return &nfsi->vfs_inode;
}
......
......@@ -55,7 +55,8 @@ extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[];
static nfs4_stateid zero_stateid =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static spinlock_t renew_lock = SPIN_LOCK_UNLOCKED;
static void
......@@ -80,19 +81,6 @@ nfs4_setup_access(struct nfs4_compound *cp, u32 req_access, u32 *resp_supported,
cp->req_nops++;
}
static void
nfs4_setup_close(struct nfs4_compound *cp, nfs4_stateid stateid, u32 seqid)
{
struct nfs4_close *close = GET_OP(cp, close);
close->cl_stateid = stateid;
close->cl_seqid = seqid;
OPNUM(cp) = OP_CLOSE;
cp->req_nops++;
cp->renew_index = cp->req_nops;
}
static void
nfs4_setup_create_dir(struct nfs4_compound *cp, struct qstr *name,
struct iattr *sattr, struct nfs4_change_info *info)
......@@ -346,48 +334,6 @@ nfs4_setup_putrootfh(struct nfs4_compound *cp)
cp->req_nops++;
}
static void
nfs4_setup_open(struct nfs4_compound *cp, int flags, struct qstr *name,
struct iattr *sattr, char *stateid, struct nfs4_change_info *cinfo,
u32 *rflags)
{
struct nfs4_open *open = GET_OP(cp, open);
BUG_ON(cp->flags);
open->op_client_state = cp->server->nfs4_state;
open->op_share_access = flags & 3;
open->op_opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE;
open->op_createmode = NFS4_CREATE_UNCHECKED;
open->op_attrs = sattr;
if (flags & O_EXCL) {
u32 *p = (u32 *) open->op_verifier;
p[0] = jiffies;
p[1] = current->pid;
open->op_createmode = NFS4_CREATE_EXCLUSIVE;
}
open->op_name = name;
open->op_stateid = stateid;
open->op_cinfo = cinfo;
open->op_rflags = rflags;
OPNUM(cp) = OP_OPEN;
cp->req_nops++;
cp->renew_index = cp->req_nops;
}
static void
nfs4_setup_open_confirm(struct nfs4_compound *cp, char *stateid)
{
struct nfs4_open_confirm *open_confirm = GET_OP(cp, open_confirm);
open_confirm->oc_stateid = stateid;
OPNUM(cp) = OP_OPEN_CONFIRM;
cp->req_nops++;
cp->renew_index = cp->req_nops;
}
static void
nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier,
struct page **pages, unsigned int bufsize, struct dentry *dentry)
......@@ -516,18 +462,6 @@ nfs4_setup_savefh(struct nfs4_compound *cp)
cp->req_nops++;
}
static void
nfs4_setup_setattr(struct nfs4_compound *cp, char *stateid, struct iattr *iap)
{
struct nfs4_setattr *setattr = GET_OP(cp, setattr);
setattr->st_stateid = stateid;
setattr->st_iap = iap;
OPNUM(cp) = OP_SETATTR;
cp->req_nops++;
}
static void
nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short port)
{
......@@ -626,72 +560,193 @@ process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr)
}
}
static int
do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr,
struct nfs_fattr *fattr, struct nfs_fh *fhandle, u32 *seqid, char *stateid)
{
struct nfs4_compound compound;
struct nfs4_op ops[7];
struct nfs4_change_info dir_cinfo;
struct nfs_fattr dir_attr;
u32 dir_bmres[2];
u32 bmres[2];
u32 rflags;
int status;
dir_attr.valid = 0;
fattr->valid = 0;
nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "open");
nfs4_setup_putfh(&compound, NFS_FH(dir));
nfs4_setup_savefh(&compound);
nfs4_setup_open(&compound, flags, name, sattr, stateid, &dir_cinfo, &rflags);
nfs4_setup_getattr(&compound, fattr, bmres);
nfs4_setup_getfh(&compound, fhandle);
nfs4_setup_restorefh(&compound);
nfs4_setup_getattr(&compound, &dir_attr, dir_bmres);
if ((status = nfs4_call_compound(&compound, NULL, 0)))
return status;
int
nfs4_do_open(struct inode *dir, struct qstr *name, int flags,
struct iattr *sattr, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct nfs4_shareowner **spp)
{
struct nfs4_shareowner *sp;
struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_change_info d_cinfo;
int status;
u32 f_bmres[2];
u32 d_bmres[2];
struct nfs_fattr d_attr = {
.valid 0,
};
struct nfs_fattr f_attr = {
.valid 0,
};
struct nfs4_getattr f_getattr = {
.gt_bmval = nfs4_fattr_bitmap,
.gt_attrs = (fattr == NULL ? &f_attr: fattr),
.gt_bmres = f_bmres,
};
struct nfs4_getattr d_getattr = {
.gt_bmval = nfs4_fattr_bitmap,
.gt_attrs = &d_attr,
.gt_bmres = d_bmres,
};
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.share_access = flags & O_ACCMODE,
.clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid,
.opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE,
.createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED,
.name = name,
.f_getattr = &f_getattr,
.d_getattr = &d_getattr,
.server = server,
};
struct nfs_openres o_res = {
.cinfo = &d_cinfo,
.f_getattr = &f_getattr,
.d_getattr = &d_getattr,
.server = server,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN],
.rpc_argp = &o_arg,
.rpc_resp = &o_res,
};
process_cinfo(&dir_cinfo, &dir_attr);
nfs_refresh_inode(dir, &dir_attr);
if (!(rflags & NFS4_OPEN_RESULT_CONFIRM)) {
*seqid = 1;
return 0;
status = -ENOMEM;
if (!(sp = nfs4_get_shareowner(dir))) {
dprintk("nfs4_do_open: nfs4_get_shareowner failed!\n");
goto out;
}
if (o_arg.createmode & NFS4_CREATE_EXCLUSIVE){
u32 *p = (u32 *) o_arg.u.verifier;
p[0] = jiffies;
p[1] = current->pid;
} else if (o_arg.createmode == NFS4_CREATE_UNCHECKED) {
o_arg.u.attrs = sattr;
}
/* Serialization for the sequence id */
down(&sp->so_sema);
o_arg.seqid = sp->so_seqid;
o_arg.id = sp->so_id;
status = rpc_call_sync(server->client, &msg, 0);
if (status) {
goto out_up;
}
nfs4_increment_seqid(status, sp);
process_cinfo(&d_cinfo, &d_attr);
nfs_refresh_inode(dir, &d_attr);
if (fhandle) {
memset(fhandle, 0, sizeof(*fhandle));
fhandle->size = (o_res.fh.size < NFS_MAXFHSIZE ? o_res.fh.size : NFS_MAXFHSIZE);
memcpy(fhandle->data, o_res.fh.data, fhandle->size);
}
*seqid = 2;
nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "open_confirm");
nfs4_setup_putfh(&compound, fhandle);
nfs4_setup_open_confirm(&compound, stateid);
return nfs4_call_compound(&compound, NULL, 0);
if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) {
struct nfs_open_confirmargs oc_arg = {
.fh = &o_res.fh,
.seqid = sp->so_seqid,
};
struct nfs_open_confirmres oc_res = {
.status = 0,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
.rpc_argp = &oc_arg,
.rpc_resp = &oc_res,
};
memcpy(oc_arg.stateid, o_res.stateid, sizeof(nfs4_stateid));
status = rpc_call_sync(server->client, &msg, 0);
if (status)
goto out_up;
nfs4_increment_seqid(status, sp);
memcpy(sp->so_stateid, oc_res.stateid, sizeof(nfs4_stateid));
} else
memcpy(sp->so_stateid, o_res.stateid, sizeof(nfs4_stateid));
sp->so_flags = flags & O_ACCMODE;
out_up:
up(&sp->so_sema);
out:
*spp = sp;
return status;
}
static int
do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct iattr *sattr, char *stateid)
int
nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct iattr *sattr,
struct nfs4_shareowner *sp)
{
u32 g_bmres[2];
struct nfs4_getattr getattr = {
.gt_bmval = nfs4_fattr_bitmap,
.gt_attrs = fattr,
.gt_bmres = g_bmres,
};
struct nfs_setattrargs arg = {
.fh = fhandle,
.iap = sattr,
.attr = &getattr,
.server = server,
};
struct nfs_setattrres res = {
.attr = &getattr,
.server = server,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
.rpc_argp = &arg,
.rpc_resp = &res,
};
fattr->valid = 0;
if (sp)
memcpy(arg.stateid, sp->so_stateid, sizeof(nfs4_stateid));
else
memcpy(arg.stateid, zero_stateid, sizeof(nfs4_stateid));
return(rpc_call_sync(server->client, &msg, 0));
}
/*
* It is possible for data to be read/written from a mem-mapped file
* after the sys_close call (which hits the vfs layer as a flush).
* This means that we can't safely call nfsv4 close on a file until
* the inode is cleared. This in turn means that we are not good
* NFSv4 citizens - we do not indicate to the server to update the file's
* share state even when we are done with one of the three share
* stateid's in the inode.
*/
int
nfs4_do_close(struct inode *inode, struct nfs4_shareowner *sp)
{
struct nfs4_compound compound;
struct nfs4_op ops[3];
u32 bmres[2];
int status = 0;
struct nfs_closeargs arg = {
.fh = NFS_FH(inode),
};
struct nfs_closeres res = {
.status = 0,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
.rpc_argp = &arg,
.rpc_resp = &res,
};
fattr->valid = 0;
nfs4_setup_compound(&compound, ops, server, "setattr");
nfs4_setup_putfh(&compound, fhandle);
nfs4_setup_setattr(&compound, stateid, sattr);
nfs4_setup_getattr(&compound, fattr, bmres);
return nfs4_call_compound(&compound, NULL, 0);
}
memcpy(arg.stateid, sp->so_stateid, sizeof(nfs4_stateid));
/* Serialization for the sequence id */
down(&sp->so_sema);
arg.seqid = sp->so_seqid,
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
static int
do_close(struct nfs_server *server, struct nfs_fh *fhandle, u32 seqid, char *stateid)
{
struct nfs4_compound compound;
struct nfs4_op ops[2];
nfs4_setup_compound(&compound, ops, server, "close");
nfs4_setup_putfh(&compound, fhandle);
nfs4_setup_close(&compound, stateid, seqid);
return nfs4_call_compound(&compound, NULL, 0);
/* hmm. we are done with the inode, and in the process of freeing
* the shareowner. we keep this around to process errors
*/
nfs4_increment_seqid(status, sp);
up(&sp->so_sema);
return status;
}
static int
......@@ -792,47 +847,54 @@ nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
return nfs4_call_compound(&compound, NULL, 0);
}
/*
* The file is not closed if it is opened due to the a request to change
* the size of the file. The open call will not be needed once the
* VFS layer lookup-intents are implemented.
*
* Close is called when the inode is destroyed.
* If we haven't opened the file for O_WRONLY, we
* need to in the size_change case to obtain a stateid.
*
* Got race?
* Because OPEN is always done by name in nfsv4, it is
* possible that we opened a different file by the same
* name. We can recognize this race condition, but we
* can't do anything about it besides returning an error.
*
* This will be fixed with VFS changes (lookup-intent).
*/
static int
nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
struct iattr *sattr)
{
struct inode * inode = dentry->d_inode;
int size_change = sattr->ia_valid & ATTR_SIZE;
struct nfs_fh throwaway_fh;
u32 seqid;
nfs4_stateid stateid;
struct nfs4_shareowner *sp = NULL;
int status;
fattr->valid = 0;
if (size_change) {
status = do_open(dentry->d_parent->d_inode, &dentry->d_name,
NFS4_SHARE_ACCESS_WRITE, NULL, fattr,
&throwaway_fh, &seqid, stateid);
if (NFS_I(inode)->wo_owner) {
/* file is already open for O_WRONLY */
sp = NFS_I(inode)->wo_owner;
goto no_open;
}
status = nfs4_do_open(dentry->d_parent->d_inode,
&dentry->d_name, O_WRONLY, NULL, fattr,
NULL, &sp);
if (status)
return status;
/*
* Because OPEN is always done by name in nfsv4, it is
* possible that we opened a different file by the same
* name. We can recognize this race condition, but we
* can't do anything about it besides returning an error.
*
* XXX: Should we compare filehandles too, as in
* nfs_find_actor()?
*/
if (fattr->fileid != NFS_FILEID(inode)) {
printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n");
do_close(NFS_SERVER(inode), NFS_FH(inode), seqid, stateid);
return -EIO;
}
}
else
memcpy(stateid, zero_stateid, sizeof(nfs4_stateid));
status = do_setattr(NFS_SERVER(inode), fattr, NFS_FH(inode), sattr, stateid);
if (size_change)
do_close(NFS_SERVER(inode), NFS_FH(inode), seqid, stateid);
no_open:
status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
NFS_FH(inode), sattr, sp);
return status;
}
......@@ -956,6 +1018,7 @@ nfs4_proc_read(struct inode *inode, struct rpc_cred *cred,
struct page *page, int *eofp)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_shareowner *sp;
uint64_t offset = page_offset(page) + base;
struct nfs_readargs arg = {
.fh = NFS_FH(inode),
......@@ -978,6 +1041,17 @@ nfs4_proc_read(struct inode *inode, struct rpc_cred *cred,
int status;
dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
/*
* Try first to use O_RDONLY, then O_RDWR stateid.
*/
sp = nfs4_get_inode_share(inode, O_RDONLY);
if (!sp)
sp = nfs4_get_inode_share(inode, O_RDWR);
if (sp)
memcpy(arg.stateid,sp->so_stateid, sizeof(nfs4_stateid));
else
memcpy(arg.stateid, zero_stateid, sizeof(nfs4_stateid));
fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, flags);
if (!status) {
......@@ -998,6 +1072,7 @@ nfs4_proc_write(struct inode *inode, struct rpc_cred *cred,
struct page *page, struct nfs_writeverf *verf)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_shareowner *sp;
uint64_t offset = page_offset(page) + base;
struct nfs_writeargs arg = {
.fh = NFS_FH(inode),
......@@ -1021,25 +1096,54 @@ nfs4_proc_write(struct inode *inode, struct rpc_cred *cred,
int rpcflags = (flags & NFS_RW_SWAP) ? NFS_RPC_SWAPFLAGS : 0;
dprintk("NFS call write %d @ %Ld\n", count, (long long)offset);
/*
* Try first to use O_WRONLY, then O_RDWR stateid.
*/
sp = nfs4_get_inode_share(inode, O_WRONLY);
if (!sp)
sp = nfs4_get_inode_share(inode, O_RDWR);
if (sp)
memcpy(arg.stateid,sp->so_stateid, sizeof(nfs4_stateid));
else
memcpy(arg.stateid, zero_stateid, sizeof(nfs4_stateid));
fattr->valid = 0;
return rpc_call_sync(server->client, &msg, rpcflags);
}
/*
* Got race?
* We will need to arrange for the VFS layer to provide an atomic open.
* Until then, this create/open method is prone to inefficiency and race
* conditions due to the lookup, create, and open VFS calls from sys_open()
* placed on the wire.
*
* Given the above sorry state of affairs, I'm simply sending an OPEN.
* The file will be opened again in the subsequent VFS open call
* (nfs4_proc_file_open).
*
* The open for read will just hang around to be used by any process that
* opens the file O_RDONLY. This will all be resolved with the VFS changes.
*/
static int
nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int oflags;
u32 seqid;
nfs4_stateid stateid;
int status;
int oflags;
struct nfs4_shareowner *sp = NULL;
int status;
oflags = NFS4_SHARE_ACCESS_READ | O_CREAT | (flags & O_EXCL);
status = do_open(dir, name, oflags, sattr, fattr, fhandle, &seqid, stateid);
oflags = O_RDONLY | O_CREAT | (flags & O_EXCL);
status = nfs4_do_open(dir, name, oflags, sattr, fattr, fhandle, &sp);
if (!status) {
if (flags & O_EXCL)
status = do_setattr(NFS_SERVER(dir), fattr, fhandle, sattr, stateid);
do_close(NFS_SERVER(dir), fhandle, seqid, stateid);
if (flags & O_EXCL) {
status = nfs4_do_setattr(NFS_SERVER(dir), fattr,
fhandle, sattr, sp);
/* XXX should i bother closing the file? */
}
}
return status;
}
......@@ -1368,6 +1472,7 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
};
struct inode *inode = data->inode;
struct nfs_page *req = nfs_list_entry(data->pages.next);
struct nfs4_shareowner *sp;
int flags;
data->args.fh = NFS_FH(inode);
......@@ -1380,6 +1485,19 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
data->res.eof = 0;
data->timestamp = jiffies;
if(req->wb_file) {
unsigned int oflags = req->wb_file->f_flags;
sp = nfs4_get_inode_share(inode, oflags);
} else {
sp = nfs4_get_inode_share(inode, O_RDONLY);
if (!sp)
sp = nfs4_get_inode_share(inode, O_RDWR);
}
if (sp)
memcpy(data->args.stateid,sp->so_stateid, sizeof(nfs4_stateid));
else
memcpy(data->args.stateid, zero_stateid, sizeof(nfs4_stateid));
/* N.B. Do we need to test? Never called for swapfile inode */
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
......@@ -1432,6 +1550,7 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
};
struct inode *inode = data->inode;
struct nfs_page *req = nfs_list_entry(data->pages.next);
struct nfs4_shareowner *sp;
int stable;
int flags;
......@@ -1454,6 +1573,20 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
data->res.verf = &data->verf;
data->timestamp = jiffies;
if(req->wb_file) {
unsigned int oflags = req->wb_file->f_flags;
sp = nfs4_get_inode_share(inode, oflags);
} else {
sp = nfs4_get_inode_share(inode, O_WRONLY);
if (!sp)
sp = nfs4_get_inode_share(inode, O_RDWR);
}
if (sp)
memcpy(data->args.stateid,sp->so_stateid, sizeof(nfs4_stateid));
else
memcpy(data->args.stateid, zero_stateid, sizeof(nfs4_stateid));
/* Set the initial flags for the task. */
flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
......@@ -1560,6 +1693,55 @@ nfs4_proc_renew(struct nfs_server *server)
return rpc_execute(task);
}
/*
* We will need to arrange for the VFS layer to provide an atomic open.
* Until then, this open method is prone to inefficiency and race conditions
* due to the lookup, potential create, and open VFS calls from sys_open()
* placed on the wire.
*/
int
nfs4_proc_file_open(struct inode *inode, struct file *filp)
{
struct dentry *dentry = filp->f_dentry;
struct inode *dir = dentry->d_parent->d_inode;
int flags, status = 0;
dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n",
(int)dentry->d_parent->d_name.len,
dentry->d_parent->d_name.name,
(int)dentry->d_name.len, dentry->d_name.name);
lock_kernel();
/* isn't this done in open_namei? */
if (!S_ISREG(inode->i_mode)) {
status = -EISDIR;
goto out;
}
flags = filp->f_flags & O_ACCMODE;
/*
* Got race??
* We have already opened the file "O_EXCL" in nfs4_proc_create!!
* This ugliness will go away with lookup-intent...
*/
while (!nfs4_get_inode_share(inode, flags)) {
struct nfs4_shareowner *sp = NULL;
status = nfs4_do_open(dir, &dentry->d_name, flags, NULL, NULL, NULL, &sp);
if (status) {
nfs4_put_shareowner(inode,sp);
break;
}
if (nfs4_set_inode_share(inode, sp, flags))
nfs4_put_shareowner(inode,sp);
}
out:
unlock_kernel();
return status;
}
struct nfs_rpc_ops nfs_v4_clientops = {
.version = 4, /* protocol version */
.getroot = nfs4_proc_get_root,
......@@ -1589,6 +1771,7 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.read_setup = nfs4_proc_read_setup,
.write_setup = nfs4_proc_write_setup,
.commit_setup = nfs4_proc_commit_setup,
.file_open = nfs4_proc_file_open,
};
/*
......
......@@ -42,6 +42,16 @@
#include <linux/slab.h>
#include <linux/nfs_fs.h>
/* This protects most of the client-side state. */
static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED;
nfs4_stateid zero_stateid =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
nfs4_stateid one_stateid =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/*
* nfs4_get_client(): returns an empty client structure
* nfs4_put_client(): drops reference to client structure
......@@ -52,26 +62,166 @@
struct nfs4_client *
nfs4_get_client(void)
{
struct nfs4_client *clp;
struct nfs4_client *clp;
if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL))) {
atomic_set(&clp->cl_count, 1);
clp->cl_clientid = 0;
INIT_LIST_HEAD(&clp->cl_lockowners);
}
return clp;
if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
memset(clp, 0, sizeof(nfs4_verifier));
return clp;
}
void
nfs4_put_client(struct nfs4_client *clp)
{
BUG_ON(!clp);
BUG_ON(!atomic_read(&clp->cl_count));
if (atomic_dec_and_test(&clp->cl_count)) {
BUG_ON(!list_empty(&clp->cl_lockowners));
kfree(clp);
BUG_ON(!clp);
kfree(clp);
}
static inline u32
nfs4_alloc_lockowner_id(struct nfs4_client *clp)
{
u32 res;
spin_lock(&state_spinlock);
res = clp->cl_lockowner_id ++;
spin_unlock(&state_spinlock);
return res;
}
/*
* nfs4_get_shareowner(): this is called on the OPEN or CREATE path to
* obtain a new shareowner.
*
* There are three shareowners (open_owner4 in rfc3010) per inode,
* one for each possible combination of share lock access. Since
* Linux does not support the deny access type, there are
* three (not 9) referenced by the nfs_inode:
*
* O_WRONLY: inode->wo_owner
* O_RDONLY: inode->ro_owner
* O_RDWR: inode->rw_owner
*
* We create a new shareowner the first time a file is OPENed with
* one of the above shares. All other OPENs with a similar
* share use the single stateid associated with the inode.
*
*/
struct nfs4_shareowner *
nfs4_get_shareowner(struct inode *dir)
{
struct nfs4_client *clp;
struct nfs4_shareowner *sp;
sp = kmalloc(sizeof(*sp),GFP_KERNEL);
if (!sp)
return NULL;
clp = (NFS_SB(dir->i_sb))->nfs4_state;
BUG_ON(!clp);
init_MUTEX(&sp->so_sema);
sp->so_seqid = 0; /* arbitrary */
memset(sp->so_stateid, 0, sizeof(nfs4_stateid));
sp->so_id = nfs4_alloc_lockowner_id(clp);
return sp;
}
/*
* Called for each non-null inode shareowner in nfs_clear_inode,
* or if nfs4_do_open fails.
*/
void
nfs4_put_shareowner(struct inode *inode, struct nfs4_shareowner *sp)
{
if (!sp)
return;
if (sp->so_flags & O_ACCMODE)
nfs4_do_close(inode, sp);
kfree(sp);
}
/*
* Called with sp->so_sema held.
*
* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
* failed with a seqid incrementing error -
* see comments nfs_fs.h:seqid_mutating_error()
*/
void
nfs4_increment_seqid(u32 status, struct nfs4_shareowner *sp)
{
if (status == NFS_OK || seqid_mutating_err(status))
sp->so_seqid++;
}
/*
* Called by nfs4_proc_open to set the appropriate stateid
*/
int
nfs4_set_inode_share(struct inode * inode, struct nfs4_shareowner *sp, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
if (!nfsi->ro_owner) {
nfsi->ro_owner = sp;
return 0;
}
break;
case O_WRONLY:
if (!nfsi->wo_owner) {
nfsi->wo_owner = sp;
return 0;
}
break;
case O_RDWR:
if (!nfsi->rw_owner) {
nfsi->rw_owner = sp;
return 0;
}
}
return -EBUSY;
}
/*
* Boolean test to determine if an OPEN call goes on the wire.
*
* Called by nfs4_proc_open.
*/
int
nfs4_test_shareowner(struct inode *inode, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
if(nfsi->ro_owner)
return 0;
break;
case O_WRONLY:
if(nfsi->wo_owner)
return 0;
break;
case O_RDWR:
if(nfsi->rw_owner)
return 0;
}
return 1;
}
struct nfs4_shareowner *
nfs4_get_inode_share(struct inode * inode, unsigned int open_flags)
{
struct nfs_inode *nfsi = NFS_I(inode);
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
return nfsi->ro_owner;
case O_WRONLY:
return nfsi->wo_owner;
case O_RDWR:
return nfsi->rw_owner;
}
/* Duh gcc warning if we don't... */
return NULL;
}
/*
......
......@@ -8,7 +8,7 @@
*
* Kendrick Smith <kmsmith@umich.edu>
* 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:
......@@ -50,12 +50,7 @@
#include <linux/nfs.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
/* Emperically, it seems that the NFS client gets confused if
* cookies larger than this are returned -- presumably a
* signedness issue?
*/
#define COOKIE_MAX 0x7fffffff
#include <linux/nfs_idmap.h>
#define NFSDBG_FACILITY NFSDBG_XDR
......@@ -78,6 +73,17 @@ extern int nfs_stat_to_errno(int);
#define encode_putfh_maxsz op_encode_hdr_maxsz + 1 + \
(NFS4_FHSIZE >> 2)
#define decode_putfh_maxsz op_decode_hdr_maxsz
#define encode_getfh_maxsz op_encode_hdr_maxsz
#define decode_getfh_maxsz op_decode_hdr_maxsz + 1 + \
(NFS4_FHSIZE >> 2)
#define encode_getattr_maxsz op_encode_hdr_maxsz + 3
#define nfs4_fattr_bitmap_maxsz 26 + 2 * ((NFS4_MAXNAMLEN +1) >> 2)
#define decode_getattr_maxsz op_decode_hdr_maxsz + 3 + \
nfs4_fattr_bitmap_maxsz
#define encode_savefh_maxsz op_encode_hdr_maxsz
#define decode_savefh_maxsz op_decode_hdr_maxsz
#define encode_restorefh_maxsz op_encode_hdr_maxsz
#define decode_restorefh_maxsz op_decode_hdr_maxsz
#define encode_read_getattr_maxsz op_encode_hdr_maxsz + 2
#define decode_read_getattr_maxsz op_decode_hdr_maxsz + 8
#define encode_pre_write_getattr_maxsz op_encode_hdr_maxsz + 2
......@@ -115,6 +121,44 @@ extern int nfs_stat_to_errno(int);
decode_pre_write_getattr_maxsz + \
op_decode_hdr_maxsz + 2 + \
decode_post_write_getattr_maxsz
#define NFS4_enc_open_sz compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_savefh_maxsz + \
op_encode_hdr_maxsz + \
13 + 3 + 2 + 64 + \
encode_getattr_maxsz + \
encode_getfh_maxsz + \
encode_restorefh_maxsz + \
encode_getattr_maxsz
#define NFS4_dec_open_sz compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
decode_savefh_maxsz + \
op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \
decode_getattr_maxsz + \
decode_getfh_maxsz + \
decode_restorefh_maxsz + \
decode_getattr_maxsz
#define NFS4_enc_open_confirm_sz \
compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 5
#define NFS4_dec_open_confirm_sz compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4
#define NFS4_enc_close_sz compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 5
#define NFS4_dec_close_sz compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4
#define NFS4_enc_setattr_sz compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 4 + \
nfs4_fattr_bitmap_maxsz + \
encode_getattr_maxsz
#define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 3
static struct {
......@@ -190,30 +234,9 @@ encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
return 0;
}
/*
* FIXME: The following dummy entries will be replaced once the userland
* upcall gets in...
*/
static int
encode_uid(char *p, uid_t uid)
{
strcpy(p, "nobody");
return 6;
}
/*
* FIXME: The following dummy entries will be replaced once the userland
* upcall gets in...
*/
static int
encode_gid(char *p, gid_t gid)
{
strcpy(p, "nobody");
return 6;
}
static int
encode_attrs(struct xdr_stream *xdr, struct iattr *iap)
encode_attrs(struct xdr_stream *xdr, struct iattr *iap,
struct nfs_server *server)
{
char owner_name[256];
char owner_group[256];
......@@ -241,20 +264,27 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap)
if (iap->ia_valid & ATTR_MODE)
len += 4;
if (iap->ia_valid & ATTR_UID) {
status = owner_namelen = encode_uid(owner_name, iap->ia_uid);
status = nfs_idmap_name(server, IDMAP_TYPE_USER,
iap->ia_uid, owner_name, &owner_namelen);
if (status < 0) {
printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n",
iap->ia_uid);
goto out;
/* XXX */
strcpy(owner_name, "nobody");
owner_namelen = sizeof("nobody") - 1;
/* goto out; */
}
len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
}
if (iap->ia_valid & ATTR_GID) {
status = owner_grouplen = encode_gid(owner_group, iap->ia_gid);
status = nfs_idmap_name(server, IDMAP_TYPE_GROUP,
iap->ia_gid, owner_group, &owner_grouplen);
if (status < 0) {
printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n",
iap->ia_gid);
goto out;
strcpy(owner_group, "nobody");
owner_grouplen = sizeof("nobody") - 1;
/* goto out; */
}
len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
}
......@@ -348,14 +378,14 @@ encode_access(struct xdr_stream *xdr, struct nfs4_access *access)
}
static int
encode_close(struct xdr_stream *xdr, struct nfs4_close *close)
encode_close(struct xdr_stream *xdr, struct nfs_closeargs *arg)
{
uint32_t *p;
RESERVE_SPACE(8+sizeof(nfs4_stateid));
WRITE32(OP_CLOSE);
WRITE32(close->cl_seqid);
WRITEMEM(close->cl_stateid, sizeof(nfs4_stateid));
WRITE32(arg->seqid);
WRITEMEM(arg->stateid, sizeof(nfs4_stateid));
return 0;
}
......@@ -374,7 +404,8 @@ encode_commit(struct xdr_stream *xdr, struct nfs_writeargs *args)
}
static int
encode_create(struct xdr_stream *xdr, struct nfs4_create *create)
encode_create(struct xdr_stream *xdr, struct nfs4_create *create,
struct nfs_server *server)
{
uint32_t *p;
......@@ -403,7 +434,7 @@ encode_create(struct xdr_stream *xdr, struct nfs4_create *create)
WRITE32(create->cr_namelen);
WRITEMEM(create->cr_name, create->cr_namelen);
return encode_attrs(xdr, create->cr_attrs);
return encode_attrs(xdr, create->cr_attrs, server);
}
static int
......@@ -509,69 +540,76 @@ encode_lookup(struct xdr_stream *xdr, struct nfs4_lookup *lookup)
}
static int
encode_open(struct xdr_stream *xdr, struct nfs4_open *open)
encode_open(struct xdr_stream *xdr, struct nfs_openargs *arg)
{
static int global_id = 0;
int id = global_id++;
int status;
uint32_t *p;
/* seqid, share_access, share_deny, clientid, ownerlen, owner, opentype */
/*
* opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
* owner 4, opentype 4 = 36
*/
RESERVE_SPACE(36);
WRITE32(OP_OPEN);
WRITE32(0); /* seqid */
WRITE32(open->op_share_access);
WRITE32(0); /* for us, share_deny== 0 always */
WRITE64(open->op_client_state->cl_clientid);
WRITE32(arg->seqid);
switch (arg->share_access) {
case O_RDONLY:
WRITE32(NFS4_SHARE_ACCESS_READ);
break;
case O_WRONLY:
WRITE32(NFS4_SHARE_ACCESS_WRITE);
break;
case O_RDWR:
WRITE32(NFS4_SHARE_ACCESS_BOTH);
}
WRITE32(0); /* for linux, share_deny = 0 always */
WRITE64(arg->clientid);
WRITE32(4);
WRITE32(id);
WRITE32(open->op_opentype);
if (open->op_opentype == NFS4_OPEN_CREATE) {
if (open->op_createmode == NFS4_CREATE_EXCLUSIVE) {
RESERVE_SPACE(4+sizeof(nfs4_verifier));
WRITE32(open->op_createmode);
WRITEMEM(open->op_verifier, sizeof(nfs4_verifier));
WRITE32(arg->id);
WRITE32(arg->opentype);
if (arg->opentype == NFS4_OPEN_CREATE) {
if (arg->createmode == NFS4_CREATE_EXCLUSIVE) {
RESERVE_SPACE(12);
WRITE32(arg->createmode);
WRITEMEM(arg->u.verifier, sizeof(nfs4_verifier));
}
else if (open->op_attrs) {
else if (arg->u.attrs) {
RESERVE_SPACE(4);
WRITE32(open->op_createmode);
if ((status = encode_attrs(xdr, open->op_attrs)))
WRITE32(arg->createmode);
if ((status = encode_attrs(xdr, arg->u.attrs, arg->server)))
return status;
}
else {
RESERVE_SPACE(12);
WRITE32(open->op_createmode);
WRITE32(arg->createmode);
WRITE32(0);
WRITE32(0);
}
}
RESERVE_SPACE(8 + open->op_name->len);
RESERVE_SPACE(8 + arg->name->len);
WRITE32(NFS4_OPEN_CLAIM_NULL);
WRITE32(open->op_name->len);
WRITEMEM(open->op_name->name, open->op_name->len);
WRITE32(arg->name->len);
WRITEMEM(arg->name->name, arg->name->len);
return 0;
}
static int
encode_open_confirm(struct xdr_stream *xdr, struct nfs4_open_confirm *open_confirm)
encode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmargs *arg)
{
uint32_t *p;
/*
* Note: In this "stateless" implementation, the OPEN_CONFIRM
* seqid is always equal to 1.
*/
RESERVE_SPACE(8+sizeof(nfs4_stateid));
WRITE32(OP_OPEN_CONFIRM);
WRITEMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid));
WRITE32(1);
WRITEMEM(arg->stateid, sizeof(nfs4_stateid));
WRITE32(arg->seqid);
return 0;
}
static int
encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh)
{
......@@ -604,10 +642,7 @@ encode_read(struct xdr_stream *xdr, struct nfs_readargs *args)
RESERVE_SPACE(32);
WRITE32(OP_READ);
WRITE32(0); /* all-zero stateid! */
WRITE32(0);
WRITE32(0);
WRITE32(0);
WRITEMEM(args->stateid, sizeof(nfs4_stateid));
WRITE64(args->offset);
WRITE32(args->count);
......@@ -727,16 +762,17 @@ encode_savefh(struct xdr_stream *xdr)
}
static int
encode_setattr(struct xdr_stream *xdr, struct nfs4_setattr *setattr)
encode_setattr(struct xdr_stream *xdr, struct nfs_setattrargs *arg,
struct nfs_server *server)
{
int status;
uint32_t *p;
RESERVE_SPACE(4+sizeof(nfs4_stateid));
WRITE32(OP_SETATTR);
WRITEMEM(setattr->st_stateid, sizeof(nfs4_stateid));
WRITEMEM(arg->stateid, sizeof(nfs4_stateid));
if ((status = encode_attrs(xdr, setattr->st_iap)))
if ((status = encode_attrs(xdr, arg->iap, server)))
return status;
return 0;
......@@ -790,10 +826,7 @@ encode_write(struct xdr_stream *xdr, struct nfs_writeargs *args)
RESERVE_SPACE(36);
WRITE32(OP_WRITE);
WRITE32(0xffffffff); /* magic stateid -1 */
WRITE32(0xffffffff);
WRITE32(0xffffffff);
WRITE32(0xffffffff);
WRITEMEM(args->stateid, sizeof(nfs4_stateid));
WRITE64(args->offset);
WRITE32(args->stable);
WRITE32(args->count);
......@@ -821,11 +854,8 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_ACCESS:
status = encode_access(xdr, &cp->ops[i].u.access);
break;
case OP_CLOSE:
status = encode_close(xdr, &cp->ops[i].u.close);
break;
case OP_CREATE:
status = encode_create(xdr, &cp->ops[i].u.create);
status = encode_create(xdr, &cp->ops[i].u.create, cp->server);
break;
case OP_GETATTR:
status = encode_getattr(xdr, &cp->ops[i].u.getattr);
......@@ -839,12 +869,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_LOOKUP:
status = encode_lookup(xdr, &cp->ops[i].u.lookup);
break;
case OP_OPEN:
status = encode_open(xdr, &cp->ops[i].u.open);
break;
case OP_OPEN_CONFIRM:
status = encode_open_confirm(xdr, &cp->ops[i].u.open_confirm);
break;
case OP_PUTFH:
status = encode_putfh(xdr, cp->ops[i].u.putfh.pf_fhandle);
break;
......@@ -872,9 +896,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_SAVEFH:
status = encode_savefh(xdr);
break;
case OP_SETATTR:
status = encode_setattr(xdr, &cp->ops[i].u.setattr);
break;
case OP_SETCLIENTID:
status = encode_setclientid(xdr, &cp->ops[i].u.setclientid);
break;
......@@ -909,6 +930,87 @@ nfs4_xdr_enc_compound(struct rpc_rqst *req, uint32_t *p, struct nfs4_compound *c
cp->timestamp = jiffies;
return status;
}
/*
* Encode a CLOSE request
*/
static int
nfs4_xdr_enc_close(struct rpc_rqst *req, uint32_t *p, struct nfs_closeargs *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
if(status)
goto out;
status = encode_close(&xdr, args);
out:
return status;
}
/*
* Encode an OPEN request
*/
static int
nfs4_xdr_enc_open(struct rpc_rqst *req, uint32_t *p, struct nfs_openargs *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 7,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
if (status)
goto out;
status = encode_savefh(&xdr);
if (status)
goto out;
status = encode_open(&xdr, args);
if (status)
goto out;
status = encode_getattr(&xdr, args->f_getattr);
if (status)
goto out;
status = encode_getfh(&xdr);
if (status)
goto out;
status = encode_restorefh(&xdr);
if (status)
goto out;
status = encode_getattr(&xdr, args->d_getattr);
out:
return status;
}
/*
* Encode an OPEN_CONFIRM request
*/
static int
nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_open_confirmargs *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
if(status)
goto out;
status = encode_open_confirm(&xdr, args);
out:
return status;
}
/*
* Encode a READ request
......@@ -945,6 +1047,32 @@ nfs4_xdr_enc_read(struct rpc_rqst *req, uint32_t *p, struct nfs_readargs *args)
return status;
}
/*
* Encode an SETATTR request
*/
static int
nfs4_xdr_enc_setattr(struct rpc_rqst *req, uint32_t *p, struct nfs_setattrargs *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 3,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
if(status)
goto out;
status = encode_setattr(&xdr, args, args->server);
if(status)
goto out;
status = encode_getattr(&xdr, args->attr);
out:
return status;
}
/*
* Encode a WRITE request
*/
......@@ -974,7 +1102,7 @@ nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args
}
/*
* Encode a COMMIT request
* a COMMIT request
*/
static int
nfs4_xdr_enc_commit(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args)
......@@ -1044,28 +1172,6 @@ xdr_error: \
} \
} while (0)
/*
* FIXME: The following dummy entry will be replaced once the userland
* upcall gets in...
*/
static int
decode_uid(char *p, uint32_t len, uid_t *uid)
{
*uid = -2;
return 0;
}
/*
* FIXME: The following dummy entry will be replaced once the userland
* upcall gets in...
*/
static int
decode_gid(char *p, uint32_t len, gid_t *gid)
{
*gid = -2;
return 0;
}
static int
decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
......@@ -1139,7 +1245,7 @@ decode_access(struct xdr_stream *xdr, struct nfs4_access *access)
}
static int
decode_close(struct xdr_stream *xdr, struct nfs4_close *close)
decode_close(struct xdr_stream *xdr, struct nfs_closeres *res)
{
uint32_t *p;
int status;
......@@ -1148,7 +1254,7 @@ decode_close(struct xdr_stream *xdr, struct nfs4_close *close)
if (status)
return status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(close->cl_stateid, sizeof(nfs4_stateid));
COPYMEM(res->stateid, sizeof(nfs4_stateid));
return 0;
}
......@@ -1190,7 +1296,8 @@ extern uint32_t nfs4_fsstat_bitmap[2];
extern uint32_t nfs4_pathconf_bitmap[2];
static int
decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr)
decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr,
struct nfs_server *server)
{
struct nfs_fattr *nfp = getattr->gt_attrs;
struct nfs_fsstat *fsstat = getattr->gt_fsstat;
......@@ -1354,35 +1461,39 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr)
}
if (bmval1 & FATTR4_WORD1_OWNER) {
READ_BUF(4);
len += 4;
READ32(dummy32); /* name length */
if (dummy32 > XDR_MAX_NETOBJ) {
len += 4;
READ32(dummy32); /* name length */
if (dummy32 > XDR_MAX_NETOBJ) {
dprintk("read_attrs: name too long!\n");
goto xdr_error;
}
READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2);
if ((status = decode_uid((char *)p, dummy32, &nfp->uid))) {
dprintk("read_attrs: gss_get_num failed!\n");
goto out;
}
dprintk("read_attrs: uid=%d\n", (int)nfp->uid);
goto xdr_error;
}
READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2);
if ((status = nfs_idmap_id(server, IDMAP_TYPE_USER,
(char *)p, len, &nfp->uid)) == -1) {
dprintk("read_attrs: gss_get_num failed!\n");
/* goto out; */
nfp->uid = -2;
}
dprintk("read_attrs: uid=%d\n", (int)nfp->uid);
}
if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
READ_BUF(4);
len += 4;
READ32(dummy32);
if (dummy32 > XDR_MAX_NETOBJ) {
dprintk("read_attrs: name too long!\n");
goto xdr_error;
}
READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2);
if ((status = decode_gid((char *)p, dummy32, &nfp->gid))) {
dprintk("read_attrs: gss_get_num failed!\n");
goto out;
}
dprintk("read_attrs: gid=%d\n", (int)nfp->gid);
len += 4;
READ32(dummy32);
if (dummy32 > XDR_MAX_NETOBJ) {
dprintk("read_attrs: name too long!\n");
goto xdr_error;
}
READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2);
if ((status = nfs_idmap_id(server, IDMAP_TYPE_GROUP,
(char *)p, len, &nfp->gid)) == -1) {
dprintk("read_attrs: gss_get_num failed!\n");
nfp->gid = -2;
/* goto out; */
}
dprintk("read_attrs: gid=%d\n", (int)nfp->gid);
}
if (bmval1 & FATTR4_WORD1_RAWDEV) {
uint32_t major, minor;
......@@ -1617,49 +1728,49 @@ decode_lookup(struct xdr_stream *xdr)
}
static int
decode_open(struct xdr_stream *xdr, struct nfs4_open *open)
decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
{
uint32_t *p;
uint32_t bmlen, delegation_type;
int status;
status = decode_op_hdr(xdr, OP_OPEN);
if (status)
return status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(open->op_stateid, sizeof(nfs4_stateid));
uint32_t *p;
uint32_t bmlen, delegation_type;
int status;
decode_change_info(xdr, open->op_cinfo);
status = decode_op_hdr(xdr, OP_OPEN);
if (status)
return status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(res->stateid, sizeof(nfs4_stateid));
READ_BUF(8);
READ32(*open->op_rflags);
READ32(bmlen);
if (bmlen > 10)
goto xdr_error;
READ_BUF((bmlen << 2) + 4);
p += bmlen;
READ32(delegation_type);
if (delegation_type != NFS4_OPEN_DELEGATE_NONE)
goto xdr_error;
DECODE_TAIL;
decode_change_info(xdr, res->cinfo);
READ_BUF(8);
READ32(res->rflags);
READ32(bmlen);
if (bmlen > 10)
goto xdr_error;
READ_BUF((bmlen << 2) + 4);
p += bmlen;
READ32(delegation_type);
if (delegation_type != NFS4_OPEN_DELEGATE_NONE)
goto xdr_error;
DECODE_TAIL;
}
static int
decode_open_confirm(struct xdr_stream *xdr, struct nfs4_open_confirm *open_confirm)
decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
{
uint32_t *p;
int status;
uint32_t *p;
status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
if (status)
return status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid));
return 0;
res->status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
if (res->status)
return res->status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(res->stateid, sizeof(nfs4_stateid));
return 0;
}
static int
decode_putfh(struct xdr_stream *xdr)
{
......@@ -1875,7 +1986,7 @@ decode_savefh(struct xdr_stream *xdr)
}
static int
decode_setattr(struct xdr_stream *xdr)
decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
{
uint32_t *p;
uint32_t bmlen;
......@@ -1986,14 +2097,11 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_ACCESS:
status = decode_access(xdr, &op->u.access);
break;
case OP_CLOSE:
status = decode_close(xdr, &op->u.close);
break;
case OP_CREATE:
status = decode_create(xdr, &op->u.create);
break;
case OP_GETATTR:
status = decode_getattr(xdr, &op->u.getattr);
status = decode_getattr(xdr, &op->u.getattr, cp->server);
break;
case OP_GETFH:
status = decode_getfh(xdr, &op->u.getfh);
......@@ -2004,12 +2112,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_LOOKUP:
status = decode_lookup(xdr);
break;
case OP_OPEN:
status = decode_open(xdr, &op->u.open);
break;
case OP_OPEN_CONFIRM:
status = decode_open_confirm(xdr, &op->u.open_confirm);
break;
case OP_PUTFH:
status = decode_putfh(xdr);
break;
......@@ -2037,9 +2139,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs
case OP_SAVEFH:
status = decode_savefh(xdr);
break;
case OP_SETATTR:
status = decode_setattr(xdr);
break;
case OP_SETCLIENTID:
status = decode_setclientid(xdr, &op->u.setclientid);
break;
......@@ -2081,6 +2180,118 @@ nfs4_xdr_dec_compound(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_compound
return status;
}
/*
* Decode CLOSE response
*/
static int
nfs4_xdr_dec_close(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status)
goto out;
status = decode_putfh(&xdr);
if (status)
goto out;
status = decode_close(&xdr, res);
out:
return status;
}
/*
* Decode OPEN response
*/
static int
nfs4_xdr_dec_open(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
struct nfs4_getfh gfh = {
.gf_fhandle = &res->fh,
};
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status)
goto out;
status = decode_putfh(&xdr);
if (status)
goto out;
status = decode_savefh(&xdr);
if (status)
goto out;
status = decode_open(&xdr, res);
if (status)
goto out;
status = decode_getattr(&xdr, res->f_getattr, res->server);
if (status)
goto out;
status = decode_getfh(&xdr, &gfh);
if (status)
goto out;
status = decode_restorefh(&xdr);
if (status)
goto out;
status = decode_getattr(&xdr, res->d_getattr, res->server);
if (status)
goto out;
out:
return status;
}
/*
* Decode OPEN_CONFIRM response
*/
static int
nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_open_confirmres *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status)
goto out;
status = decode_putfh(&xdr);
if (status)
goto out;
status = decode_open_confirm(&xdr, res);
out:
return status;
}
/*
* Decode SETATTR response
*/
static int
nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_setattrres *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status)
goto out;
status = decode_putfh(&xdr);
if (status)
goto out;
status = decode_setattr(&xdr, res);
if (status)
goto out;
status = decode_getattr(&xdr, res->attr, res->server);
out:
return status;
}
/*
* Decode Read response
*/
......@@ -2188,9 +2399,6 @@ nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
entry->name = (const char *) p;
p += XDR_QUADLEN(entry->len);
if (entry->cookie > COOKIE_MAX)
entry->cookie = COOKIE_MAX;
/*
* In case the server doesn't return an inode number,
* we fake one here. (We don't use inode number 0,
......@@ -2213,7 +2421,7 @@ nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
#endif
#define PROC(proc, argtype, restype) \
[NFSPROC4_CLNT_##proc] = { \
[NFSPROC4_CLNT_##proc] = { \
.p_proc = NFSPROC4_COMPOUND, \
.p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs4_xdr_##restype, \
......@@ -2225,6 +2433,10 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(READ, enc_read, dec_read),
PROC(WRITE, enc_write, dec_write),
PROC(COMMIT, enc_commit, dec_commit),
PROC(OPEN, enc_open, dec_open),
PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm),
PROC(CLOSE, enc_close, dec_close),
PROC(SETATTR, enc_setattr, dec_setattr),
};
struct rpc_version nfs_version4 = {
......
......@@ -206,6 +206,10 @@ enum {
NFSPROC4_CLNT_READ,
NFSPROC4_CLNT_WRITE,
NFSPROC4_CLNT_COMMIT,
NFSPROC4_CLNT_OPEN,
NFSPROC4_CLNT_OPEN_CONFIRM,
NFSPROC4_CLNT_CLOSE,
NFSPROC4_CLNT_SETATTR,
};
#endif
......
......@@ -155,6 +155,13 @@ struct nfs_inode {
wait_queue_head_t nfs_i_wait;
#ifdef CONFIG_NFS_V4
/* NFSv4 state */
struct nfs4_shareowner *ro_owner;
struct nfs4_shareowner *wo_owner;
struct nfs4_shareowner *rw_owner;
#endif /* CONFIG_NFS_V4*/
struct inode vfs_inode;
};
......@@ -435,28 +442,75 @@ extern void * nfs_root_data(void);
#define NFS_JUKEBOX_RETRY_TIME (5 * HZ)
#ifdef CONFIG_NFS_V4
/*
* In a seqid-mutating op, this macro controls which error return
* values trigger incrementation of the seqid.
*
* from rfc 3010:
* The client MUST monotonically increment the sequence number for the
* CLOSE, LOCK, LOCKU, OPEN, OPEN_CONFIRM, and OPEN_DOWNGRADE
* operations. This is true even in the event that the previous
* operation that used the sequence number received an error. The only
* exception to this rule is if the previous operation received one of
* the following errors: NFSERR_STALE_CLIENTID, NFSERR_STALE_STATEID,
* NFSERR_BAD_STATEID, NFSERR_BAD_SEQID, NFSERR_BADXDR,
* NFSERR_RESOURCE, NFSERR_NOFILEHANDLE.
*
*/
#define seqid_mutating_err(err) \
(((err) != NFSERR_STALE_CLIENTID) && \
((err) != NFSERR_STALE_STATEID) && \
((err) != NFSERR_BAD_STATEID) && \
((err) != NFSERR_BAD_SEQID) && \
((err) != NFSERR_BAD_XDR) && \
((err) != NFSERR_RESOURCE) && \
((err) != NFSERR_NOFILEHANDLE))
struct nfs4_client {
atomic_t cl_count; /* refcount */
u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm;
nfs4_verifier cl_confirm;
/*
* Starts a list of lockowners, linked through lo_list.
*/
struct list_head cl_lockowners; /* protected by state_spinlock */
u32 cl_lockowner_id;
};
/*
* The ->so_sema is held during all shareowner seqid-mutating operations:
* OPEN, OPEN_DOWNGRADE, and CLOSE.
* Its purpose is to properly serialize so_seqid, as mandated by
* the protocol.
*/
struct nfs4_shareowner {
u32 so_id; /* 32-bit identifier, unique */
struct semaphore so_sema;
u32 so_seqid; /* protected by so_sema */
nfs4_stateid so_stateid; /* protected by so_sema */
unsigned int so_flags; /* protected by so_sema */
};
/* nfs4proc.c */
extern int nfs4_proc_renew(struct nfs_server *server);
extern int nfs4_do_close(struct inode *inode, struct nfs4_shareowner *sp);
/* nfs4renewd.c */
extern int nfs4_init_renewd(struct nfs_server *server);
#endif /* CONFIG_NFS_V4 */
#ifdef CONFIG_NFS_V4
/* nfs4state.c */
extern struct nfs4_client *nfs4_get_client(void);
extern void nfs4_put_client(struct nfs4_client *clp);
extern struct nfs4_shareowner * nfs4_get_shareowner(struct inode *inode);
void nfs4_put_shareowner(struct inode *inode, struct nfs4_shareowner *sp);
extern int nfs4_set_inode_share(struct inode * inode,
struct nfs4_shareowner *sp, unsigned int flags);
extern void nfs4_increment_seqid(u32 status, struct nfs4_shareowner *sp);
extern int nfs4_test_shareowner(struct inode *inode, unsigned int open_flags);
struct nfs4_shareowner * nfs4_get_inode_share(struct inode * inode, unsigned int open_flags);
struct nfs4_mount_data;
static inline int
......@@ -481,6 +535,7 @@ destroy_nfsv4_state(struct nfs_server *server)
#else
#define create_nfsv4_state(server, data) 0
#define destroy_nfsv4_state(server) do { } while (0)
#define nfs4_put_shareowner(inode, owner) do { } while (0)
#endif
#endif /* __KERNEL__ */
......
......@@ -36,6 +36,7 @@ struct nfs_server {
struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */
unsigned long lease_time; /* in jiffies */
unsigned long last_renewal; /* in jiffies */
void *idmap;
#endif
};
......
/*
* include/linux/nfs_idmap.h
*
* UID and GID to name mapping for clients.
*
* Copyright (c) 2002 The Regents of the University of Michigan.
* All rights reserved.
*
* Marius Aamodt Eriksen <marius@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.
*/
#ifndef NFS_IDMAP_H
#define NFS_IDMAP_H
/* XXX from bits/utmp.h */
#define IDMAP_NAMESZ 128
#define IDMAP_TYPE_USER 0
#define IDMAP_TYPE_GROUP 1
#define IDMAP_CONV_IDTONAME 0
#define IDMAP_CONV_NAMETOID 1
#define IDMAP_STATUS_INVALIDMSG 0x01
#define IDMAP_STATUS_AGAIN 0x02
#define IDMAP_STATUS_LOOKUPFAIL 0x04
#define IDMAP_STATUS_SUCCESS 0x08
struct idmap_msg {
u_int8_t im_type;
u_int8_t im_conv;
char im_name[IDMAP_NAMESZ];
u_int32_t im_id;
u_int8_t im_status;
};
#ifdef __KERNEL__
void *nfs_idmap_new(struct nfs_server *);
void nfs_idmap_delete(struct nfs_server *);
int nfs_idmap_id(struct nfs_server *, u_int8_t, char *, u_int, uid_t *);
int nfs_idmap_name(struct nfs_server *, u_int8_t, uid_t, char *, u_int *);
#endif /* __KERNEL__ */
#endif /* NFS_IDMAP_H */
......@@ -87,6 +87,67 @@ struct nfs_pathconf {
__u32 max_namelen; /* max name length */
};
/*
* Arguments to the open call.
*/
struct nfs_openargs {
struct nfs_fh * fh;
__u32 seqid;
__u32 share_access;
__u64 clientid;
__u32 id;
__u32 opentype;
__u32 createmode;
union {
struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */
} u;
struct qstr * name;
struct nfs4_getattr * f_getattr;
struct nfs4_getattr * d_getattr;
struct nfs_server * server; /* Needed for ID mapping */
};
struct nfs_openres {
__u32 status;
nfs4_stateid stateid;
struct nfs_fh fh;
struct nfs4_change_info * cinfo;
__u32 rflags;
struct nfs4_getattr * f_getattr;
struct nfs4_getattr * d_getattr;
struct nfs_server * server;
};
/*
* Arguments to the open_confirm call.
*/
struct nfs_open_confirmargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u32 seqid;
};
struct nfs_open_confirmres {
__u32 status;
nfs4_stateid stateid;
};
/*
* Arguments to the close call.
*/
struct nfs_closeargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u32 seqid;
};
struct nfs_closeres {
__u32 status;
nfs4_stateid stateid;
};
/*
* Arguments to the read call.
*/
......@@ -98,6 +159,7 @@ struct nfs_pathconf {
struct nfs_readargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u64 offset;
__u32 count;
unsigned int pgbase;
......@@ -120,6 +182,7 @@ struct nfs_readres {
struct nfs_writeargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u64 offset;
__u32 count;
enum nfs3_stable_how stable;
......@@ -182,6 +245,19 @@ struct nfs_renameargs {
unsigned int tolen;
};
struct nfs_setattrargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
struct iattr * iap;
struct nfs4_getattr * attr;
struct nfs_server * server; /* Needed for name mapping */
};
struct nfs_setattrres {
struct nfs4_getattr * attr;
struct nfs_server * server;
};
struct nfs_linkargs {
struct nfs_fh * fromfh;
struct nfs_fh * tofh;
......@@ -597,6 +673,7 @@ struct nfs_rpc_ops {
void (*read_setup) (struct nfs_read_data *, unsigned int count);
void (*write_setup) (struct nfs_write_data *, unsigned int count, int how);
void (*commit_setup) (struct nfs_write_data *, u64 start, u32 len, int how);
int (*file_open) (struct inode *, struct file *);
};
/*
......
......@@ -625,7 +625,8 @@ skb_read_bits(skb_reader_t *desc, void *to, size_t len)
{
if (len > desc->count)
len = desc->count;
skb_copy_bits(desc->skb, desc->offset, to, len);
if (skb_copy_bits(desc->skb, desc->offset, to, len))
return 0;
desc->count -= len;
desc->offset += len;
return len;
......@@ -669,11 +670,15 @@ csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0);
desc.csum = csum_block_add(desc.csum, csum2, desc.offset);
}
if (desc.count)
return -1;
if ((unsigned short)csum_fold(desc.csum))
return -1;
return 0;
no_checksum:
xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits);
if (desc.count)
return -1;
return 0;
}
......@@ -750,7 +755,8 @@ tcp_copy_data(skb_reader_t *desc, void *p, size_t len)
{
if (len > desc->count)
len = desc->count;
skb_copy_bits(desc->skb, desc->offset, p, len);
if (skb_copy_bits(desc->skb, desc->offset, p, len))
return 0;
desc->offset += len;
desc->count -= len;
return len;
......
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