Commit cd2fbf89 authored by Deepak Saxena's avatar Deepak Saxena

Merge bk://linux.bkbits.net/linux-2.5

into plexity.net:/home/dsaxena/src/linux-2.5-bk
parents 4d5661a0 6a8e8a44
...@@ -1415,8 +1415,8 @@ config NFS_V3 ...@@ -1415,8 +1415,8 @@ config NFS_V3
bool "Provide NFSv3 client support" bool "Provide NFSv3 client support"
depends on NFS_FS depends on NFS_FS
help help
Say Y here if you want your NFS client to be able to speak the newer Say Y here if you want your NFS client to be able to speak version
version 3 of the NFS protocol. 3 of the NFS protocol.
If unsure, say Y. If unsure, say Y.
...@@ -1560,6 +1560,22 @@ config RPCSEC_GSS_KRB5 ...@@ -1560,6 +1560,22 @@ config RPCSEC_GSS_KRB5
If unsure, say N. If unsure, say N.
config RPCSEC_GSS_SPKM3
tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL
select SUNRPC_GSS
select CRYPTO
select CRYPTO_MD5
select CRYPTO_DES
help
Provides for secure RPC calls by means of a gss-api
mechanism based on the SPKM3 public-key mechanism.
Note: Requires an auxiliary userspace daemon which may be found on
http://www.citi.umich.edu/projects/nfsv4/
If unsure, say N.
config SMB_FS config SMB_FS
tristate "SMB file system support (to mount Windows shares etc.)" tristate "SMB file system support (to mount Windows shares etc.)"
depends on INET depends on INET
......
...@@ -237,8 +237,13 @@ nlmsvc_delete_block(struct nlm_block *block, int unlock) ...@@ -237,8 +237,13 @@ nlmsvc_delete_block(struct nlm_block *block, int unlock)
/* Remove block from list */ /* Remove block from list */
nlmsvc_remove_block(block); nlmsvc_remove_block(block);
if (fl->fl_next)
posix_unblock_lock(file->f_file, fl); posix_unblock_lock(file->f_file, fl);
if (unlock) {
fl->fl_type = F_UNLCK;
posix_lock_file(file->f_file, fl);
block->b_granted = 0; block->b_granted = 0;
}
/* If the block is in the middle of a GRANT callback, /* If the block is in the middle of a GRANT callback,
* don't kill it yet. */ * don't kill it yet. */
......
...@@ -67,7 +67,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, ...@@ -67,7 +67,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
down(&nlm_file_sema); down(&nlm_file_sema);
for (file = nlm_files[hash]; file; file = file->f_next) for (file = nlm_files[hash]; file; file = file->f_next)
if (!memcmp(&file->f_handle, f, sizeof(*f))) if (!nfs_compare_fh(&file->f_handle, f))
goto found; goto found;
dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n", dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n",
......
...@@ -9,6 +9,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \ ...@@ -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_ROOT_NFS) += nfsroot.o mount_clnt.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.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 delegation.o idmap.o \
callback.o callback_xdr.o callback_proc.o
nfs-$(CONFIG_NFS_DIRECTIO) += direct.o nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
nfs-objs := $(nfs-y) 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 @@ ...@@ -32,6 +32,8 @@
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/namei.h> #include <linux/namei.h>
#include "delegation.h"
#define NFS_PARANOIA 1 #define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */ /* #define NFS_DEBUG_VERBOSE 1 */
...@@ -610,7 +612,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -610,7 +612,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
verifier = nfs_save_change_attribute(dir); verifier = nfs_save_change_attribute(dir);
error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
if (!error) { if (!error) {
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad; goto out_bad;
if (nfs_lookup_verify_inode(inode, isopen)) if (nfs_lookup_verify_inode(inode, isopen))
goto out_zap_parent; goto out_zap_parent;
...@@ -623,7 +625,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -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); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error) if (error)
goto out_bad; goto out_bad;
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad; goto out_bad;
if ((error = nfs_refresh_inode(inode, &fattr)) != 0) if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad; goto out_bad;
...@@ -850,22 +852,22 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -850,22 +852,22 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
unsigned long verifier; unsigned long verifier;
int openflags, ret = 0; int openflags, ret = 0;
/* NFS only supports OPEN for regular files */
if (inode && !S_ISREG(inode->i_mode))
goto no_open;
parent = dget_parent(dentry); parent = dget_parent(dentry);
dir = parent->d_inode; dir = parent->d_inode;
if (!is_atomic_open(dir, nd)) if (!is_atomic_open(dir, nd))
goto no_open; goto no_open;
openflags = nd->intent.open.flags; /* We can't create new files in nfs_open_revalidate(), so we
if (openflags & O_CREAT) { * optimize away revalidation of negative dentries.
/* If this is a negative dentry, just drop it */ */
if (!inode) if (inode == NULL)
goto out; goto out;
/* If this is exclusive open, just revalidate */ /* NFS only supports OPEN on regular files */
if (openflags & O_EXCL) if (!S_ISREG(inode->i_mode))
goto no_open;
openflags = nd->intent.open.flags;
/* We cannot do exclusive creation on a positive dentry */
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
goto no_open; goto no_open;
}
/* We can't create new files, or truncate existing ones here */ /* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC); openflags &= ~(O_CREAT|O_TRUNC);
...@@ -887,6 +889,8 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -887,6 +889,8 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
return ret; return ret;
no_open: no_open:
dput(parent); dput(parent);
if (inode != NULL && nfs_have_delegation(inode, FMODE_READ))
return 1;
return nfs_lookup_revalidate(dentry, nd); return nfs_lookup_revalidate(dentry, nd);
} }
#endif /* CONFIG_NFSV4 */ #endif /* CONFIG_NFSV4 */
...@@ -982,12 +986,18 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, ...@@ -982,12 +986,18 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
/* We may have been initialized further down */ /* We may have been initialized further down */
if (dentry->d_inode) if (dentry->d_inode)
return 0; return 0;
if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) { if (fhandle->size == 0) {
struct inode *dir = dentry->d_parent->d_inode; struct inode *dir = dentry->d_parent->d_inode;
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error) if (error)
goto out_err; 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); inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
if (inode) { if (inode) {
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
...@@ -1299,19 +1309,6 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ...@@ -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, dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
dir->i_ino, dentry->d_name.name, symname); dir->i_ino, dentry->d_name.name, symname);
error = -ENAMETOOLONG;
switch (NFS_PROTO(dir)->version) {
case 2:
if (strlen(symname) > NFS2_MAXPATHLEN)
goto out;
break;
case 3:
if (strlen(symname) > NFS3_MAXPATHLEN)
goto out;
default:
break;
}
#ifdef NFS_PARANOIA #ifdef NFS_PARANOIA
if (dentry->d_inode) if (dentry->d_inode)
printk("nfs_proc_symlink: %s/%s not negative!\n", printk("nfs_proc_symlink: %s/%s not negative!\n",
...@@ -1341,8 +1338,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -1341,8 +1338,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
d_drop(dentry); d_drop(dentry);
} }
unlock_kernel(); unlock_kernel();
out:
return error; return error;
} }
...@@ -1498,10 +1493,56 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1498,10 +1493,56 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error; return error;
} }
int int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
nfs_permission(struct inode *inode, int mask, struct nameidata *nd) {
struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
if (cache->cred != cred
|| time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
|| (NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR))
return -ENOENT;
memcpy(res, cache, sizeof(*res));
return 0;
}
void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
{
struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
if (cache->cred != set->cred) {
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = get_rpccred(set->cred);
}
cache->jiffies = set->jiffies;
cache->mask = set->mask;
}
static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
{
struct nfs_access_entry cache;
int status;
status = nfs_access_get_cached(inode, cred, &cache);
if (status == 0)
goto out;
/* Be clever: ask server to check for all possible rights */
cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
cache.cred = cred;
cache.jiffies = jiffies;
status = NFS_PROTO(inode)->access(inode, &cache);
if (status != 0)
return status;
nfs_access_add_cache(inode, &cache);
out:
if ((cache.mask & mask) == mask)
return 0;
return -EACCES;
}
int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
{ {
struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
struct rpc_cred *cred; struct rpc_cred *cred;
int mode = inode->i_mode; int mode = inode->i_mode;
int res; int res;
...@@ -1542,24 +1583,7 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1542,24 +1583,7 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
goto out_notsup; goto out_notsup;
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (cache->cred == cred res = nfs_do_access(inode, cred, mask);
&& time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
&& !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
if (!(res = cache->err)) {
/* Is the mask a subset of an accepted mask? */
if ((cache->mask & mask) == mask)
goto out;
} else {
/* ...or is it a superset of a rejected mask? */
if ((cache->mask & mask) == cache->mask)
goto out;
}
}
res = NFS_PROTO(inode)->access(inode, cred, mask);
if (!res || res == -EACCES)
goto add_cache;
out:
put_rpccred(cred); put_rpccred(cred);
unlock_kernel(); unlock_kernel();
return res; return res;
...@@ -1568,15 +1592,6 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1568,15 +1592,6 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
res = vfs_permission(inode, mask); res = vfs_permission(inode, mask);
unlock_kernel(); unlock_kernel();
return res; return res;
add_cache:
cache->jiffies = jiffies;
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = cred;
cache->mask = mask;
cache->err = res;
unlock_kernel();
return res;
} }
/* /*
......
...@@ -110,7 +110,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty) ...@@ -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 * nfs_direct_read_seg - Read in one iov segment. Generate separate
* read RPCs for each "rsize" bytes. * read RPCs for each "rsize" bytes.
* @inode: target inode * @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 * user_addr: starting address of this segment of user's buffer
* count: size of this segment * count: size of this segment
* file_offset: offset in file to begin the operation * 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) ...@@ -118,7 +118,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
* nr_pages: size of pages array * nr_pages: size of pages array
*/ */
static int 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, unsigned long user_addr, size_t count, loff_t file_offset,
struct page **pages, int nr_pages) struct page **pages, int nr_pages)
{ {
...@@ -127,9 +127,10 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, ...@@ -127,9 +127,10 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
int curpage = 0; int curpage = 0;
struct nfs_read_data rdata = { struct nfs_read_data rdata = {
.inode = inode, .inode = inode,
.cred = ctx->cred,
.args = { .args = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
.lockowner = current->files, .context = ctx,
}, },
.res = { .res = {
.fattr = &rdata.fattr, .fattr = &rdata.fattr,
...@@ -151,7 +152,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, ...@@ -151,7 +152,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
user_addr + tot_bytes, rdata.args.pgbase, curpage); user_addr + tot_bytes, rdata.args.pgbase, curpage);
lock_kernel(); lock_kernel();
result = NFS_PROTO(inode)->read(&rdata, file); result = NFS_PROTO(inode)->read(&rdata);
unlock_kernel(); unlock_kernel();
if (result <= 0) { if (result <= 0) {
...@@ -183,7 +184,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, ...@@ -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 * nfs_direct_read - For each iov segment, map the user's buffer
* then generate read RPCs. * then generate read RPCs.
* @inode: target inode * @inode: target inode
* @file: target file (may be NULL) * @ctx: target file open context
* @iov: array of vectors that define I/O buffer * @iov: array of vectors that define I/O buffer
* file_offset: offset in file to begin the operation * file_offset: offset in file to begin the operation
* nr_segs: size of iovec array * nr_segs: size of iovec array
...@@ -193,7 +194,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file, ...@@ -193,7 +194,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
* server. * server.
*/ */
static ssize_t 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, const struct iovec *iov, loff_t file_offset,
unsigned long nr_segs) unsigned long nr_segs)
{ {
...@@ -216,7 +217,7 @@ nfs_direct_read(struct inode *inode, struct file *file, ...@@ -216,7 +217,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
return page_count; 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); file_offset, pages, page_count);
nfs_free_user_pages(pages, page_count, 1); nfs_free_user_pages(pages, page_count, 1);
...@@ -239,7 +240,7 @@ nfs_direct_read(struct inode *inode, struct file *file, ...@@ -239,7 +240,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
* nfs_direct_write_seg - Write out one iov segment. Generate separate * nfs_direct_write_seg - Write out one iov segment. Generate separate
* write RPCs for each "wsize" bytes, then commit. * write RPCs for each "wsize" bytes, then commit.
* @inode: target inode * @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 * user_addr: starting address of this segment of user's buffer
* count: size of this segment * count: size of this segment
* file_offset: offset in file to begin the operation * file_offset: offset in file to begin the operation
...@@ -247,7 +248,7 @@ nfs_direct_read(struct inode *inode, struct file *file, ...@@ -247,7 +248,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
* nr_pages: size of pages array * nr_pages: size of pages array
*/ */
static int 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, unsigned long user_addr, size_t count, loff_t file_offset,
struct page **pages, int nr_pages) struct page **pages, int nr_pages)
{ {
...@@ -257,9 +258,10 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -257,9 +258,10 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
struct nfs_writeverf first_verf; struct nfs_writeverf first_verf;
struct nfs_write_data wdata = { struct nfs_write_data wdata = {
.inode = inode, .inode = inode,
.cred = ctx->cred,
.args = { .args = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
.lockowner = current->files, .context = ctx,
}, },
.res = { .res = {
.fattr = &wdata.fattr, .fattr = &wdata.fattr,
...@@ -290,7 +292,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -290,7 +292,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
user_addr + tot_bytes, wdata.args.pgbase, curpage); user_addr + tot_bytes, wdata.args.pgbase, curpage);
lock_kernel(); lock_kernel();
result = NFS_PROTO(inode)->write(&wdata, file); result = NFS_PROTO(inode)->write(&wdata);
unlock_kernel(); unlock_kernel();
if (result <= 0) { if (result <= 0) {
...@@ -325,7 +327,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -325,7 +327,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
wdata.args.offset = file_offset; wdata.args.offset = file_offset;
lock_kernel(); lock_kernel();
result = NFS_PROTO(inode)->commit(&wdata, file); result = NFS_PROTO(inode)->commit(&wdata);
unlock_kernel(); unlock_kernel();
if (result < 0 || memcmp(&first_verf.verifier, if (result < 0 || memcmp(&first_verf.verifier,
...@@ -349,7 +351,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -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 * nfs_direct_write - For each iov segment, map the user's buffer
* then generate write and commit RPCs. * then generate write and commit RPCs.
* @inode: target inode * @inode: target inode
* @file: target file (may be NULL) * @ctx: target file open context
* @iov: array of vectors that define I/O buffer * @iov: array of vectors that define I/O buffer
* file_offset: offset in file to begin the operation * file_offset: offset in file to begin the operation
* nr_segs: size of iovec array * nr_segs: size of iovec array
...@@ -358,8 +360,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -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 * that non-direct readers might access, so they will pick up these
* writes immediately. * writes immediately.
*/ */
static ssize_t static int nfs_direct_write(struct inode *inode, struct nfs_open_context *ctx,
nfs_direct_write(struct inode *inode, struct file *file,
const struct iovec *iov, loff_t file_offset, const struct iovec *iov, loff_t file_offset,
unsigned long nr_segs) unsigned long nr_segs)
{ {
...@@ -382,7 +383,7 @@ nfs_direct_write(struct inode *inode, struct file *file, ...@@ -382,7 +383,7 @@ nfs_direct_write(struct inode *inode, struct file *file,
return page_count; 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); file_offset, pages, page_count);
nfs_free_user_pages(pages, page_count, 0); nfs_free_user_pages(pages, page_count, 0);
...@@ -414,6 +415,7 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, ...@@ -414,6 +415,7 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
{ {
ssize_t result = -EINVAL; ssize_t result = -EINVAL;
struct file *file = iocb->ki_filp; struct file *file = iocb->ki_filp;
struct nfs_open_context *ctx;
struct dentry *dentry = file->f_dentry; struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -423,19 +425,20 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, ...@@ -423,19 +425,20 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
if (!is_sync_kiocb(iocb)) if (!is_sync_kiocb(iocb))
return result; return result;
ctx = (struct nfs_open_context *)file->private_data;
switch (rw) { switch (rw) {
case READ: case READ:
dprintk("NFS: direct_IO(read) (%s) off/no(%Lu/%lu)\n", dprintk("NFS: direct_IO(read) (%s) off/no(%Lu/%lu)\n",
dentry->d_name.name, file_offset, nr_segs); 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); file_offset, nr_segs);
break; break;
case WRITE: case WRITE:
dprintk("NFS: direct_IO(write) (%s) off/no(%Lu/%lu)\n", dprintk("NFS: direct_IO(write) (%s) off/no(%Lu/%lu)\n",
dentry->d_name.name, file_offset, nr_segs); 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); file_offset, nr_segs);
break; break;
default: default:
...@@ -471,6 +474,8 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t ...@@ -471,6 +474,8 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
ssize_t retval = -EINVAL; ssize_t retval = -EINVAL;
loff_t *ppos = &iocb->ki_pos; loff_t *ppos = &iocb->ki_pos;
struct file *file = iocb->ki_filp; 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 dentry *dentry = file->f_dentry;
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
...@@ -502,7 +507,7 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t ...@@ -502,7 +507,7 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
goto out; goto out;
} }
retval = nfs_direct_read(inode, file, &iov, pos, 1); retval = nfs_direct_read(inode, ctx, &iov, pos, 1);
if (retval > 0) if (retval > 0)
*ppos = pos + retval; *ppos = pos + retval;
...@@ -542,6 +547,8 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, ...@@ -542,6 +547,8 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
loff_t *ppos = &iocb->ki_pos; loff_t *ppos = &iocb->ki_pos;
unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
struct file *file = iocb->ki_filp; 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 dentry *dentry = file->f_dentry;
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
...@@ -589,7 +596,7 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, ...@@ -589,7 +596,7 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
goto out; goto out;
} }
retval = nfs_direct_write(inode, file, &iov, pos, 1); retval = nfs_direct_write(inode, ctx, &iov, pos, 1);
if (mapping->nrpages) if (mapping->nrpages)
invalidate_inode_pages2(mapping); invalidate_inode_pages2(mapping);
if (retval > 0) if (retval > 0)
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_FILE #define NFSDBG_FACILITY NFSDBG_FILE
static int nfs_file_open(struct inode *, struct file *); static int nfs_file_open(struct inode *, struct file *);
...@@ -113,6 +115,7 @@ nfs_file_release(struct inode *inode, struct file *filp) ...@@ -113,6 +115,7 @@ nfs_file_release(struct inode *inode, struct file *filp)
static int static int
nfs_file_flush(struct file *file) 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; struct inode *inode = file->f_dentry->d_inode;
int status; int status;
...@@ -124,9 +127,9 @@ nfs_file_flush(struct file *file) ...@@ -124,9 +127,9 @@ nfs_file_flush(struct file *file)
/* Ensure that data+attribute caches are up to date after close() */ /* Ensure that data+attribute caches are up to date after close() */
status = nfs_wb_all(inode); status = nfs_wb_all(inode);
if (!status) { if (!status) {
status = file->f_error; status = ctx->error;
file->f_error = 0; ctx->error = 0;
if (!status) if (!status && !nfs_have_delegation(inode, FMODE_READ))
__nfs_revalidate_inode(NFS_SERVER(inode), inode); __nfs_revalidate_inode(NFS_SERVER(inode), inode);
} }
unlock_kernel(); unlock_kernel();
...@@ -197,6 +200,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) ...@@ -197,6 +200,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
static int static int
nfs_fsync(struct file *file, struct dentry *dentry, int datasync) 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; struct inode *inode = dentry->d_inode;
int status; int status;
...@@ -205,8 +209,8 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync) ...@@ -205,8 +209,8 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
lock_kernel(); lock_kernel();
status = nfs_wb_all(inode); status = nfs_wb_all(inode);
if (!status) { if (!status) {
status = file->f_error; status = ctx->error;
file->f_error = 0; ctx->error = 0;
} }
unlock_kernel(); unlock_kernel();
return status; return status;
......
This diff is collapsed.
...@@ -108,7 +108,6 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res) ...@@ -108,7 +108,6 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{ {
struct nfs_fh *fh = res->fh; struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) { if ((res->status = ntohl(*p++)) == 0) {
fh->size = NFS2_FHSIZE; fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE); memcpy(fh->data, p, NFS2_FHSIZE);
...@@ -121,7 +120,6 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res) ...@@ -121,7 +120,6 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{ {
struct nfs_fh *fh = res->fh; struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) { if ((res->status = ntohl(*p++)) == 0) {
int size = ntohl(*p++); int size = ntohl(*p++);
if (size <= NFS3_FHSIZE) { if (size <= NFS3_FHSIZE) {
......
...@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle) ...@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
static inline u32 * static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle) xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
{ {
/* Zero handle first to allow comparisons */
memset(fhandle, 0, sizeof(*fhandle));
/* NFSv2 handles have a fixed length */ /* NFSv2 handles have a fixed length */
fhandle->size = NFS2_FHSIZE; fhandle->size = NFS2_FHSIZE;
memcpy(fhandle->data, p, NFS2_FHSIZE); memcpy(fhandle->data, p, NFS2_FHSIZE);
...@@ -94,6 +92,23 @@ xdr_encode_time(u32 *p, struct timespec *timep) ...@@ -94,6 +92,23 @@ xdr_encode_time(u32 *p, struct timespec *timep)
return p; return p;
} }
static inline u32*
xdr_encode_current_server_time(u32 *p, struct timespec *timep)
{
/*
* Passing the invalid value useconds=1000000 is a
* Sun convention for "set to current server time".
* It's needed to make permissions checks for the
* "touch" program across v2 mounts to Solaris and
* Irix boxes work correctly. See description of
* sattr in section 6.1 of "NFS Illustrated" by
* Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
*/
*p++ = htonl(timep->tv_sec);
*p++ = htonl(1000000);
return p;
}
static inline u32* static inline u32*
xdr_decode_time(u32 *p, struct timespec *timep) xdr_decode_time(u32 *p, struct timespec *timep)
{ {
...@@ -142,15 +157,19 @@ xdr_encode_sattr(u32 *p, struct iattr *attr) ...@@ -142,15 +157,19 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
SATTR(p, attr, ATTR_GID, ia_gid); SATTR(p, attr, ATTR_GID, ia_gid);
SATTR(p, attr, ATTR_SIZE, ia_size); SATTR(p, attr, ATTR_SIZE, ia_size);
if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) { if (attr->ia_valid & ATTR_ATIME_SET) {
p = xdr_encode_time(p, &attr->ia_atime); p = xdr_encode_time(p, &attr->ia_atime);
} else if (attr->ia_valid & ATTR_ATIME) {
p = xdr_encode_current_server_time(p, &attr->ia_atime);
} else { } else {
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
} }
if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) { if (attr->ia_valid & ATTR_MTIME_SET) {
p = xdr_encode_time(p, &attr->ia_mtime); p = xdr_encode_time(p, &attr->ia_mtime);
} else if (attr->ia_valid & ATTR_MTIME) {
p = xdr_encode_current_server_time(p, &attr->ia_mtime);
} else { } else {
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
*p++ = ~(u32) 0; *p++ = ~(u32) 0;
......
...@@ -68,18 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task) ...@@ -68,18 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task)
return 1; 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. * 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, ...@@ -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. * One function for each procedure in the NFS protocol.
*/ */
static int 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; int status;
dprintk("NFS call getattr\n"); dprintk("NFS call getattr\n");
fattr->valid = 0; fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_GETATTR, status = rpc_call(server->client, NFS3PROC_GETATTR,
NFS_FH(inode), fattr, 0); fhandle, fattr, 0);
dprintk("NFS reply getattr\n"); dprintk("NFS reply getattr\n");
return status; return status;
} }
...@@ -164,8 +153,7 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name, ...@@ -164,8 +153,7 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
return status; return status;
} }
static int static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
{ {
struct nfs_fattr fattr; struct nfs_fattr fattr;
struct nfs3_accessargs arg = { struct nfs3_accessargs arg = {
...@@ -178,8 +166,9 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) ...@@ -178,8 +166,9 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
.rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS], .rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS],
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_resp = &res, .rpc_resp = &res,
.rpc_cred = cred .rpc_cred = entry->cred
}; };
int mode = entry->mask;
int status; int status;
dprintk("NFS call access\n"); dprintk("NFS call access\n");
...@@ -200,10 +189,16 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) ...@@ -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); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, &fattr); nfs_refresh_inode(inode, &fattr);
dprintk("NFS reply access\n"); if (status == 0) {
entry->mask = 0;
if (status == 0 && (arg.access & res.access) != arg.access) if (res.access & NFS3_ACCESS_READ)
status = -EACCES; entry->mask |= MAY_READ;
if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
entry->mask |= MAY_WRITE;
if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
dprintk("NFS reply access, status = %d\n", status);
return status; return status;
} }
...@@ -227,8 +222,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page) ...@@ -227,8 +222,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page)
return status; return status;
} }
static int static int nfs3_proc_read(struct nfs_read_data *rdata)
nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
{ {
int flags = rdata->flags; int flags = rdata->flags;
struct inode * inode = rdata->inode; struct inode * inode = rdata->inode;
...@@ -237,13 +231,13 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp) ...@@ -237,13 +231,13 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_READ], .rpc_proc = &nfs3_procedures[NFS3PROC_READ],
.rpc_argp = &rdata->args, .rpc_argp = &rdata->args,
.rpc_resp = &rdata->res, .rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
}; };
int status; int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count, dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset); (long long) rdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) if (status >= 0)
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
...@@ -251,8 +245,7 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp) ...@@ -251,8 +245,7 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
return status; return status;
} }
static int static int nfs3_proc_write(struct nfs_write_data *wdata)
nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
{ {
int rpcflags = wdata->flags; int rpcflags = wdata->flags;
struct inode * inode = wdata->inode; struct inode * inode = wdata->inode;
...@@ -261,13 +254,13 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -261,13 +254,13 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_WRITE], .rpc_proc = &nfs3_procedures[NFS3PROC_WRITE],
.rpc_argp = &wdata->args, .rpc_argp = &wdata->args,
.rpc_resp = &wdata->res, .rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
}; };
int status; int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count, dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset); (long long) wdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0) if (status >= 0)
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
...@@ -275,8 +268,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -275,8 +268,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
return status < 0? status : wdata->res.count; return status < 0? status : wdata->res.count;
} }
static int static int nfs3_proc_commit(struct nfs_write_data *cdata)
nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
{ {
struct inode * inode = cdata->inode; struct inode * inode = cdata->inode;
struct nfs_fattr * fattr = cdata->res.fattr; struct nfs_fattr * fattr = cdata->res.fattr;
...@@ -284,13 +276,13 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp) ...@@ -284,13 +276,13 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT], .rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT],
.rpc_argp = &cdata->args, .rpc_argp = &cdata->args,
.rpc_resp = &cdata->res, .rpc_resp = &cdata->res,
.rpc_cred = cdata->cred,
}; };
int status; int status;
dprintk("NFS call commit %d @ %Ld\n", cdata->args.count, dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
(long long) cdata->args.offset); (long long) cdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status >= 0) if (status >= 0)
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
...@@ -534,6 +526,8 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, ...@@ -534,6 +526,8 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
}; };
int status; int status;
if (path->len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name); dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
dir_attr.valid = 0; dir_attr.valid = 0;
fattr->valid = 0; fattr->valid = 0;
...@@ -832,27 +826,6 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, int how) ...@@ -832,27 +826,6 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, int how)
rpc_call_setup(task, &msg, 0); 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 static int
nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
{ {
...@@ -892,7 +865,5 @@ struct nfs_rpc_ops nfs_v3_clientops = { ...@@ -892,7 +865,5 @@ struct nfs_rpc_ops nfs_v3_clientops = {
.commit_setup = nfs3_proc_commit_setup, .commit_setup = nfs3_proc_commit_setup,
.file_open = nfs_open, .file_open = nfs_open,
.file_release = nfs_release, .file_release = nfs_release,
.request_init = nfs3_request_init,
.request_compatible = nfs3_request_compatible,
.lock = nfs3_proc_lock, .lock = nfs3_proc_lock,
}; };
...@@ -109,10 +109,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh) ...@@ -109,10 +109,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
static inline u32 * static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fh) xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
{ {
/*
* Zero all nonused bytes
*/
memset((u8 *)fh, 0, sizeof(*fh));
if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) { if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
memcpy(fh->data, p, fh->size); memcpy(fh->data, p, fh->size);
return p + XDR_QUADLEN(fh->size); return p + XDR_QUADLEN(fh->size);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -495,10 +495,8 @@ static int __init root_nfs_get_handle(void) ...@@ -495,10 +495,8 @@ static int __init root_nfs_get_handle(void)
if (status < 0) if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d " printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path); "while mounting %s\n", status, nfs_path);
else { else
nfs_data.root.size = fh.size; nfs_copy_fh(nfs_data.root, fh);
memcpy(nfs_data.root.data, fh.data, fh.size);
}
return status; return status;
} }
......
...@@ -21,11 +21,6 @@ ...@@ -21,11 +21,6 @@
#define NFS_PARANOIA 1 #define NFS_PARANOIA 1
/*
* Spinlock
*/
spinlock_t nfs_wreq_lock = SPIN_LOCK_UNLOCKED;
static kmem_cache_t *nfs_page_cachep; static kmem_cache_t *nfs_page_cachep;
static inline struct nfs_page * static inline struct nfs_page *
...@@ -36,7 +31,6 @@ nfs_page_alloc(void) ...@@ -36,7 +31,6 @@ nfs_page_alloc(void)
if (p) { if (p) {
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->wb_list); INIT_LIST_HEAD(&p->wb_list);
init_waitqueue_head(&p->wb_wait);
} }
return p; return p;
} }
...@@ -62,7 +56,7 @@ nfs_page_free(struct nfs_page *p) ...@@ -62,7 +56,7 @@ nfs_page_free(struct nfs_page *p)
* User should ensure it is safe to sleep in this function. * User should ensure it is safe to sleep in this function.
*/ */
struct nfs_page * 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, struct page *page,
unsigned int offset, unsigned int count) unsigned int offset, unsigned int count)
{ {
...@@ -94,33 +88,38 @@ nfs_create_request(struct file *file, struct inode *inode, ...@@ -94,33 +88,38 @@ nfs_create_request(struct file *file, struct inode *inode,
req->wb_offset = offset; req->wb_offset = offset;
req->wb_pgbase = offset; req->wb_pgbase = offset;
req->wb_bytes = count; req->wb_bytes = count;
req->wb_inode = inode; atomic_set(&req->wb_count, 1);
req->wb_count = 1; req->wb_context = get_nfs_open_context(ctx);
server->rpc_ops->request_init(req, file);
return req; 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 * nfs_clear_request - Free up all resources allocated to the request
* @req: * @req:
* *
* Release all resources associated with a write request after it * Release page resources associated with a write request after it
* has completed. * has completed.
*/ */
void nfs_clear_request(struct nfs_page *req) 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) { if (req->wb_page) {
page_cache_release(req->wb_page); page_cache_release(req->wb_page);
req->wb_page = NULL; req->wb_page = NULL;
...@@ -137,12 +136,8 @@ void nfs_clear_request(struct nfs_page *req) ...@@ -137,12 +136,8 @@ void nfs_clear_request(struct nfs_page *req)
void void
nfs_release_request(struct nfs_page *req) nfs_release_request(struct nfs_page *req)
{ {
spin_lock(&nfs_wreq_lock); if (!atomic_dec_and_test(&req->wb_count))
if (--req->wb_count) {
spin_unlock(&nfs_wreq_lock);
return; return;
}
spin_unlock(&nfs_wreq_lock);
#ifdef NFS_PARANOIA #ifdef NFS_PARANOIA
BUG_ON (!list_empty(&req->wb_list)); BUG_ON (!list_empty(&req->wb_list));
...@@ -151,6 +146,7 @@ nfs_release_request(struct nfs_page *req) ...@@ -151,6 +146,7 @@ nfs_release_request(struct nfs_page *req)
/* Release struct file or cached credential */ /* Release struct file or cached credential */
nfs_clear_request(req); nfs_clear_request(req);
put_nfs_open_context(req->wb_context);
nfs_page_free(req); nfs_page_free(req);
} }
...@@ -194,12 +190,12 @@ nfs_list_add_request(struct nfs_page *req, struct list_head *head) ...@@ -194,12 +190,12 @@ nfs_list_add_request(struct nfs_page *req, struct list_head *head)
int int
nfs_wait_on_request(struct nfs_page *req) 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); struct rpc_clnt *clnt = NFS_CLIENT(inode);
if (!NFS_WBACK_BUSY(req)) if (!NFS_WBACK_BUSY(req))
return 0; 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, ...@@ -224,7 +220,11 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
req = nfs_list_entry(head->next); req = nfs_list_entry(head->next);
if (prev) { 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; break;
if (req->wb_index != (prev->wb_index + 1)) if (req->wb_index != (prev->wb_index + 1))
break; break;
...@@ -254,7 +254,7 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, ...@@ -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 * If the number of requests is set to 0, the entire address_space
* starting at index idx_start, is scanned. * starting at index idx_start, is scanned.
* The requests are *not* checked to ensure that they form a contiguous set. * The requests are *not* checked to ensure that they form a contiguous set.
* You must be holding the nfs_wreq_lock when calling this function * You must be holding the inode's req_lock when calling this function
*/ */
int int
nfs_scan_list(struct list_head *head, struct list_head *dst, nfs_scan_list(struct list_head *head, struct list_head *dst,
......
...@@ -49,18 +49,6 @@ ...@@ -49,18 +49,6 @@
extern struct rpc_procinfo nfs_procedures[]; 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. * 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, ...@@ -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. * One function for each procedure in the NFS protocol.
*/ */
static int 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; int status;
dprintk("NFS call getattr\n"); dprintk("NFS call getattr\n");
fattr->valid = 0; fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFSPROC_GETATTR, status = rpc_call(server->client, NFSPROC_GETATTR,
NFS_FH(inode), fattr, 0); fhandle, fattr, 0);
dprintk("NFS reply getattr\n"); dprintk("NFS reply getattr\n");
return status; return status;
} }
...@@ -167,8 +156,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page) ...@@ -167,8 +156,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page)
return status; return status;
} }
static int static int nfs_proc_read(struct nfs_read_data *rdata)
nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
{ {
int flags = rdata->flags; int flags = rdata->flags;
struct inode * inode = rdata->inode; struct inode * inode = rdata->inode;
...@@ -177,15 +165,14 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp) ...@@ -177,15 +165,14 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
.rpc_proc = &nfs_procedures[NFSPROC_READ], .rpc_proc = &nfs_procedures[NFSPROC_READ],
.rpc_argp = &rdata->args, .rpc_argp = &rdata->args,
.rpc_resp = &rdata->res, .rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
}; };
int status; int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count, dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset); (long long) rdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) { if (status >= 0) {
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
/* Emulate the eof flag, which isn't normally needed in NFSv2 /* 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) ...@@ -198,8 +185,7 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
return status; return status;
} }
static int static int nfs_proc_write(struct nfs_write_data *wdata)
nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
{ {
int flags = wdata->flags; int flags = wdata->flags;
struct inode * inode = wdata->inode; struct inode * inode = wdata->inode;
...@@ -208,13 +194,13 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -208,13 +194,13 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
.rpc_proc = &nfs_procedures[NFSPROC_WRITE], .rpc_proc = &nfs_procedures[NFSPROC_WRITE],
.rpc_argp = &wdata->args, .rpc_argp = &wdata->args,
.rpc_resp = &wdata->res, .rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
}; };
int status; int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count, dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset); (long long) wdata->args.offset);
fattr->valid = 0; fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) { if (status >= 0) {
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
...@@ -400,6 +386,8 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, ...@@ -400,6 +386,8 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
}; };
int status; int status;
if (path->len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name); dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
fattr->valid = 0; fattr->valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0); status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
...@@ -619,27 +607,6 @@ nfs_proc_commit_setup(struct nfs_write_data *data, int how) ...@@ -619,27 +607,6 @@ nfs_proc_commit_setup(struct nfs_write_data *data, int how)
BUG(); 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 static int
nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
{ {
...@@ -680,7 +647,5 @@ struct nfs_rpc_ops nfs_v2_clientops = { ...@@ -680,7 +647,5 @@ struct nfs_rpc_ops nfs_v2_clientops = {
.commit_setup = nfs_proc_commit_setup, .commit_setup = nfs_proc_commit_setup,
.file_open = nfs_open, .file_open = nfs_open,
.file_release = nfs_release, .file_release = nfs_release,
.request_init = nfs_request_init,
.request_compatible = nfs_request_compatible,
.lock = nfs_proc_lock, .lock = nfs_proc_lock,
}; };
This diff is collapsed.
This diff is collapsed.
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define _LINUX_NFS_H #define _LINUX_NFS_H
#include <linux/sunrpc/msg_prot.h> #include <linux/sunrpc/msg_prot.h>
#include <linux/string.h>
#define NFS_PROGRAM 100003 #define NFS_PROGRAM 100003
#define NFS_PORT 2049 #define NFS_PORT 2049
...@@ -138,6 +139,22 @@ struct nfs_fh { ...@@ -138,6 +139,22 @@ struct nfs_fh {
unsigned char data[NFS_MAXFHSIZE]; unsigned char data[NFS_MAXFHSIZE];
}; };
/*
* Returns a zero iff the size and data fields match.
* Checks only "size" bytes in the data field.
*/
static inline int nfs_compare_fh(const struct nfs_fh *a, const struct nfs_fh *b)
{
return a->size != b->size || memcmp(a->data, b->data, a->size) != 0;
}
static inline void nfs_copy_fh(struct nfs_fh *target, const struct nfs_fh *source)
{
target->size = source->size;
memcpy(target->data, source->data, source->size);
}
/* /*
* This is really a general kernel constant, but since nothing like * This is really a general kernel constant, but since nothing like
* this is defined in the kernel headers, I have to do it here. * this is defined in the kernel headers, I have to do it here.
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#define NFS4_VERIFIER_SIZE 8 #define NFS4_VERIFIER_SIZE 8
#define NFS4_FHSIZE 128 #define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX #define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_ACCESS_READ 0x0001 #define NFS4_ACCESS_READ 0x0001
...@@ -354,7 +355,7 @@ enum { ...@@ -354,7 +355,7 @@ enum {
NFSPROC4_CLNT_COMMIT, NFSPROC4_CLNT_COMMIT,
NFSPROC4_CLNT_OPEN, NFSPROC4_CLNT_OPEN,
NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_OPEN_CONFIRM,
NFSPROC4_CLNT_OPEN_RECLAIM, NFSPROC4_CLNT_OPEN_NOATTR,
NFSPROC4_CLNT_OPEN_DOWNGRADE, NFSPROC4_CLNT_OPEN_DOWNGRADE,
NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_CLOSE,
NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_SETATTR,
...@@ -372,12 +373,14 @@ enum { ...@@ -372,12 +373,14 @@ enum {
NFSPROC4_CLNT_REMOVE, NFSPROC4_CLNT_REMOVE,
NFSPROC4_CLNT_RENAME, NFSPROC4_CLNT_RENAME,
NFSPROC4_CLNT_LINK, NFSPROC4_CLNT_LINK,
NFSPROC4_CLNT_SYMLINK,
NFSPROC4_CLNT_CREATE, NFSPROC4_CLNT_CREATE,
NFSPROC4_CLNT_PATHCONF, NFSPROC4_CLNT_PATHCONF,
NFSPROC4_CLNT_STATFS, NFSPROC4_CLNT_STATFS,
NFSPROC4_CLNT_READLINK, NFSPROC4_CLNT_READLINK,
NFSPROC4_CLNT_READDIR, NFSPROC4_CLNT_READDIR,
NFSPROC4_CLNT_SERVER_CAPS, NFSPROC4_CLNT_SERVER_CAPS,
NFSPROC4_CLNT_DELEGRETURN,
}; };
#endif #endif
......
This diff is collapsed.
...@@ -18,6 +18,7 @@ struct nfs_server { ...@@ -18,6 +18,7 @@ struct nfs_server {
unsigned int rpages; /* read size (in pages) */ unsigned int rpages; /* read size (in pages) */
unsigned int wsize; /* write size */ unsigned int wsize; /* write size */
unsigned int wpages; /* write size (in pages) */ unsigned int wpages; /* write size (in pages) */
unsigned int wtmult; /* server disk block size */
unsigned int dtsize; /* readdir size */ unsigned int dtsize; /* readdir size */
unsigned int bsize; /* server block size */ unsigned int bsize; /* server block size */
unsigned int acregmin; /* attr cache timeouts */ unsigned int acregmin; /* attr cache timeouts */
......
This diff is collapsed.
This diff is collapsed.
...@@ -69,7 +69,6 @@ u32 g_verify_token_header( ...@@ -69,7 +69,6 @@ u32 g_verify_token_header(
struct xdr_netobj *mech, struct xdr_netobj *mech,
int *body_size, int *body_size,
unsigned char **buf_in, unsigned char **buf_in,
int tok_type,
int toksize); int toksize);
u32 g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf); u32 g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf);
......
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.
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