Commit 0646a4e4 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 7d22476d b4a558fd
......@@ -1415,8 +1415,8 @@ config NFS_V3
bool "Provide NFSv3 client support"
depends on NFS_FS
help
Say Y here if you want your NFS client to be able to speak the newer
version 3 of the NFS protocol.
Say Y here if you want your NFS client to be able to speak version
3 of the NFS protocol.
If unsure, say Y.
......@@ -1560,6 +1560,22 @@ config RPCSEC_GSS_KRB5
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
tristate "SMB file system support (to mount Windows shares etc.)"
depends on INET
......
......@@ -237,8 +237,13 @@ nlmsvc_delete_block(struct nlm_block *block, int unlock)
/* Remove block from list */
nlmsvc_remove_block(block);
posix_unblock_lock(file->f_file, fl);
block->b_granted = 0;
if (fl->fl_next)
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;
}
/* If the block is in the middle of a GRANT callback,
* don't kill it yet. */
......
......@@ -67,7 +67,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
down(&nlm_file_sema);
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;
dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n",
......
......@@ -9,6 +9,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.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 \
idmap.o
delegation.o idmap.o \
callback.o callback_xdr.o callback_proc.o
nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
nfs-objs := $(nfs-y)
/*
* linux/fs/nfs/callback.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback handling
*/
#include <linux/config.h>
#include <linux/completion.h>
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/nfs_fs.h>
#include "callback.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK
struct nfs_callback_data {
unsigned int users;
struct svc_serv *serv;
pid_t pid;
struct completion started;
struct completion stopped;
};
static struct nfs_callback_data nfs_callback_info;
static DECLARE_MUTEX(nfs_callback_sema);
static struct svc_program nfs4_callback_program;
unsigned short nfs_callback_tcpport;
/*
* This is the callback kernel thread.
*/
static void nfs_callback_svc(struct svc_rqst *rqstp)
{
struct svc_serv *serv = rqstp->rq_server;
int err;
__module_get(THIS_MODULE);
lock_kernel();
nfs_callback_info.pid = current->pid;
daemonize("nfsv4-svc");
/* Process request with signals blocked, but allow SIGKILL. */
allow_signal(SIGKILL);
complete(&nfs_callback_info.started);
while (nfs_callback_info.users != 0 || !signalled()) {
/*
* Listen for a request on the socket
*/
err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT);
if (err == -EAGAIN || err == -EINTR)
continue;
if (err < 0) {
printk(KERN_WARNING
"%s: terminating on error %d\n",
__FUNCTION__, -err);
break;
}
dprintk("%s: request from %u.%u.%u.%u\n", __FUNCTION__,
NIPQUAD(rqstp->rq_addr.sin_addr.s_addr));
svc_process(serv, rqstp);
}
nfs_callback_info.pid = 0;
complete(&nfs_callback_info.stopped);
unlock_kernel();
module_put_and_exit(0);
}
/*
* Bring up the server process if it is not already up.
*/
int nfs_callback_up(void)
{
struct svc_serv *serv;
struct svc_sock *svsk;
int ret = 0;
lock_kernel();
down(&nfs_callback_sema);
if (nfs_callback_info.users++ || nfs_callback_info.pid != 0)
goto out;
init_completion(&nfs_callback_info.started);
init_completion(&nfs_callback_info.stopped);
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE);
ret = -ENOMEM;
if (!serv)
goto out_err;
/* FIXME: We don't want to register this socket with the portmapper */
ret = svc_makesock(serv, IPPROTO_TCP, 0);
if (ret < 0)
goto out_destroy;
if (!list_empty(&serv->sv_permsocks)) {
svsk = list_entry(serv->sv_permsocks.next,
struct svc_sock, sk_list);
nfs_callback_tcpport = ntohs(inet_sk(svsk->sk_sk)->sport);
dprintk ("Callback port = 0x%x\n", nfs_callback_tcpport);
} else
BUG();
ret = svc_create_thread(nfs_callback_svc, serv);
if (ret < 0)
goto out_destroy;
nfs_callback_info.serv = serv;
wait_for_completion(&nfs_callback_info.started);
out:
up(&nfs_callback_sema);
unlock_kernel();
return ret;
out_destroy:
svc_destroy(serv);
out_err:
nfs_callback_info.users--;
goto out;
}
/*
* Kill the server process if it is not already up.
*/
int nfs_callback_down(void)
{
int ret = 0;
lock_kernel();
down(&nfs_callback_sema);
if (--nfs_callback_info.users || nfs_callback_info.pid == 0)
goto out;
kill_proc(nfs_callback_info.pid, SIGKILL, 1);
wait_for_completion(&nfs_callback_info.stopped);
out:
up(&nfs_callback_sema);
unlock_kernel();
return ret;
}
/*
* AUTH_NULL authentication
*/
static int nfs_callback_null_accept(struct svc_rqst *rqstp, u32 *authp)
{
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
if (argv->iov_len < 3*4)
return SVC_GARBAGE;
if (svc_getu32(argv) != 0) {
dprintk("svc: bad null cred\n");
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
dprintk("svc: bad null verf\n");
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
/* Signal that mapping to nobody uid/gid is required */
rqstp->rq_cred.cr_uid = (uid_t) -1;
rqstp->rq_cred.cr_gid = (gid_t) -1;
rqstp->rq_cred.cr_group_info = groups_alloc(0);
if (rqstp->rq_cred.cr_group_info == NULL)
return SVC_DROP; /* kmalloc failure - client must retry */
/* Put NULL verifier */
svc_putu32(resv, RPC_AUTH_NULL);
svc_putu32(resv, 0);
dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
return SVC_OK;
}
static int nfs_callback_null_release(struct svc_rqst *rqstp)
{
if (rqstp->rq_cred.cr_group_info)
put_group_info(rqstp->rq_cred.cr_group_info);
rqstp->rq_cred.cr_group_info = NULL;
return 0; /* don't drop */
}
static struct auth_ops nfs_callback_auth_null = {
.name = "null",
.flavour = RPC_AUTH_NULL,
.accept = nfs_callback_null_accept,
.release = nfs_callback_null_release,
};
/*
* AUTH_SYS authentication
*/
static int nfs_callback_unix_accept(struct svc_rqst *rqstp, u32 *authp)
{
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
struct svc_cred *cred = &rqstp->rq_cred;
u32 slen, i;
int len = argv->iov_len;
dprintk("%s: start\n", __FUNCTION__);
cred->cr_group_info = NULL;
rqstp->rq_client = NULL;
if ((len -= 3*4) < 0)
return SVC_GARBAGE;
/* Get length, time stamp and machine name */
svc_getu32(argv);
svc_getu32(argv);
slen = XDR_QUADLEN(ntohl(svc_getu32(argv)));
if (slen > 64 || (len -= (slen + 3)*4) < 0)
goto badcred;
argv->iov_base = (void*)((u32*)argv->iov_base + slen);
argv->iov_len -= slen*4;
cred->cr_uid = ntohl(svc_getu32(argv));
cred->cr_gid = ntohl(svc_getu32(argv));
slen = ntohl(svc_getu32(argv));
if (slen > 16 || (len -= (slen + 2)*4) < 0)
goto badcred;
cred->cr_group_info = groups_alloc(slen);
if (cred->cr_group_info == NULL)
return SVC_DROP;
for (i = 0; i < slen; i++)
GROUP_AT(cred->cr_group_info, i) = ntohl(svc_getu32(argv));
if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
/* Put NULL verifier */
svc_putu32(resv, RPC_AUTH_NULL);
svc_putu32(resv, 0);
dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
return SVC_OK;
badcred:
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
static int nfs_callback_unix_release(struct svc_rqst *rqstp)
{
if (rqstp->rq_cred.cr_group_info)
put_group_info(rqstp->rq_cred.cr_group_info);
rqstp->rq_cred.cr_group_info = NULL;
return 0;
}
static struct auth_ops nfs_callback_auth_unix = {
.name = "unix",
.flavour = RPC_AUTH_UNIX,
.accept = nfs_callback_unix_accept,
.release = nfs_callback_unix_release,
};
/*
* Hook the authentication protocol
*/
static int nfs_callback_auth(struct svc_rqst *rqstp, u32 *authp)
{
struct in_addr *addr = &rqstp->rq_addr.sin_addr;
struct nfs4_client *clp;
struct kvec *argv = &rqstp->rq_arg.head[0];
int flavour;
int retval;
/* Don't talk to strangers */
clp = nfs4_find_client(addr);
if (clp == NULL)
return SVC_DROP;
dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr));
nfs4_put_client(clp);
flavour = ntohl(svc_getu32(argv));
switch(flavour) {
case RPC_AUTH_NULL:
if (rqstp->rq_proc != CB_NULL) {
*authp = rpc_autherr_tooweak;
retval = SVC_DENIED;
break;
}
rqstp->rq_authop = &nfs_callback_auth_null;
retval = nfs_callback_null_accept(rqstp, authp);
break;
case RPC_AUTH_UNIX:
/* Eat the authentication flavour */
rqstp->rq_authop = &nfs_callback_auth_unix;
retval = nfs_callback_unix_accept(rqstp, authp);
break;
default:
/* FIXME: need to add RPCSEC_GSS upcalls */
#if 0
svc_ungetu32(argv);
retval = svc_authenticate(rqstp, authp);
#else
*authp = rpc_autherr_rejectedcred;
retval = SVC_DENIED;
#endif
}
dprintk("%s: flavour %d returning error %d\n", __FUNCTION__, flavour, retval);
return retval;
}
/*
* Define NFS4 callback program
*/
extern struct svc_version nfs4_callback_version1;
static struct svc_version *nfs4_callback_version[] = {
[1] = &nfs4_callback_version1,
};
static struct svc_stat nfs4_callback_stats;
static struct svc_program nfs4_callback_program = {
.pg_prog = NFS4_CALLBACK, /* RPC service number */
.pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */
.pg_vers = nfs4_callback_version, /* version table */
.pg_name = "NFSv4 callback", /* service name */
.pg_class = "nfs", /* authentication class */
.pg_stats = &nfs4_callback_stats,
.pg_authenticate = nfs_callback_auth,
};
/*
* linux/fs/nfs/callback.h
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback definitions
*/
#ifndef __LINUX_FS_NFS_CALLBACK_H
#define __LINUX_FS_NFS_CALLBACK_H
#define NFS4_CALLBACK 0x40000000
#define NFS4_CALLBACK_XDRSIZE 2048
#define NFS4_CALLBACK_BUFSIZE (1024 + NFS4_CALLBACK_XDRSIZE)
enum nfs4_callback_procnum {
CB_NULL = 0,
CB_COMPOUND = 1,
};
enum nfs4_callback_opnum {
OP_CB_GETATTR = 3,
OP_CB_RECALL = 4,
OP_CB_ILLEGAL = 10044,
};
struct cb_compound_hdr_arg {
int taglen;
const char *tag;
unsigned int callback_ident;
unsigned nops;
};
struct cb_compound_hdr_res {
uint32_t *status;
int taglen;
const char *tag;
uint32_t *nops;
};
struct cb_getattrargs {
struct sockaddr_in *addr;
struct nfs_fh fh;
uint32_t bitmap[2];
};
struct cb_getattrres {
uint32_t status;
uint32_t bitmap[2];
uint64_t size;
uint64_t change_attr;
struct timespec ctime;
struct timespec mtime;
};
struct cb_recallargs {
struct sockaddr_in *addr;
struct nfs_fh fh;
nfs4_stateid stateid;
uint32_t truncate;
};
extern unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
extern unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
extern int nfs_callback_up(void);
extern int nfs_callback_down(void);
extern unsigned short nfs_callback_tcpport;
#endif /* __LINUX_FS_NFS_CALLBACK_H */
/*
* linux/fs/nfs/callback_proc.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback procedures
*/
#include <linux/config.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include "callback.h"
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK
unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res)
{
struct nfs4_client *clp;
struct nfs_delegation *delegation;
struct nfs_inode *nfsi;
struct inode *inode;
res->bitmap[0] = res->bitmap[1] = 0;
res->status = htonl(NFS4ERR_BADHANDLE);
clp = nfs4_find_client(&args->addr->sin_addr);
if (clp == NULL)
goto out;
inode = nfs_delegation_find_inode(clp, &args->fh);
if (inode == NULL)
goto out_putclient;
nfsi = NFS_I(inode);
down_read(&nfsi->rwsem);
delegation = nfsi->delegation;
if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0)
goto out_iput;
res->size = i_size_read(inode);
res->change_attr = NFS_CHANGE_ATTR(inode);
res->ctime = inode->i_ctime;
res->mtime = inode->i_mtime;
res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) &
args->bitmap[0];
res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) &
args->bitmap[1];
res->status = 0;
out_iput:
up_read(&nfsi->rwsem);
iput(inode);
out_putclient:
nfs4_put_client(clp);
out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
return res->status;
}
unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
{
struct nfs4_client *clp;
struct inode *inode;
unsigned res;
res = htonl(NFS4ERR_BADHANDLE);
clp = nfs4_find_client(&args->addr->sin_addr);
if (clp == NULL)
goto out;
inode = nfs_delegation_find_inode(clp, &args->fh);
if (inode == NULL)
goto out_putclient;
/* Set up a helper thread to actually return the delegation */
switch(nfs_async_inode_return_delegation(inode, &args->stateid)) {
case 0:
res = 0;
break;
case -ENOENT:
res = htonl(NFS4ERR_BAD_STATEID);
break;
default:
res = htonl(NFS4ERR_RESOURCE);
}
iput(inode);
out_putclient:
nfs4_put_client(clp);
out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
return res;
}
This diff is collapsed.
/*
* linux/fs/nfs/delegation.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFS file delegation management
*
*/
#include <linux/config.h>
#include <linux/completion.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_xdr.h>
#include "delegation.h"
static struct nfs_delegation *nfs_alloc_delegation(void)
{
return (struct nfs_delegation *)kmalloc(sizeof(struct nfs_delegation), GFP_KERNEL);
}
static void nfs_free_delegation(struct nfs_delegation *delegation)
{
if (delegation->cred)
put_rpccred(delegation->cred);
kfree(delegation);
}
static void nfs_delegation_claim_opens(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
struct nfs4_state *state;
again:
spin_lock(&inode->i_lock);
list_for_each_entry(ctx, &nfsi->open_files, list) {
state = ctx->state;
if (state == NULL)
continue;
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
continue;
get_nfs_open_context(ctx);
spin_unlock(&inode->i_lock);
if (nfs4_open_delegation_recall(ctx->dentry, state) < 0)
return;
put_nfs_open_context(ctx);
goto again;
}
spin_unlock(&inode->i_lock);
}
/*
* Set up a delegation on an inode
*/
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{
struct nfs_delegation *delegation = NFS_I(inode)->delegation;
if (delegation == NULL)
return;
memcpy(delegation->stateid.data, res->delegation.data,
sizeof(delegation->stateid.data));
delegation->type = res->delegation_type;
delegation->maxsize = res->maxsize;
put_rpccred(cred);
delegation->cred = get_rpccred(cred);
delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
NFS_I(inode)->delegation_state = delegation->type;
smp_wmb();
}
/*
* Set up a delegation on an inode
*/
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int status = 0;
delegation = nfs_alloc_delegation();
if (delegation == NULL)
return -ENOMEM;
memcpy(delegation->stateid.data, res->delegation.data,
sizeof(delegation->stateid.data));
delegation->type = res->delegation_type;
delegation->maxsize = res->maxsize;
delegation->cred = get_rpccred(cred);
delegation->inode = inode;
spin_lock(&clp->cl_lock);
if (nfsi->delegation == NULL) {
list_add(&delegation->super_list, &clp->cl_delegations);
nfsi->delegation = delegation;
nfsi->delegation_state = delegation->type;
delegation = NULL;
} else {
if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
sizeof(delegation->stateid)) != 0 ||
delegation->type != nfsi->delegation->type) {
printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
__FUNCTION__, NIPQUAD(clp->cl_addr));
status = -EIO;
}
}
spin_unlock(&clp->cl_lock);
if (delegation != NULL)
kfree(delegation);
return status;
}
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
{
int res = 0;
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid);
nfs_free_delegation(delegation);
return res;
}
/* Sync all data to disk upon delegation return */
static void nfs_msync_inode(struct inode *inode)
{
filemap_fdatawrite(inode->i_mapping);
nfs_wb_all(inode);
filemap_fdatawait(inode->i_mapping);
}
/*
* Basic procedure for returning a delegation to the server
*/
int nfs_inode_return_delegation(struct inode *inode)
{
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int res = 0;
nfs_msync_inode(inode);
down_read(&clp->cl_sem);
/* Guard against new delegated open calls */
down_write(&nfsi->rwsem);
spin_lock(&clp->cl_lock);
delegation = nfsi->delegation;
if (delegation != NULL) {
list_del_init(&delegation->super_list);
nfsi->delegation = NULL;
nfsi->delegation_state = 0;
}
spin_unlock(&clp->cl_lock);
nfs_delegation_claim_opens(inode);
up_write(&nfsi->rwsem);
up_read(&clp->cl_sem);
nfs_msync_inode(inode);
if (delegation != NULL)
res = nfs_do_return_delegation(inode, delegation);
return res;
}
/*
* Return all delegations associated to a super block
*/
void nfs_return_all_delegations(struct super_block *sb)
{
struct nfs4_client *clp = NFS_SB(sb)->nfs4_state;
struct nfs_delegation *delegation;
struct inode *inode;
if (clp == NULL)
return;
restart:
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
if (delegation->inode->i_sb != sb)
continue;
inode = igrab(delegation->inode);
if (inode == NULL)
continue;
spin_unlock(&clp->cl_lock);
nfs_inode_return_delegation(inode);
iput(inode);
goto restart;
}
spin_unlock(&clp->cl_lock);
}
/*
* Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
*/
void nfs_handle_cb_pathdown(struct nfs4_client *clp)
{
struct nfs_delegation *delegation;
struct inode *inode;
if (clp == NULL)
return;
restart:
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
inode = igrab(delegation->inode);
if (inode == NULL)
continue;
spin_unlock(&clp->cl_lock);
nfs_inode_return_delegation(inode);
iput(inode);
goto restart;
}
spin_unlock(&clp->cl_lock);
}
struct recall_threadargs {
struct inode *inode;
struct nfs4_client *clp;
const nfs4_stateid *stateid;
struct completion started;
int result;
};
static int recall_thread(void *data)
{
struct recall_threadargs *args = (struct recall_threadargs *)data;
struct inode *inode = igrab(args->inode);
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
daemonize("nfsv4-delegreturn");
nfs_msync_inode(inode);
down_read(&clp->cl_sem);
down_write(&nfsi->rwsem);
spin_lock(&clp->cl_lock);
delegation = nfsi->delegation;
if (delegation != NULL && memcmp(delegation->stateid.data,
args->stateid->data,
sizeof(delegation->stateid.data)) == 0) {
list_del_init(&delegation->super_list);
nfsi->delegation = NULL;
nfsi->delegation_state = 0;
args->result = 0;
} else {
delegation = NULL;
args->result = -ENOENT;
}
spin_unlock(&clp->cl_lock);
complete(&args->started);
nfs_delegation_claim_opens(inode);
up_write(&nfsi->rwsem);
up_read(&clp->cl_sem);
nfs_msync_inode(inode);
if (delegation != NULL)
nfs_do_return_delegation(inode, delegation);
iput(inode);
module_put_and_exit(0);
}
/*
* Asynchronous delegation recall!
*/
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
{
struct recall_threadargs data = {
.inode = inode,
.stateid = stateid,
};
int status;
init_completion(&data.started);
__module_get(THIS_MODULE);
status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
if (status < 0)
goto out_module_put;
wait_for_completion(&data.started);
return data.result;
out_module_put:
module_put(THIS_MODULE);
return status;
}
/*
* Retrieve the inode associated with a delegation
*/
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle)
{
struct nfs_delegation *delegation;
struct inode *res = NULL;
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
res = igrab(delegation->inode);
break;
}
}
spin_unlock(&clp->cl_lock);
return res;
}
/*
* Mark all delegations as needing to be reclaimed
*/
void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
{
struct nfs_delegation *delegation;
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list)
delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
spin_unlock(&clp->cl_lock);
}
/*
* Reap all unclaimed delegations after reboot recovery is done
*/
void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
{
struct nfs_delegation *delegation, *n;
LIST_HEAD(head);
spin_lock(&clp->cl_lock);
list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) {
if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
continue;
list_move(&delegation->super_list, &head);
NFS_I(delegation->inode)->delegation = NULL;
NFS_I(delegation->inode)->delegation_state = 0;
}
spin_unlock(&clp->cl_lock);
while(!list_empty(&head)) {
delegation = list_entry(head.next, struct nfs_delegation, super_list);
list_del(&delegation->super_list);
nfs_free_delegation(delegation);
}
}
/*
* linux/fs/nfs/delegation.h
*
* Copyright (c) Trond Myklebust
*
* Definitions pertaining to NFS delegated files
*/
#ifndef FS_NFS_DELEGATION_H
#define FS_NFS_DELEGATION_H
#if defined(CONFIG_NFS_V4)
/*
* NFSv4 delegation
*/
struct nfs_delegation {
struct list_head super_list;
struct rpc_cred *cred;
struct inode *inode;
nfs4_stateid stateid;
int type;
#define NFS_DELEGATION_NEED_RECLAIM 1
long flags;
loff_t maxsize;
};
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
int nfs_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle);
void nfs_return_all_delegations(struct super_block *sb);
void nfs_handle_cb_pathdown(struct nfs4_client *clp);
void nfs_delegation_mark_reclaim(struct nfs4_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs4_client *clp);
/* NFSv4 delegation-related procedures */
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid);
int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state);
static inline int nfs_have_delegation(struct inode *inode, int flags)
{
flags &= FMODE_READ|FMODE_WRITE;
smp_rmb();
if ((NFS_I(inode)->delegation_state & flags) == flags)
return 1;
return 0;
}
#else
static inline int nfs_have_delegation(struct inode *inode, int flags)
{
return 0;
}
#endif
#endif
......@@ -32,6 +32,8 @@
#include <linux/smp_lock.h>
#include <linux/namei.h>
#include "delegation.h"
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
......@@ -610,7 +612,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
verifier = nfs_save_change_attribute(dir);
error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
if (!error) {
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad;
if (nfs_lookup_verify_inode(inode, isopen))
goto out_zap_parent;
......@@ -623,7 +625,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error)
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;
if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad;
......@@ -850,22 +852,22 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
unsigned long verifier;
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);
dir = parent->d_inode;
if (!is_atomic_open(dir, nd))
goto no_open;
/* We can't create new files in nfs_open_revalidate(), so we
* optimize away revalidation of negative dentries.
*/
if (inode == NULL)
goto out;
/* NFS only supports OPEN on regular files */
if (!S_ISREG(inode->i_mode))
goto no_open;
openflags = nd->intent.open.flags;
if (openflags & O_CREAT) {
/* If this is a negative dentry, just drop it */
if (!inode)
goto out;
/* If this is exclusive open, just revalidate */
if (openflags & O_EXCL)
goto no_open;
}
/* We cannot do exclusive creation on a positive dentry */
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
goto no_open;
/* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC);
......@@ -887,6 +889,8 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
return ret;
no_open:
dput(parent);
if (inode != NULL && nfs_have_delegation(inode, FMODE_READ))
return 1;
return nfs_lookup_revalidate(dentry, nd);
}
#endif /* CONFIG_NFSV4 */
......@@ -982,12 +986,18 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
/* We may have been initialized further down */
if (dentry->d_inode)
return 0;
if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) {
if (fhandle->size == 0) {
struct inode *dir = dentry->d_parent->d_inode;
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error)
goto out_err;
}
if (!(fattr->valid & NFS_ATTR_FATTR)) {
struct nfs_server *server = NFS_SB(dentry->d_sb);
error = server->rpc_ops->getattr(server, fhandle, fattr);
if (error < 0)
goto out_err;
}
inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
if (inode) {
d_instantiate(dentry, inode);
......@@ -1299,19 +1309,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,
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
if (dentry->d_inode)
printk("nfs_proc_symlink: %s/%s not negative!\n",
......@@ -1341,8 +1338,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
d_drop(dentry);
}
unlock_kernel();
out:
return error;
}
......@@ -1498,10 +1493,56 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error;
}
int
nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
{
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;
}
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;
int mode = inode->i_mode;
int res;
......@@ -1542,24 +1583,7 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
goto out_notsup;
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (cache->cred == cred
&& 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:
res = nfs_do_access(inode, cred, mask);
put_rpccred(cred);
unlock_kernel();
return res;
......@@ -1568,15 +1592,6 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
res = vfs_permission(inode, mask);
unlock_kernel();
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;
}
/*
......
......@@ -110,7 +110,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
* nfs_direct_read_seg - Read in one iov segment. Generate separate
* read RPCs for each "rsize" bytes.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* user_addr: starting address of this segment of user's buffer
* count: size of this segment
* file_offset: offset in file to begin the operation
......@@ -118,7 +118,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
* nr_pages: size of pages array
*/
static int
nfs_direct_read_seg(struct inode *inode, struct file *file,
nfs_direct_read_seg(struct inode *inode, struct nfs_open_context *ctx,
unsigned long user_addr, size_t count, loff_t file_offset,
struct page **pages, int nr_pages)
{
......@@ -127,9 +127,10 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
int curpage = 0;
struct nfs_read_data rdata = {
.inode = inode,
.cred = ctx->cred,
.args = {
.fh = NFS_FH(inode),
.lockowner = current->files,
.context = ctx,
},
.res = {
.fattr = &rdata.fattr,
......@@ -151,7 +152,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
user_addr + tot_bytes, rdata.args.pgbase, curpage);
lock_kernel();
result = NFS_PROTO(inode)->read(&rdata, file);
result = NFS_PROTO(inode)->read(&rdata);
unlock_kernel();
if (result <= 0) {
......@@ -183,7 +184,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
* nfs_direct_read - For each iov segment, map the user's buffer
* then generate read RPCs.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* @iov: array of vectors that define I/O buffer
* file_offset: offset in file to begin the operation
* nr_segs: size of iovec array
......@@ -193,7 +194,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
* server.
*/
static ssize_t
nfs_direct_read(struct inode *inode, struct file *file,
nfs_direct_read(struct inode *inode, struct nfs_open_context *ctx,
const struct iovec *iov, loff_t file_offset,
unsigned long nr_segs)
{
......@@ -216,7 +217,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
return page_count;
}
result = nfs_direct_read_seg(inode, file, user_addr, size,
result = nfs_direct_read_seg(inode, ctx, user_addr, size,
file_offset, pages, page_count);
nfs_free_user_pages(pages, page_count, 1);
......@@ -239,7 +240,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
* nfs_direct_write_seg - Write out one iov segment. Generate separate
* write RPCs for each "wsize" bytes, then commit.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* user_addr: starting address of this segment of user's buffer
* count: size of this segment
* file_offset: offset in file to begin the operation
......@@ -247,7 +248,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
* nr_pages: size of pages array
*/
static int
nfs_direct_write_seg(struct inode *inode, struct file *file,
nfs_direct_write_seg(struct inode *inode, struct nfs_open_context *ctx,
unsigned long user_addr, size_t count, loff_t file_offset,
struct page **pages, int nr_pages)
{
......@@ -257,9 +258,10 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
struct nfs_writeverf first_verf;
struct nfs_write_data wdata = {
.inode = inode,
.cred = ctx->cred,
.args = {
.fh = NFS_FH(inode),
.lockowner = current->files,
.context = ctx,
},
.res = {
.fattr = &wdata.fattr,
......@@ -290,7 +292,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
user_addr + tot_bytes, wdata.args.pgbase, curpage);
lock_kernel();
result = NFS_PROTO(inode)->write(&wdata, file);
result = NFS_PROTO(inode)->write(&wdata);
unlock_kernel();
if (result <= 0) {
......@@ -325,7 +327,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
wdata.args.offset = file_offset;
lock_kernel();
result = NFS_PROTO(inode)->commit(&wdata, file);
result = NFS_PROTO(inode)->commit(&wdata);
unlock_kernel();
if (result < 0 || memcmp(&first_verf.verifier,
......@@ -349,7 +351,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
* nfs_direct_write - For each iov segment, map the user's buffer
* then generate write and commit RPCs.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* @iov: array of vectors that define I/O buffer
* file_offset: offset in file to begin the operation
* nr_segs: size of iovec array
......@@ -358,8 +360,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
* that non-direct readers might access, so they will pick up these
* writes immediately.
*/
static ssize_t
nfs_direct_write(struct inode *inode, struct file *file,
static int nfs_direct_write(struct inode *inode, struct nfs_open_context *ctx,
const struct iovec *iov, loff_t file_offset,
unsigned long nr_segs)
{
......@@ -382,7 +383,7 @@ nfs_direct_write(struct inode *inode, struct file *file,
return page_count;
}
result = nfs_direct_write_seg(inode, file, user_addr, size,
result = nfs_direct_write_seg(inode, ctx, user_addr, size,
file_offset, pages, page_count);
nfs_free_user_pages(pages, page_count, 0);
......@@ -414,6 +415,7 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
{
ssize_t result = -EINVAL;
struct file *file = iocb->ki_filp;
struct nfs_open_context *ctx;
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
......@@ -423,19 +425,20 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
if (!is_sync_kiocb(iocb))
return result;
ctx = (struct nfs_open_context *)file->private_data;
switch (rw) {
case READ:
dprintk("NFS: direct_IO(read) (%s) off/no(%Lu/%lu)\n",
dentry->d_name.name, file_offset, nr_segs);
result = nfs_direct_read(inode, file, iov,
result = nfs_direct_read(inode, ctx, iov,
file_offset, nr_segs);
break;
case WRITE:
dprintk("NFS: direct_IO(write) (%s) off/no(%Lu/%lu)\n",
dentry->d_name.name, file_offset, nr_segs);
result = nfs_direct_write(inode, file, iov,
result = nfs_direct_write(inode, ctx, iov,
file_offset, nr_segs);
break;
default:
......@@ -471,6 +474,8 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
ssize_t retval = -EINVAL;
loff_t *ppos = &iocb->ki_pos;
struct file *file = iocb->ki_filp;
struct nfs_open_context *ctx =
(struct nfs_open_context *) file->private_data;
struct dentry *dentry = file->f_dentry;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
......@@ -502,7 +507,7 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
goto out;
}
retval = nfs_direct_read(inode, file, &iov, pos, 1);
retval = nfs_direct_read(inode, ctx, &iov, pos, 1);
if (retval > 0)
*ppos = pos + retval;
......@@ -542,6 +547,8 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
loff_t *ppos = &iocb->ki_pos;
unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
struct file *file = iocb->ki_filp;
struct nfs_open_context *ctx =
(struct nfs_open_context *) file->private_data;
struct dentry *dentry = file->f_dentry;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
......@@ -589,7 +596,7 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
goto out;
}
retval = nfs_direct_write(inode, file, &iov, pos, 1);
retval = nfs_direct_write(inode, ctx, &iov, pos, 1);
if (mapping->nrpages)
invalidate_inode_pages2(mapping);
if (retval > 0)
......
......@@ -31,6 +31,8 @@
#include <asm/uaccess.h>
#include <asm/system.h>
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_FILE
static int nfs_file_open(struct inode *, struct file *);
......@@ -113,6 +115,7 @@ nfs_file_release(struct inode *inode, struct file *filp)
static int
nfs_file_flush(struct file *file)
{
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct inode *inode = file->f_dentry->d_inode;
int status;
......@@ -124,9 +127,9 @@ nfs_file_flush(struct file *file)
/* Ensure that data+attribute caches are up to date after close() */
status = nfs_wb_all(inode);
if (!status) {
status = file->f_error;
file->f_error = 0;
if (!status)
status = ctx->error;
ctx->error = 0;
if (!status && !nfs_have_delegation(inode, FMODE_READ))
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
}
unlock_kernel();
......@@ -197,6 +200,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
static int
nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
{
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct inode *inode = dentry->d_inode;
int status;
......@@ -205,8 +209,8 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
lock_kernel();
status = nfs_wb_all(inode);
if (!status) {
status = file->f_error;
file->f_error = 0;
status = ctx->error;
ctx->error = 0;
}
unlock_kernel();
return status;
......
This diff is collapsed.
......@@ -108,7 +108,6 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{
struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) {
fh->size = 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)
{
struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) {
int size = ntohl(*p++);
if (size <= NFS3_FHSIZE) {
......
......@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
static inline u32 *
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 */
fhandle->size = NFS2_FHSIZE;
memcpy(fhandle->data, p, NFS2_FHSIZE);
......@@ -94,6 +92,23 @@ xdr_encode_time(u32 *p, struct timespec *timep)
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*
xdr_decode_time(u32 *p, struct timespec *timep)
{
......@@ -142,15 +157,19 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
SATTR(p, attr, ATTR_GID, ia_gid);
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);
} else if (attr->ia_valid & ATTR_ATIME) {
p = xdr_encode_current_server_time(p, &attr->ia_atime);
} else {
*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);
} else if (attr->ia_valid & ATTR_MTIME) {
p = xdr_encode_current_server_time(p, &attr->ia_mtime);
} else {
*p++ = ~(u32) 0;
*p++ = ~(u32) 0;
......
......@@ -68,18 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task)
return 1;
}
static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp)
{
struct rpc_cred *cred = NULL;
if (filp)
cred = (struct rpc_cred *)filp->private_data;
if (!cred)
cred = NFS_I(inode)->mm_cred;
return cred;
}
/*
* Bare-bones access to getattr: this is for nfs_read_super.
*/
......@@ -104,14 +92,15 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
* One function for each procedure in the NFS protocol.
*/
static int
nfs3_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
int status;
dprintk("NFS call getattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_GETATTR,
NFS_FH(inode), fattr, 0);
status = rpc_call(server->client, NFS3PROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr\n");
return status;
}
......@@ -164,8 +153,7 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
return status;
}
static int
nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
struct nfs_fattr fattr;
struct nfs3_accessargs arg = {
......@@ -178,9 +166,10 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
.rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS],
.rpc_argp = &arg,
.rpc_resp = &res,
.rpc_cred = cred
.rpc_cred = entry->cred
};
int status;
int mode = entry->mask;
int status;
dprintk("NFS call access\n");
fattr.valid = 0;
......@@ -200,10 +189,16 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
}
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, &fattr);
dprintk("NFS reply access\n");
if (status == 0 && (arg.access & res.access) != arg.access)
status = -EACCES;
if (status == 0) {
entry->mask = 0;
if (res.access & NFS3_ACCESS_READ)
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;
}
......@@ -227,8 +222,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page)
return status;
}
static int
nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
static int nfs3_proc_read(struct nfs_read_data *rdata)
{
int flags = rdata->flags;
struct inode * inode = rdata->inode;
......@@ -237,13 +231,13 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_READ],
.rpc_argp = &rdata->args,
.rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
};
int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
......@@ -251,8 +245,7 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
return status;
}
static int
nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
static int nfs3_proc_write(struct nfs_write_data *wdata)
{
int rpcflags = wdata->flags;
struct inode * inode = wdata->inode;
......@@ -261,13 +254,13 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_WRITE],
.rpc_argp = &wdata->args,
.rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
};
int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
......@@ -275,8 +268,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
return status < 0? status : wdata->res.count;
}
static int
nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
static int nfs3_proc_commit(struct nfs_write_data *cdata)
{
struct inode * inode = cdata->inode;
struct nfs_fattr * fattr = cdata->res.fattr;
......@@ -284,13 +276,13 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT],
.rpc_argp = &cdata->args,
.rpc_resp = &cdata->res,
.rpc_cred = cdata->cred,
};
int status;
dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
(long long) cdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
......@@ -534,6 +526,8 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
};
int status;
if (path->len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
dir_attr.valid = 0;
fattr->valid = 0;
......@@ -832,27 +826,6 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, int how)
rpc_call_setup(task, &msg, 0);
}
/*
* Set up the nfspage struct with the right credentials
*/
void
nfs3_request_init(struct nfs_page *req, struct file *filp)
{
req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
}
static int
nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
{
if (req->wb_file != filp)
return 0;
if (req->wb_page != page)
return 0;
if (req->wb_cred != nfs_file_cred(filp))
return 0;
return 1;
}
static int
nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
{
......@@ -892,7 +865,5 @@ struct nfs_rpc_ops nfs_v3_clientops = {
.commit_setup = nfs3_proc_commit_setup,
.file_open = nfs_open,
.file_release = nfs_release,
.request_init = nfs3_request_init,
.request_compatible = nfs3_request_compatible,
.lock = nfs3_proc_lock,
};
......@@ -109,10 +109,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
static inline u32 *
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) {
memcpy(fh->data, p, fh->size);
return p + XDR_QUADLEN(fh->size);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -495,10 +495,8 @@ static int __init root_nfs_get_handle(void)
if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path);
else {
nfs_data.root.size = fh.size;
memcpy(nfs_data.root.data, fh.data, fh.size);
}
else
nfs_copy_fh(nfs_data.root, fh);
return status;
}
......
......@@ -21,11 +21,6 @@
#define NFS_PARANOIA 1
/*
* Spinlock
*/
spinlock_t nfs_wreq_lock = SPIN_LOCK_UNLOCKED;
static kmem_cache_t *nfs_page_cachep;
static inline struct nfs_page *
......@@ -36,7 +31,6 @@ nfs_page_alloc(void)
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->wb_list);
init_waitqueue_head(&p->wb_wait);
}
return p;
}
......@@ -62,7 +56,7 @@ nfs_page_free(struct nfs_page *p)
* User should ensure it is safe to sleep in this function.
*/
struct nfs_page *
nfs_create_request(struct file *file, struct inode *inode,
nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
struct page *page,
unsigned int offset, unsigned int count)
{
......@@ -94,33 +88,38 @@ nfs_create_request(struct file *file, struct inode *inode,
req->wb_offset = offset;
req->wb_pgbase = offset;
req->wb_bytes = count;
req->wb_inode = inode;
req->wb_count = 1;
server->rpc_ops->request_init(req, file);
atomic_set(&req->wb_count, 1);
req->wb_context = get_nfs_open_context(ctx);
return req;
}
/**
* nfs_unlock_request - Unlock request and wake up sleepers.
* @req:
*/
void nfs_unlock_request(struct nfs_page *req)
{
if (!NFS_WBACK_BUSY(req)) {
printk(KERN_ERR "NFS: Invalid unlock attempted\n");
BUG();
}
smp_mb__before_clear_bit();
clear_bit(PG_BUSY, &req->wb_flags);
smp_mb__after_clear_bit();
wake_up_all(&req->wb_context->waitq);
nfs_release_request(req);
}
/**
* nfs_clear_request - Free up all resources allocated to the request
* @req:
*
* Release all resources associated with a write request after it
* Release page resources associated with a write request after it
* has completed.
*/
void nfs_clear_request(struct nfs_page *req)
{
if (req->wb_state)
req->wb_state = NULL;
/* Release struct file or cached credential */
if (req->wb_file) {
fput(req->wb_file);
req->wb_file = NULL;
}
if (req->wb_cred) {
put_rpccred(req->wb_cred);
req->wb_cred = NULL;
}
if (req->wb_page) {
page_cache_release(req->wb_page);
req->wb_page = NULL;
......@@ -137,12 +136,8 @@ void nfs_clear_request(struct nfs_page *req)
void
nfs_release_request(struct nfs_page *req)
{
spin_lock(&nfs_wreq_lock);
if (--req->wb_count) {
spin_unlock(&nfs_wreq_lock);
if (!atomic_dec_and_test(&req->wb_count))
return;
}
spin_unlock(&nfs_wreq_lock);
#ifdef NFS_PARANOIA
BUG_ON (!list_empty(&req->wb_list));
......@@ -151,6 +146,7 @@ nfs_release_request(struct nfs_page *req)
/* Release struct file or cached credential */
nfs_clear_request(req);
put_nfs_open_context(req->wb_context);
nfs_page_free(req);
}
......@@ -194,12 +190,12 @@ nfs_list_add_request(struct nfs_page *req, struct list_head *head)
int
nfs_wait_on_request(struct nfs_page *req)
{
struct inode *inode = req->wb_inode;
struct inode *inode = req->wb_context->dentry->d_inode;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
if (!NFS_WBACK_BUSY(req))
return 0;
return nfs_wait_event(clnt, req->wb_wait, !NFS_WBACK_BUSY(req));
return nfs_wait_event(clnt, req->wb_context->waitq, !NFS_WBACK_BUSY(req));
}
/**
......@@ -224,7 +220,11 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
req = nfs_list_entry(head->next);
if (prev) {
if (req->wb_cred != prev->wb_cred)
if (req->wb_context->cred != prev->wb_context->cred)
break;
if (req->wb_context->lockowner != prev->wb_context->lockowner)
break;
if (req->wb_context->state != prev->wb_context->state)
break;
if (req->wb_index != (prev->wb_index + 1))
break;
......@@ -254,7 +254,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
* starting at index idx_start, is scanned.
* 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
nfs_scan_list(struct list_head *head, struct list_head *dst,
......
......@@ -49,18 +49,6 @@
extern struct rpc_procinfo nfs_procedures[];
static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp)
{
struct rpc_cred *cred = NULL;
if (filp)
cred = (struct rpc_cred *)filp->private_data;
if (!cred)
cred = NFS_I(inode)->mm_cred;
return cred;
}
/*
* Bare-bones access to getattr: this is for nfs_read_super.
*/
......@@ -99,14 +87,15 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
* One function for each procedure in the NFS protocol.
*/
static int
nfs_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
int status;
dprintk("NFS call getattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFSPROC_GETATTR,
NFS_FH(inode), fattr, 0);
status = rpc_call(server->client, NFSPROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr\n");
return status;
}
......@@ -167,8 +156,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page)
return status;
}
static int
nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
static int nfs_proc_read(struct nfs_read_data *rdata)
{
int flags = rdata->flags;
struct inode * inode = rdata->inode;
......@@ -177,15 +165,14 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
.rpc_proc = &nfs_procedures[NFSPROC_READ],
.rpc_argp = &rdata->args,
.rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
};
int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) {
nfs_refresh_inode(inode, fattr);
/* Emulate the eof flag, which isn't normally needed in NFSv2
......@@ -198,8 +185,7 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
return status;
}
static int
nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
static int nfs_proc_write(struct nfs_write_data *wdata)
{
int flags = wdata->flags;
struct inode * inode = wdata->inode;
......@@ -208,13 +194,13 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
.rpc_proc = &nfs_procedures[NFSPROC_WRITE],
.rpc_argp = &wdata->args,
.rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
};
int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) {
nfs_refresh_inode(inode, fattr);
......@@ -400,6 +386,8 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
};
int status;
if (path->len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
......@@ -619,27 +607,6 @@ nfs_proc_commit_setup(struct nfs_write_data *data, int how)
BUG();
}
/*
* Set up the nfspage struct with the right credentials
*/
static void
nfs_request_init(struct nfs_page *req, struct file *filp)
{
req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
}
static int
nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
{
if (req->wb_file != filp)
return 0;
if (req->wb_page != page)
return 0;
if (req->wb_cred != nfs_file_cred(filp))
return 0;
return 1;
}
static int
nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
{
......@@ -680,7 +647,5 @@ struct nfs_rpc_ops nfs_v2_clientops = {
.commit_setup = nfs_proc_commit_setup,
.file_open = nfs_open,
.file_release = nfs_release,
.request_init = nfs_request_init,
.request_compatible = nfs_request_compatible,
.lock = nfs_proc_lock,
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -18,6 +18,7 @@ struct nfs_server {
unsigned int rpages; /* read size (in pages) */
unsigned int wsize; /* write size */
unsigned int wpages; /* write size (in pages) */
unsigned int wtmult; /* server disk block size */
unsigned int dtsize; /* readdir size */
unsigned int bsize; /* server block size */
unsigned int acregmin; /* attr cache timeouts */
......
This diff is collapsed.
This diff is collapsed.
......@@ -69,7 +69,6 @@ u32 g_verify_token_header(
struct xdr_netobj *mech,
int *body_size,
unsigned char **buf_in,
int tok_type,
int toksize);
u32 g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -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 \
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
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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