aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/Kconfig35
-rw-r--r--fs/Makefile1
-rw-r--r--fs/lockd/clntlock.c113
-rw-r--r--fs/lockd/clntproc.c40
-rw-r--r--fs/lockd/host.c8
-rw-r--r--fs/lockd/mon.c7
-rw-r--r--fs/locks.c6
-rw-r--r--fs/nfs/Makefile1
-rw-r--r--fs/nfs/callback.c1
-rw-r--r--fs/nfs/callback_proc.c1
-rw-r--r--fs/nfs/callback_xdr.c2
-rw-r--r--fs/nfs/delegation.c1
-rw-r--r--fs/nfs/dir.c160
-rw-r--r--fs/nfs/direct.c2
-rw-r--r--fs/nfs/file.c48
-rw-r--r--fs/nfs/idmap.c1
-rw-r--r--fs/nfs/inode.c427
-rw-r--r--fs/nfs/mount_clnt.c4
-rw-r--r--fs/nfs/nfs3acl.c403
-rw-r--r--fs/nfs/nfs3proc.c43
-rw-r--r--fs/nfs/nfs3xdr.c147
-rw-r--r--fs/nfs/nfs4_fs.h253
-rw-r--r--fs/nfs/nfs4proc.c429
-rw-r--r--fs/nfs/nfs4renewd.c1
-rw-r--r--fs/nfs/nfs4state.c193
-rw-r--r--fs/nfs/nfs4xdr.c241
-rw-r--r--fs/nfs/nfsroot.c9
-rw-r--r--fs/nfs/pagelist.c142
-rw-r--r--fs/nfs/proc.c1
-rw-r--r--fs/nfs/read.c3
-rw-r--r--fs/nfs/write.c108
-rw-r--r--fs/nfs_common/Makefile7
-rw-r--r--fs/nfs_common/nfsacl.c257
-rw-r--r--fs/nfsd/Makefile2
-rw-r--r--fs/nfsd/nfs2acl.c336
-rw-r--r--fs/nfsd/nfs3acl.c267
-rw-r--r--fs/nfsd/nfs3xdr.c13
-rw-r--r--fs/nfsd/nfs4callback.c4
-rw-r--r--fs/nfsd/nfsproc.c1
-rw-r--r--fs/nfsd/nfssvc.c28
-rw-r--r--fs/nfsd/nfsxdr.c11
-rw-r--r--fs/nfsd/vfs.c107
-rw-r--r--include/linux/fs.h1
-rw-r--r--include/linux/lockd/lockd.h7
-rw-r--r--include/linux/nfs4.h2
-rw-r--r--include/linux/nfs_fs.h306
-rw-r--r--include/linux/nfs_fs_i.h5
-rw-r--r--include/linux/nfs_fs_sb.h1
-rw-r--r--include/linux/nfs_mount.h1
-rw-r--r--include/linux/nfs_page.h30
-rw-r--r--include/linux/nfs_xdr.h43
-rw-r--r--include/linux/nfsacl.h58
-rw-r--r--include/linux/nfsd/nfsd.h16
-rw-r--r--include/linux/nfsd/xdr.h4
-rw-r--r--include/linux/nfsd/xdr3.h26
-rw-r--r--include/linux/sunrpc/clnt.h6
-rw-r--r--include/linux/sunrpc/sched.h1
-rw-r--r--include/linux/sunrpc/svc.h14
-rw-r--r--include/linux/sunrpc/xdr.h21
-rw-r--r--net/sunrpc/auth.c6
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c18
-rw-r--r--net/sunrpc/clnt.c205
-rw-r--r--net/sunrpc/pmap_clnt.c9
-rw-r--r--net/sunrpc/sched.c84
-rw-r--r--net/sunrpc/sunrpc_syms.c6
-rw-r--r--net/sunrpc/svc.c36
-rw-r--r--net/sunrpc/xdr.c298
-rw-r--r--net/sunrpc/xprt.c71
68 files changed, 4135 insertions, 1004 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index 178e27494b7..a7c0cc3203c 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1268,6 +1268,7 @@ config NFS_FS
depends on INET
select LOCKD
select SUNRPC
+ select NFS_ACL_SUPPORT if NFS_V3_ACL
help
If you are connected to some other (usually local) Unix computer
(using SLIP, PLIP, PPP or Ethernet) and want to mount files residing
@@ -1310,6 +1311,16 @@ config NFS_V3
If unsure, say Y.
+config NFS_V3_ACL
+ bool "Provide client support for the NFSv3 ACL protocol extension"
+ depends on NFS_V3
+ help
+ Implement the NFSv3 ACL protocol extension for manipulating POSIX
+ Access Control Lists. The server should also be compiled with
+ the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option.
+
+ If unsure, say N.
+
config NFS_V4
bool "Provide NFSv4 client support (EXPERIMENTAL)"
depends on NFS_FS && EXPERIMENTAL
@@ -1353,6 +1364,7 @@ config NFSD
select LOCKD
select SUNRPC
select EXPORTFS
+ select NFS_ACL_SUPPORT if NFSD_V3_ACL || NFSD_V2_ACL
help
If you want your Linux box to act as an NFS *server*, so that other
computers on your local network which support NFS can access certain
@@ -1376,6 +1388,10 @@ config NFSD
To compile the NFS server support as a module, choose M here: the
module will be called nfsd. If unsure, say N.
+config NFSD_V2_ACL
+ bool
+ depends on NFSD
+
config NFSD_V3
bool "Provide NFSv3 server support"
depends on NFSD
@@ -1383,6 +1399,16 @@ config NFSD_V3
If you would like to include the NFSv3 server as well as the NFSv2
server, say Y here. If unsure, say Y.
+config NFSD_V3_ACL
+ bool "Provide server support for the NFSv3 ACL protocol extension"
+ depends on NFSD_V3
+ select NFSD_V2_ACL
+ help
+ Implement the NFSv3 ACL protocol extension for manipulating POSIX
+ Access Control Lists on exported file systems. NFS clients should
+ be compiled with the NFSv3 ACL protocol extension; see the
+ CONFIG_NFS_V3_ACL option. If unsure, say N.
+
config NFSD_V4
bool "Provide NFSv4 server support (EXPERIMENTAL)"
depends on NFSD_V3 && EXPERIMENTAL
@@ -1427,6 +1453,15 @@ config LOCKD_V4
config EXPORTFS
tristate
+config NFS_ACL_SUPPORT
+ tristate
+ select FS_POSIX_ACL
+
+config NFS_COMMON
+ bool
+ depends on NFSD || NFS_FS
+ default y
+
config SUNRPC
tristate
diff --git a/fs/Makefile b/fs/Makefile
index 443f2bc56cc..fc92e59e9fa 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o
obj-$(CONFIG_FS_MBCACHE) += mbcache.o
obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
+obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_QUOTA) += dquot.o
obj-$(CONFIG_QFMT_V1) += quota_v1.o
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index ef7103b8c5b..006bb9e1457 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -31,7 +31,7 @@ static int reclaimer(void *ptr);
* This is the representation of a blocked client lock.
*/
struct nlm_wait {
- struct nlm_wait * b_next; /* linked list */
+ struct list_head b_list; /* linked list */
wait_queue_head_t b_wait; /* where to wait on */
struct nlm_host * b_host;
struct file_lock * b_lock; /* local file lock */
@@ -39,27 +39,54 @@ struct nlm_wait {
u32 b_status; /* grant callback status */
};
-static struct nlm_wait * nlm_blocked;
+static LIST_HEAD(nlm_blocked);
/*
- * Block on a lock
+ * Queue up a lock for blocking so that the GRANTED request can see it
*/
-int
-nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
+int nlmclnt_prepare_block(struct nlm_rqst *req, struct nlm_host *host, struct file_lock *fl)
+{
+ struct nlm_wait *block;
+
+ BUG_ON(req->a_block != NULL);
+ block = kmalloc(sizeof(*block), GFP_KERNEL);
+ if (block == NULL)
+ return -ENOMEM;
+ block->b_host = host;
+ block->b_lock = fl;
+ init_waitqueue_head(&block->b_wait);
+ block->b_status = NLM_LCK_BLOCKED;
+
+ list_add(&block->b_list, &nlm_blocked);
+ req->a_block = block;
+
+ return 0;
+}
+
+void nlmclnt_finish_block(struct nlm_rqst *req)
{
- struct nlm_wait block, **head;
- int err;
- u32 pstate;
+ struct nlm_wait *block = req->a_block;
- block.b_host = host;
- block.b_lock = fl;
- init_waitqueue_head(&block.b_wait);
- block.b_status = NLM_LCK_BLOCKED;
- block.b_next = nlm_blocked;
- nlm_blocked = █
+ if (block == NULL)
+ return;
+ req->a_block = NULL;
+ list_del(&block->b_list);
+ kfree(block);
+}
+
+/*
+ * Block on a lock
+ */
+long nlmclnt_block(struct nlm_rqst *req, long timeout)
+{
+ struct nlm_wait *block = req->a_block;
+ long ret;
- /* Remember pseudo nsm state */
- pstate = host->h_state;
+ /* A borken server might ask us to block even if we didn't
+ * request it. Just say no!
+ */
+ if (!req->a_args.block)
+ return -EAGAIN;
/* Go to sleep waiting for GRANT callback. Some servers seem
* to lose callbacks, however, so we're going to poll from
@@ -69,28 +96,16 @@ nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
* a 1 minute timeout would do. See the comment before
* nlmclnt_lock for an explanation.
*/
- sleep_on_timeout(&block.b_wait, 30*HZ);
-
- for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
- if (*head == &block) {
- *head = block.b_next;
- break;
- }
- }
+ ret = wait_event_interruptible_timeout(block->b_wait,
+ block->b_status != NLM_LCK_BLOCKED,
+ timeout);
- if (!signalled()) {
- *statp = block.b_status;
- return 0;
+ if (block->b_status != NLM_LCK_BLOCKED) {
+ req->a_res.status = block->b_status;
+ block->b_status = NLM_LCK_BLOCKED;
}
- /* Okay, we were interrupted. Cancel the pending request
- * unless the server has rebooted.
- */
- if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0)
- printk(KERN_NOTICE
- "lockd: CANCEL call failed (errno %d)\n", -err);
-
- return -ERESTARTSYS;
+ return ret;
}
/*
@@ -100,27 +115,23 @@ u32
nlmclnt_grant(struct nlm_lock *lock)
{
struct nlm_wait *block;
+ u32 res = nlm_lck_denied;
/*
* Look up blocked request based on arguments.
* Warning: must not use cookie to match it!
*/
- for (block = nlm_blocked; block; block = block->b_next) {
- if (nlm_compare_locks(block->b_lock, &lock->fl))
- break;
+ list_for_each_entry(block, &nlm_blocked, b_list) {
+ if (nlm_compare_locks(block->b_lock, &lock->fl)) {
+ /* Alright, we found a lock. Set the return status
+ * and wake up the caller
+ */
+ block->b_status = NLM_LCK_GRANTED;
+ wake_up(&block->b_wait);
+ res = nlm_granted;
+ }
}
-
- /* Ooops, no blocked request found. */
- if (block == NULL)
- return nlm_lck_denied;
-
- /* Alright, we found the lock. Set the return status and
- * wake up the caller.
- */
- block->b_status = NLM_LCK_GRANTED;
- wake_up(&block->b_wait);
-
- return nlm_granted;
+ return res;
}
/*
@@ -230,7 +241,7 @@ restart:
host->h_reclaiming = 0;
/* Now, wake up all processes that sleep on a blocked lock */
- for (block = nlm_blocked; block; block = block->b_next) {
+ list_for_each_entry(block, &nlm_blocked, b_list) {
if (block->b_host == host) {
block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
wake_up(&block->b_wait);
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index a4407619b1f..fd77ed1d710 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -21,6 +21,7 @@
#define NLMDBG_FACILITY NLMDBG_CLIENT
#define NLMCLNT_GRACE_WAIT (5*HZ)
+#define NLMCLNT_POLL_TIMEOUT (30*HZ)
static int nlmclnt_test(struct nlm_rqst *, struct file_lock *);
static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
@@ -553,7 +554,8 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
{
struct nlm_host *host = req->a_host;
struct nlm_res *resp = &req->a_res;
- int status;
+ long timeout;
+ int status;
if (!host->h_monitored && nsm_monitor(host) < 0) {
printk(KERN_NOTICE "lockd: failed to monitor %s\n",
@@ -562,15 +564,32 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
goto out;
}
- do {
- if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
- if (resp->status != NLM_LCK_BLOCKED)
- break;
- status = nlmclnt_block(host, fl, &resp->status);
- }
+ if (req->a_args.block) {
+ status = nlmclnt_prepare_block(req, host, fl);
if (status < 0)
goto out;
- } while (resp->status == NLM_LCK_BLOCKED && req->a_args.block);
+ }
+ for(;;) {
+ status = nlmclnt_call(req, NLMPROC_LOCK);
+ if (status < 0)
+ goto out_unblock;
+ if (resp->status != NLM_LCK_BLOCKED)
+ break;
+ /* Wait on an NLM blocking lock */
+ timeout = nlmclnt_block(req, NLMCLNT_POLL_TIMEOUT);
+ /* Did a reclaimer thread notify us of a server reboot? */
+ if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD)
+ continue;
+ if (resp->status != NLM_LCK_BLOCKED)
+ break;
+ if (timeout >= 0)
+ continue;
+ /* We were interrupted. Send a CANCEL request to the server
+ * and exit
+ */
+ status = (int)timeout;
+ goto out_unblock;
+ }
if (resp->status == NLM_LCK_GRANTED) {
fl->fl_u.nfs_fl.state = host->h_state;
@@ -579,6 +598,11 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
do_vfs_lock(fl);
}
status = nlm_stat_to_errno(resp->status);
+out_unblock:
+ nlmclnt_finish_block(req);
+ /* Cancel the blocked request if it is still pending */
+ if (resp->status == NLM_LCK_BLOCKED)
+ nlmclnt_cancel(host, fl);
out:
nlmclnt_release_lockargs(req);
return status;
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index 52707c5ad6e..82c77df81c5 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -189,17 +189,15 @@ nlm_bind_host(struct nlm_host *host)
goto forgetit;
xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout);
+ xprt->nocong = 1; /* No congestion control for NLM */
+ xprt->resvport = 1; /* NLM requires a reserved port */
/* Existing NLM servers accept AUTH_UNIX only */
clnt = rpc_create_client(xprt, host->h_name, &nlm_program,
host->h_version, RPC_AUTH_UNIX);
- if (IS_ERR(clnt)) {
- xprt_destroy(xprt);
+ if (IS_ERR(clnt))
goto forgetit;
- }
clnt->cl_autobind = 1; /* turn on pmap queries */
- xprt->nocong = 1; /* No congestion control for NLM */
- xprt->resvport = 1; /* NLM requires a reserved port */
host->h_rpcclnt = clnt;
}
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 6fc1bebeec1..2d144abe84a 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -115,20 +115,19 @@ nsm_create(void)
xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
+ xprt->resvport = 1; /* NSM requires a reserved port */
clnt = rpc_create_client(xprt, "localhost",
&nsm_program, SM_VERSION,
RPC_AUTH_NULL);
if (IS_ERR(clnt))
- goto out_destroy;
+ goto out_err;
clnt->cl_softrtry = 1;
clnt->cl_chatty = 1;
clnt->cl_oneshot = 1;
- xprt->resvport = 1; /* NSM requires a reserved port */
return clnt;
-out_destroy:
- xprt_destroy(xprt);
+out_err:
return clnt;
}
diff --git a/fs/locks.c b/fs/locks.c
index 3fa6a7ce57a..a0bc03495bd 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1548,6 +1548,8 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
if (filp->f_op && filp->f_op->lock) {
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
+ if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
+ file_lock.fl_ops->fl_release_private(&file_lock);
if (error < 0)
goto out;
else
@@ -1690,6 +1692,8 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
if (filp->f_op && filp->f_op->lock) {
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
+ if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
+ file_lock.fl_ops->fl_release_private(&file_lock);
if (error < 0)
goto out;
else
@@ -1873,6 +1877,8 @@ void locks_remove_flock(struct file *filp)
.fl_end = OFFSET_MAX,
};
filp->f_op->flock(filp, F_SETLKW, &fl);
+ if (fl.fl_ops && fl.fl_ops->fl_release_private)
+ fl.fl_ops->fl_release_private(&fl);
}
lock_kernel();
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index b4baa031edf..8b3bb715d17 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -8,6 +8,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \
proc.o read.o symlink.o unlink.o write.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
+nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
delegation.o idmap.o \
callback.o callback_xdr.o callback_proc.o
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 560d6175dd5..f2ca782aba3 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -14,6 +14,7 @@
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/nfs_fs.h>
+#include "nfs4_fs.h"
#include "callback.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index ece27e42b93..65f1e19e4d1 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -8,6 +8,7 @@
#include <linux/config.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
+#include "nfs4_fs.h"
#include "callback.h"
#include "delegation.h"
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index d271df9df2b..7c33b9a81a9 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -10,6 +10,7 @@
#include <linux/sunrpc/svc.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
+#include "nfs4_fs.h"
#include "callback.h"
#define CB_OP_TAGLEN_MAXSZ (512)
@@ -410,7 +411,6 @@ static int nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp
xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base);
p = (uint32_t*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
- rqstp->rq_res.head[0].iov_len = PAGE_SIZE;
xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
decode_compound_hdr_arg(&xdr_in, &hdr_arg);
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 5b9c60f9779..d7f7eb669d0 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -16,6 +16,7 @@
#include <linux/nfs_fs.h>
#include <linux/nfs_xdr.h>
+#include "nfs4_fs.h"
#include "delegation.h"
static struct nfs_delegation *nfs_alloc_delegation(void)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ff6155f5e8d..b38a57e78a6 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -32,6 +32,7 @@
#include <linux/smp_lock.h>
#include <linux/namei.h>
+#include "nfs4_fs.h"
#include "delegation.h"
#define NFS_PARANOIA 1
@@ -50,8 +51,10 @@ static int nfs_mknod(struct inode *, struct dentry *, int, dev_t);
static int nfs_rename(struct inode *, struct dentry *,
struct inode *, struct dentry *);
static int nfs_fsync_dir(struct file *, struct dentry *, int);
+static loff_t nfs_llseek_dir(struct file *, loff_t, int);
struct file_operations nfs_dir_operations = {
+ .llseek = nfs_llseek_dir,
.read = generic_read_dir,
.readdir = nfs_readdir,
.open = nfs_opendir,
@@ -74,6 +77,27 @@ struct inode_operations nfs_dir_inode_operations = {
.setattr = nfs_setattr,
};
+#ifdef CONFIG_NFS_V3
+struct inode_operations nfs3_dir_inode_operations = {
+ .create = nfs_create,
+ .lookup = nfs_lookup,
+ .link = nfs_link,
+ .unlink = nfs_unlink,
+ .symlink = nfs_symlink,
+ .mkdir = nfs_mkdir,
+ .rmdir = nfs_rmdir,
+ .mknod = nfs_mknod,
+ .rename = nfs_rename,
+ .permission = nfs_permission,
+ .getattr = nfs_getattr,
+ .setattr = nfs_setattr,
+ .listxattr = nfs3_listxattr,
+ .getxattr = nfs3_getxattr,
+ .setxattr = nfs3_setxattr,
+ .removexattr = nfs3_removexattr,
+};
+#endif /* CONFIG_NFS_V3 */
+
#ifdef CONFIG_NFS_V4
static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *);
@@ -90,6 +114,9 @@ struct inode_operations nfs4_dir_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
+ .getxattr = nfs4_getxattr,
+ .setxattr = nfs4_setxattr,
+ .listxattr = nfs4_listxattr,
};
#endif /* CONFIG_NFS_V4 */
@@ -116,7 +143,8 @@ typedef struct {
struct page *page;
unsigned long page_index;
u32 *ptr;
- u64 target;
+ u64 *dir_cookie;
+ loff_t current_index;
struct nfs_entry *entry;
decode_dirent_t decode;
int plus;
@@ -164,12 +192,10 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
/* Ensure consistent page alignment of the data.
* Note: assumes we have exclusive access to this mapping either
- * throught inode->i_sem or some other mechanism.
+ * through inode->i_sem or some other mechanism.
*/
- if (page->index == 0) {
- invalidate_inode_pages(inode->i_mapping);
- NFS_I(inode)->readdir_timestamp = timestamp;
- }
+ if (page->index == 0)
+ invalidate_inode_pages2_range(inode->i_mapping, PAGE_CACHE_SIZE, -1);
unlock_page(page);
return 0;
error:
@@ -202,22 +228,22 @@ void dir_page_release(nfs_readdir_descriptor_t *desc)
/*
* Given a pointer to a buffer that has already been filled by a call
- * to readdir, find the next entry.
+ * to readdir, find the next entry with cookie '*desc->dir_cookie'.
*
* If the end of the buffer has been reached, return -EAGAIN, if not,
* return the offset within the buffer of the next entry to be
* read.
*/
static inline
-int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
+int find_dirent(nfs_readdir_descriptor_t *desc)
{
struct nfs_entry *entry = desc->entry;
int loop_count = 0,
status;
while((status = dir_decode(desc)) == 0) {
- dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie);
- if (entry->prev_cookie == desc->target)
+ dfprintk(VFS, "NFS: found cookie %Lu\n", (unsigned long long)entry->cookie);
+ if (entry->prev_cookie == *desc->dir_cookie)
break;
if (loop_count++ > 200) {
loop_count = 0;
@@ -229,8 +255,44 @@ int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
}
/*
- * Find the given page, and call find_dirent() in order to try to
- * return the next entry.
+ * Given a pointer to a buffer that has already been filled by a call
+ * to readdir, find the entry at offset 'desc->file->f_pos'.
+ *
+ * If the end of the buffer has been reached, return -EAGAIN, if not,
+ * return the offset within the buffer of the next entry to be
+ * read.
+ */
+static inline
+int find_dirent_index(nfs_readdir_descriptor_t *desc)
+{
+ struct nfs_entry *entry = desc->entry;
+ int loop_count = 0,
+ status;
+
+ for(;;) {
+ status = dir_decode(desc);
+ if (status)
+ break;
+
+ dfprintk(VFS, "NFS: found cookie %Lu at index %Ld\n", (unsigned long long)entry->cookie, desc->current_index);
+
+ if (desc->file->f_pos == desc->current_index) {
+ *desc->dir_cookie = entry->cookie;
+ break;
+ }
+ desc->current_index++;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
+ }
+ dfprintk(VFS, "NFS: find_dirent_index() returns %d\n", status);
+ return status;
+}
+
+/*
+ * Find the given page, and call find_dirent() or find_dirent_index in
+ * order to try to return the next entry.
*/
static inline
int find_dirent_page(nfs_readdir_descriptor_t *desc)
@@ -253,7 +315,10 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
/* NOTE: Someone else may have changed the READDIRPLUS flag */
desc->page = page;
desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */
- status = find_dirent(desc, page);
+ if (*desc->dir_cookie != 0)
+ status = find_dirent(desc);
+ else
+ status = find_dirent_index(desc);
if (status < 0)
dir_page_release(desc);
out:
@@ -268,7 +333,8 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
* Recurse through the page cache pages, and return a
* filled nfs_entry structure of the next directory entry if possible.
*
- * The target for the search is 'desc->target'.
+ * The target for the search is '*desc->dir_cookie' if non-0,
+ * 'desc->file->f_pos' otherwise
*/
static inline
int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
@@ -276,7 +342,16 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
int loop_count = 0;
int res;
- dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target);
+ /* Always search-by-index from the beginning of the cache */
+ if (*desc->dir_cookie == 0) {
+ dfprintk(VFS, "NFS: readdir_search_pagecache() searching for offset %Ld\n", (long long)desc->file->f_pos);
+ desc->page_index = 0;
+ desc->entry->cookie = desc->entry->prev_cookie = 0;
+ desc->entry->eof = 0;
+ desc->current_index = 0;
+ } else
+ dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie);
+
for (;;) {
res = find_dirent_page(desc);
if (res != -EAGAIN)
@@ -313,7 +388,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
int loop_count = 0,
res;
- dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target);
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)entry->cookie);
for(;;) {
unsigned d_type = DT_UNKNOWN;
@@ -333,10 +408,11 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
}
res = filldir(dirent, entry->name, entry->len,
- entry->prev_cookie, fileid, d_type);
+ file->f_pos, fileid, d_type);
if (res < 0)
break;
- file->f_pos = desc->target = entry->cookie;
+ file->f_pos++;
+ *desc->dir_cookie = entry->cookie;
if (dir_decode(desc) != 0) {
desc->page_index ++;
break;
@@ -349,7 +425,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
dir_page_release(desc);
if (dentry != NULL)
dput(dentry);
- dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res);
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (unsigned long long)*desc->dir_cookie, res);
return res;
}
@@ -375,14 +451,14 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
struct page *page = NULL;
int status;
- dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target);
+ dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie);
page = alloc_page(GFP_HIGHUSER);
if (!page) {
status = -ENOMEM;
goto out;
}
- desc->error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->target,
+ desc->error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, *desc->dir_cookie,
page,
NFS_SERVER(inode)->dtsize,
desc->plus);
@@ -391,7 +467,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */
if (desc->error >= 0) {
if ((status = dir_decode(desc)) == 0)
- desc->entry->prev_cookie = desc->target;
+ desc->entry->prev_cookie = *desc->dir_cookie;
} else
status = -EIO;
if (status < 0)
@@ -412,8 +488,9 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
goto out;
}
-/* The file offset position is now represented as a true offset into the
- * page cache as is the case in most of the other filesystems.
+/* The file offset position represents the dirent entry number. A
+ last cookie cache takes care of the common case of reading the
+ whole directory.
*/
static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
@@ -435,15 +512,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
}
/*
- * filp->f_pos points to the file offset in the page cache.
- * but if the cache has meanwhile been zapped, we need to
- * read from the last dirent to revalidate f_pos
- * itself.
+ * filp->f_pos points to the dirent entry number.
+ * *desc->dir_cookie has the cookie for the next entry. We have
+ * to either find the entry with the appropriate number or
+ * revalidate the cookie.
*/
memset(desc, 0, sizeof(*desc));
desc->file = filp;
- desc->target = filp->f_pos;
+ desc->dir_cookie = &((struct nfs_open_context *)filp->private_data)->dir_cookie;
desc->decode = NFS_PROTO(inode)->decode_dirent;
desc->plus = NFS_USE_READDIRPLUS(inode);
@@ -455,9 +532,10 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
while(!desc->entry->eof) {
res = readdir_search_pagecache(desc);
+
if (res == -EBADCOOKIE) {
/* This means either end of directory */
- if (desc->entry->cookie != desc->target) {
+ if (*desc->dir_cookie && desc->entry->cookie != *desc->dir_cookie) {
/* Or that the server has 'lost' a cookie */
res = uncached_readdir(desc, dirent, filldir);
if (res >= 0)
@@ -490,6 +568,28 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
return 0;
}
+loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
+{
+ down(&filp->f_dentry->d_inode->i_sem);
+ switch (origin) {
+ case 1:
+ offset += filp->f_pos;
+ case 0:
+ if (offset >= 0)
+ break;
+ default:
+ offset = -EINVAL;
+ goto out;
+ }
+ if (offset != filp->f_pos) {
+ filp->f_pos = offset;
+ ((struct nfs_open_context *)filp->private_data)->dir_cookie = 0;
+ }
+out:
+ up(&filp->f_dentry->d_inode->i_sem);
+ return offset;
+}
+
/*
* All directory operations under NFS are synchronous, so fsync()
* is a dummy operation.
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 68df803f27c..d6a30c844de 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -517,7 +517,7 @@ retry:
result = tot_bytes;
out:
- nfs_end_data_update_defer(inode);
+ nfs_end_data_update(inode);
nfs_writedata_free(wdata);
return result;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 55c90759249..5621ba9885f 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -71,6 +71,18 @@ struct inode_operations nfs_file_inode_operations = {
.setattr = nfs_setattr,
};
+#ifdef CONFIG_NFS_V3
+struct inode_operations nfs3_file_inode_operations = {
+ .permission = nfs_permission,
+ .getattr = nfs_getattr,
+ .setattr = nfs_setattr,
+ .listxattr = nfs3_listxattr,
+ .getxattr = nfs3_getxattr,
+ .setxattr = nfs3_setxattr,
+ .removexattr = nfs3_removexattr,
+};
+#endif /* CONFIG_NFS_v3 */
+
/* Hack for future NFS swap support */
#ifndef IS_SWAPFILE
# define IS_SWAPFILE(inode) (0)
@@ -116,6 +128,21 @@ nfs_file_release(struct inode *inode, struct file *filp)
}
/**
+ * nfs_revalidate_file - Revalidate the page cache & related metadata
+ * @inode - pointer to inode struct
+ * @file - pointer to file
+ */
+static int nfs_revalidate_file(struct inode *inode, struct file *filp)
+{
+ int retval = 0;
+
+ if ((NFS_FLAGS(inode) & NFS_INO_REVAL_PAGECACHE) || nfs_attribute_timeout(inode))
+ retval = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ nfs_revalidate_mapping(inode, filp->f_mapping);
+ return 0;
+}
+
+/**
* nfs_revalidate_size - Revalidate the file size
* @inode - pointer to inode struct
* @file - pointer to struct file
@@ -137,7 +164,8 @@ static int nfs_revalidate_file_size(struct inode *inode, struct file *filp)
goto force_reval;
if (nfsi->npages != 0)
return 0;
- return nfs_revalidate_inode(server, inode);
+ if (!(NFS_FLAGS(inode) & NFS_INO_REVAL_PAGECACHE) && !nfs_attribute_timeout(inode))
+ return 0;
force_reval:
return __nfs_revalidate_inode(server, inode);
}
@@ -198,7 +226,7 @@ nfs_file_read(struct kiocb *iocb, char __user * buf, size_t count, loff_t pos)
dentry->d_parent->d_name.name, dentry->d_name.name,
(unsigned long) count, (unsigned long) pos);
- result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ result = nfs_revalidate_file(inode, iocb->ki_filp);
if (!result)
result = generic_file_aio_read(iocb, buf, count, pos);
return result;
@@ -216,7 +244,7 @@ nfs_file_sendfile(struct file *filp, loff_t *ppos, size_t count,
dentry->d_parent->d_name.name, dentry->d_name.name,
(unsigned long) count, (unsigned long long) *ppos);
- res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ res = nfs_revalidate_file(inode, filp);
if (!res)
res = generic_file_sendfile(filp, ppos, count, actor, target);
return res;
@@ -232,7 +260,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
dfprintk(VFS, "nfs: mmap(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ status = nfs_revalidate_file(inode, file);
if (!status)
status = generic_file_mmap(file, vma);
return status;
@@ -321,9 +349,15 @@ nfs_file_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t
result = -EBUSY;
if (IS_SWAPFILE(inode))
goto out_swapfile;
- result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
- if (result)
- goto out;
+ /*
+ * O_APPEND implies that we must revalidate the file length.
+ */
+ if (iocb->ki_filp->f_flags & O_APPEND) {
+ result = nfs_revalidate_file_size(inode, iocb->ki_filp);
+ if (result)
+ goto out;
+ }
+ nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping);
result = count;
if (!count)
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c
index 87f4f9aeac8..ffb8df91dc3 100644
--- a/fs/nfs/idmap.c
+++ b/fs/nfs/idmap.c
@@ -50,6 +50,7 @@
#include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h>
+#include "nfs4_fs.h"
#define IDMAP_HASH_SZ 128
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index f2317f3e29f..4845911f1c6 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -39,6 +39,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#include "nfs4_fs.h"
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_VFS
@@ -63,6 +64,7 @@ static void nfs_clear_inode(struct inode *);
static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct super_block *, struct kstatfs *);
static int nfs_show_options(struct seq_file *, struct vfsmount *);
+static void nfs_zap_acl_cache(struct inode *);
static struct rpc_program nfs_program;
@@ -106,6 +108,21 @@ static struct rpc_program nfs_program = {
.pipe_dir_name = "/nfs",
};
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program };
+static struct rpc_version * nfsacl_version[] = {
+ [3] = &nfsacl_version3,
+};
+
+struct rpc_program nfsacl_program = {
+ .name = "nfsacl",
+ .number = NFS_ACL_PROGRAM,
+ .nrvers = sizeof(nfsacl_version) / sizeof(nfsacl_version[0]),
+ .version = nfsacl_version,
+ .stats = &nfsacl_rpcstat,
+};
+#endif /* CONFIG_NFS_V3_ACL */
+
static inline unsigned long
nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
{
@@ -118,7 +135,7 @@ nfs_write_inode(struct inode *inode, int sync)
int flags = sync ? FLUSH_WAIT : 0;
int ret;
- ret = nfs_commit_inode(inode, 0, 0, flags);
+ ret = nfs_commit_inode(inode, flags);
if (ret < 0)
return ret;
return 0;
@@ -140,10 +157,6 @@ nfs_delete_inode(struct inode * inode)
clear_inode(inode);
}
-/*
- * For the moment, the only task for the NFS clear_inode method is to
- * release the mmap credential
- */
static void
nfs_clear_inode(struct inode *inode)
{
@@ -152,6 +165,7 @@ nfs_clear_inode(struct inode *inode)
nfs_wb_all(inode);
BUG_ON (!list_empty(&nfsi->open_files));
+ nfs_zap_acl_cache(inode);
cred = nfsi->cache_access.cred;
if (cred)
put_rpccred(cred);
@@ -161,11 +175,13 @@ nfs_clear_inode(struct inode *inode)
void
nfs_umount_begin(struct super_block *sb)
{
- struct nfs_server *server = NFS_SB(sb);
- struct rpc_clnt *rpc;
+ struct rpc_clnt *rpc = NFS_SB(sb)->client;
/* -EIO all pending I/O */
- if ((rpc = server->client) != NULL)
+ if (!IS_ERR(rpc))
+ rpc_killall_tasks(rpc);
+ rpc = NFS_SB(sb)->client_acl;
+ if (!IS_ERR(rpc))
rpc_killall_tasks(rpc);
}
@@ -366,13 +382,15 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)
xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP,
&server->addr, &timeparms);
if (IS_ERR(xprt)) {
- printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
+ dprintk("%s: cannot create RPC transport. Error = %ld\n",
+ __FUNCTION__, PTR_ERR(xprt));
return (struct rpc_clnt *)xprt;
}
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
server->rpc_ops->version, data->pseudoflavor);
if (IS_ERR(clnt)) {
- printk(KERN_WARNING "NFS: cannot create RPC client.\n");
+ dprintk("%s: cannot create RPC client. Error = %ld\n",
+ __FUNCTION__, PTR_ERR(xprt));
goto out_fail;
}
@@ -383,7 +401,6 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)
return clnt;
out_fail:
- xprt_destroy(xprt);
return clnt;
}
@@ -427,21 +444,16 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
/* Check NFS protocol revision and initialize RPC op vector
* and file handle pool. */
- if (server->flags & NFS_MOUNT_VER3) {
#ifdef CONFIG_NFS_V3
+ if (server->flags & NFS_MOUNT_VER3) {
server->rpc_ops = &nfs_v3_clientops;
server->caps |= NFS_CAP_READDIRPLUS;
- if (data->version < 4) {
- printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
- return -EIO;
- }
-#else
- printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
- return -EIO;
-#endif
} else {
server->rpc_ops = &nfs_v2_clientops;
}
+#else
+ server->rpc_ops = &nfs_v2_clientops;
+#endif
/* Fill in pseudoflavor for mount version < 5 */
if (!(data->flags & NFS_MOUNT_SECFLAVOUR))
@@ -455,17 +467,34 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
return PTR_ERR(server->client);
/* RFC 2623, sec 2.3.2 */
if (authflavor != RPC_AUTH_UNIX) {
+ struct rpc_auth *auth;
+
server->client_sys = rpc_clone_client(server->client);
if (IS_ERR(server->client_sys))
return PTR_ERR(server->client_sys);
- if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys))
- return -ENOMEM;
+ auth = rpcauth_create(RPC_AUTH_UNIX, server->client_sys);
+ if (IS_ERR(auth))
+ return PTR_ERR(auth);
} else {
atomic_inc(&server->client->cl_count);
server->client_sys = server->client;
}
-
if (server->flags & NFS_MOUNT_VER3) {
+#ifdef CONFIG_NFS_V3_ACL
+ if (!(server->flags & NFS_MOUNT_NOACL)) {
+ server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
+ /* No errors! Assume that Sun nfsacls are supported */
+ if (!IS_ERR(server->client_acl))
+ server->caps |= NFS_CAP_ACLS;
+ }
+#else
+ server->flags &= ~NFS_MOUNT_NOACL;
+#endif /* CONFIG_NFS_V3_ACL */
+ /*
+ * The VFS shouldn't apply the umask to mode bits. We will
+ * do so ourselves when necessary.
+ */
+ sb->s_flags |= MS_POSIXACL;
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN;
sb->s_time_gran = 1;
@@ -549,6 +578,7 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
{ NFS_MOUNT_NOCTO, ",nocto", "" },
{ NFS_MOUNT_NOAC, ",noac", "" },
{ NFS_MOUNT_NONLM, ",nolock", ",lock" },
+ { NFS_MOUNT_NOACL, ",noacl", "" },
{ 0, NULL, NULL }
};
struct proc_nfs_info *nfs_infop;
@@ -590,9 +620,19 @@ nfs_zap_caches(struct inode *inode)
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
- nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
+ nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
else
- nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
+ nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
+}
+
+static void nfs_zap_acl_cache(struct inode *inode)
+{
+ void (*clear_acl_cache)(struct inode *);
+
+ clear_acl_cache = NFS_PROTO(inode)->clear_acl_cache;
+ if (clear_acl_cache != NULL)
+ clear_acl_cache(inode);
+ NFS_I(inode)->flags &= ~NFS_INO_INVALID_ACL;
}
/*
@@ -689,7 +729,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
/* Why so? Because we want revalidate for devices/FIFOs, and
* that's precisely what we have in nfs_file_inode_operations.
*/
- inode->i_op = &nfs_file_inode_operations;
+ inode->i_op = NFS_SB(sb)->rpc_ops->file_inode_ops;
if (S_ISREG(inode->i_mode)) {
inode->i_fop = &nfs_file_operations;
inode->i_data.a_ops = &nfs_file_aops;
@@ -792,7 +832,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
}
}
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
- NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS;
+ NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
nfs_end_data_update(inode);
unlock_kernel();
return error;
@@ -851,7 +891,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rp
ctx->state = NULL;
ctx->lockowner = current->files;
ctx->error = 0;
- init_waitqueue_head(&ctx->waitq);
+ ctx->dir_cookie = 0;
}
return ctx;
}
@@ -1015,6 +1055,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
goto out;
}
flags = nfsi->flags;
+ nfsi->flags &= ~NFS_INO_REVAL_PAGECACHE;
/*
* We may need to keep the attributes marked as invalid if
* we raced with nfs_end_attr_update().
@@ -1022,21 +1063,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
if (verifier == nfsi->cache_change_attribute)
nfsi->flags &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
/* Do the page cache invalidation */
- if (flags & NFS_INO_INVALID_DATA) {
- if (S_ISREG(inode->i_mode)) {
- if (filemap_fdatawrite(inode->i_mapping) == 0)
- filemap_fdatawait(inode->i_mapping);
- nfs_wb_all(inode);
- }
- nfsi->flags &= ~NFS_INO_INVALID_DATA;
- invalidate_inode_pages2(inode->i_mapping);
- memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
- dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
- inode->i_sb->s_id,
- (long long)NFS_FILEID(inode));
- /* This ensures we revalidate dentries */
- nfsi->cache_change_attribute++;
- }
+ nfs_revalidate_mapping(inode, inode->i_mapping);
+ if (flags & NFS_INO_INVALID_ACL)
+ nfs_zap_acl_cache(inode);
dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
@@ -1074,6 +1103,34 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
}
/**
+ * nfs_revalidate_mapping - Revalidate the pagecache
+ * @inode - pointer to host inode
+ * @mapping - pointer to mapping
+ */
+void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ if (nfsi->flags & NFS_INO_INVALID_DATA) {
+ if (S_ISREG(inode->i_mode)) {
+ if (filemap_fdatawrite(mapping) == 0)
+ filemap_fdatawait(mapping);
+ nfs_wb_all(inode);
+ }
+ invalidate_inode_pages2(mapping);
+ nfsi->flags &= ~NFS_INO_INVALID_DATA;
+ if (S_ISDIR(inode->i_mode)) {
+ memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
+ /* This ensures we revalidate child dentries */
+ nfsi->cache_change_attribute++;
+ }
+ dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
+ inode->i_sb->s_id,
+ (long long)NFS_FILEID(inode));
+ }
+}
+
+/**
* nfs_begin_data_update
* @inode - pointer to inode
* Declare that a set of operations will update file data on the server
@@ -1106,27 +1163,6 @@ void nfs_end_data_update(struct inode *inode)
}
/**
- * nfs_end_data_update_defer
- * @inode - pointer to inode
- * Declare end of the operations that will update file data
- * This will defer marking the inode as needing revalidation
- * unless there are no other pending updates.
- */
-void nfs_end_data_update_defer(struct inode *inode)
-{
- struct nfs_inode *nfsi = NFS_I(inode);
-
- if (atomic_dec_and_test(&nfsi->data_updates)) {
- /* Mark the attribute cache for revalidation */
- nfsi->flags |= NFS_INO_INVALID_ATTR;
- /* Directories and symlinks: invalidate page cache too */
- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
- nfsi->flags |= NFS_INO_INVALID_DATA;
- nfsi->cache_change_attribute ++;
- }
-}
-
-/**
* nfs_refresh_inode - verify consistency of the inode attribute cache
* @inode - pointer to inode
* @fattr - updated attributes
@@ -1152,8 +1188,11 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0
&& nfsi->change_attr == fattr->pre_change_attr)
nfsi->change_attr = fattr->change_attr;
- if (!data_unstable && nfsi->change_attr != fattr->change_attr)
+ if (nfsi->change_attr != fattr->change_attr) {
nfsi->flags |= NFS_INO_INVALID_ATTR;
+ if (!data_unstable)
+ nfsi->flags |= NFS_INO_REVAL_PAGECACHE;
+ }
}
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
@@ -1176,18 +1215,22 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
}
/* Verify a few of the more important attributes */
- if (!data_unstable) {
- if (!timespec_equal(&inode->i_mtime, &fattr->mtime)
- || cur_size != new_isize)
- nfsi->flags |= NFS_INO_INVALID_ATTR;
- } else if (S_ISREG(inode->i_mode) && new_isize > cur_size)
- nfsi->flags |= NFS_INO_INVALID_ATTR;
+ if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
+ nfsi->flags |= NFS_INO_INVALID_ATTR;
+ if (!data_unstable)
+ nfsi->flags |= NFS_INO_REVAL_PAGECACHE;
+ }
+ if (cur_size != new_isize) {
+ nfsi->flags |= NFS_INO_INVALID_ATTR;
+ if (nfsi->npages == 0)
+ nfsi->flags |= NFS_INO_REVAL_PAGECACHE;
+ }
/* Have any file permissions changed? */
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)
|| inode->i_uid != fattr->uid
|| inode->i_gid != fattr->gid)
- nfsi->flags |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
+ nfsi->flags |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
/* Has the link count changed? */
if (inode->i_nlink != fattr->nlink)
@@ -1215,10 +1258,8 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier)
{
struct nfs_inode *nfsi = NFS_I(inode);
- __u64 new_size;
- loff_t new_isize;
+ loff_t cur_isize, new_isize;
unsigned int invalid = 0;
- loff_t cur_isize;
int data_unstable;
dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
@@ -1251,61 +1292,56 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
/* Are we racing with known updates of the metadata on the server? */
data_unstable = ! nfs_verify_change_attribute(inode, verifier);
- /* Check if the file size agrees */
- new_size = fattr->size;
+ /* Check if our cached file size is stale */
new_isize = nfs_size_to_loff_t(fattr->size);
cur_isize = i_size_read(inode);
- if (cur_isize != new_size) {
-#ifdef NFS_DEBUG_VERBOSE
- printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
-#endif
- /*
- * If we have pending writebacks, things can get
- * messy.
- */
- if (S_ISREG(inode->i_mode) && data_unstable) {
- if (new_isize > cur_isize) {
+ if (new_isize != cur_isize) {
+ /* Do we perhaps have any outstanding writes? */
+ if (nfsi->npages == 0) {
+ /* No, but did we race with nfs_end_data_update()? */
+ if (verifier == nfsi->cache_change_attribute) {
inode->i_size = new_isize;
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+ invalid |= NFS_INO_INVALID_DATA;
}
- } else {
+ invalid |= NFS_INO_INVALID_ATTR;
+ } else if (new_isize > cur_isize) {
inode->i_size = new_isize;
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
}
+ dprintk("NFS: isize change on server for file %s/%ld\n",
+ inode->i_sb->s_id, inode->i_ino);
}
- /*
- * Note: we don't check inode->i_mtime since pipes etc.
- * can change this value in VFS without requiring a
- * cache revalidation.
- */
+ /* Check if the mtime agrees */
if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
-#ifdef NFS_DEBUG_VERBOSE
- printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
-#endif
+ dprintk("NFS: mtime change on server for file %s/%ld\n",
+ inode->i_sb->s_id, inode->i_ino);
if (!data_unstable)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
}
if ((fattr->valid & NFS_ATTR_FATTR_V4)
&& nfsi->change_attr != fattr->change_attr) {
-#ifdef NFS_DEBUG_VERBOSE
- printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n",
+ dprintk("NFS: change_attr change on server for file %s/%ld\n",
inode->i_sb->s_id, inode->i_ino);
-#endif
nfsi->change_attr = fattr->change_attr;
if (!data_unstable)
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
+ invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
}
- memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+ /* If ctime has changed we should definitely clear access+acl caches */
+ if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
+ if (!data_unstable)
+ invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+ memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+ }
memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) ||
inode->i_uid != fattr->uid ||
inode->i_gid != fattr->gid)
- invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
+ invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
@@ -1385,74 +1421,95 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data)
{
int error;
- struct nfs_server *server;
+ struct nfs_server *server = NULL;
struct super_block *s;
struct nfs_fh *root;
struct nfs_mount_data *data = raw_data;
- if (!data) {
- printk("nfs_read_super: missing data argument\n");
- return ERR_PTR(-EINVAL);
+ s = ERR_PTR(-EINVAL);
+ if (data == NULL) {
+ dprintk("%s: missing data argument\n", __FUNCTION__);
+ goto out_err;
+ }
+ if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) {
+ dprintk("%s: bad mount version\n", __FUNCTION__);
+ goto out_err;
}
+ switch (data->version) {
+ case 1:
+ data->namlen = 0;
+ case 2:
+ data->bsize = 0;
+ case 3:
+ if (data->flags & NFS_MOUNT_VER3) {
+ dprintk("%s: mount structure version %d does not support NFSv3\n",
+ __FUNCTION__,
+ data->version);
+ goto out_err;
+ }
+ data->root.size = NFS2_FHSIZE;
+ memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
+ case 4:
+ if (data->flags & NFS_MOUNT_SECFLAVOUR) {
+ dprintk("%s: mount structure version %d does not support strong security\n",
+ __FUNCTION__,
+ data->version);
+ goto out_err;
+ }
+ case 5:
+ memset(data->context, 0, sizeof(data->context));
+ }
+#ifndef CONFIG_NFS_V3
+ /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */
+ s = ERR_PTR(-EPROTONOSUPPORT);
+ if (data->flags & NFS_MOUNT_VER3) {
+ dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__);
+ goto out_err;
+ }
+#endif /* CONFIG_NFS_V3 */
+ s = ERR_PTR(-ENOMEM);
server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL);
if (!server)
- return ERR_PTR(-ENOMEM);
+ goto out_err;
memset(server, 0, sizeof(struct nfs_server));
/* Zero out the NFS state stuff */
init_nfsv4_state(server);
-
- if (data->version != NFS_MOUNT_VERSION) {
- printk("nfs warning: mount version %s than kernel\n",
- data->version < NFS_MOUNT_VERSION ? "older" : "newer");
- if (data->version < 2)
- data->namlen = 0;
- if (data->version < 3)
- data->bsize = 0;
- if (data->version < 4) {
- data->flags &= ~NFS_MOUNT_VER3;
- data->root.size = NFS2_FHSIZE;
- memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
- }
- if (data->version < 5)
- data->flags &= ~NFS_MOUNT_SECFLAVOUR;
- }
+ server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
root = &server->fh;
if (data->flags & NFS_MOUNT_VER3)
root->size = data->root.size;
else
root->size = NFS2_FHSIZE;
+ s = ERR_PTR(-EINVAL);
if (root->size > sizeof(root->data)) {
- printk("nfs_get_sb: invalid root filehandle\n");
- kfree(server);
- return ERR_PTR(-EINVAL);
+ dprintk("%s: invalid root filehandle\n", __FUNCTION__);
+ goto out_err;
}
memcpy(root->data, data->root.data, root->size);
/* We now require that the mount process passes the remote address */
memcpy(&server->addr, &data->addr, sizeof(server->addr));
if (server->addr.sin_addr.s_addr == INADDR_ANY) {
- printk("NFS: mount program didn't pass remote address!\n");
- kfree(server);
- return ERR_PTR(-EINVAL);
+ dprintk("%s: mount program didn't pass remote address!\n",
+ __FUNCTION__);
+ goto out_err;
}
- s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
-
- if (IS_ERR(s) || s->s_root) {
- kfree(server);
- return s;
+ /* Fire up rpciod if not yet running */
+ s = ERR_PTR(rpciod_up());
+ if (IS_ERR(s)) {
+ dprintk("%s: couldn't start rpciod! Error = %ld\n",
+ __FUNCTION__, PTR_ERR(s));
+ goto out_err;
}
- s->s_flags = flags;
+ s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
+ if (IS_ERR(s) || s->s_root)
+ goto out_rpciod_down;
- /* Fire up rpciod if not yet running */
- if (rpciod_up() != 0) {
- printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
- kfree(server);
- return ERR_PTR(-EIO);
- }
+ s->s_flags = flags;
error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) {
@@ -1462,6 +1519,11 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
}
s->s_flags |= MS_ACTIVE;
return s;
+out_rpciod_down:
+ rpciod_down();
+out_err:
+ kfree(server);
+ return s;
}
static void nfs_kill_super(struct super_block *s)
@@ -1470,10 +1532,12 @@ static void nfs_kill_super(struct super_block *s)
kill_anon_super(s);
- if (server->client != NULL && !IS_ERR(server->client))
+ if (!IS_ERR(server->client))
rpc_shutdown_client(server->client);
- if (server->client_sys != NULL && !IS_ERR(server->client_sys))
+ if (!IS_ERR(server->client_sys))
rpc_shutdown_client(server->client_sys);
+ if (!IS_ERR(server->client_acl))
+ rpc_shutdown_client(server->client_acl);
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
@@ -1594,15 +1658,19 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
clp = nfs4_get_client(&server->addr.sin_addr);
if (!clp) {
- printk(KERN_WARNING "NFS: failed to create NFS4 client.\n");
+ dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__);
return -EIO;
}
/* Now create transport and client */
authflavour = RPC_AUTH_UNIX;
if (data->auth_flavourlen != 0) {
- if (data->auth_flavourlen > 1)
- printk(KERN_INFO "NFS: cannot yet deal with multiple auth flavours.\n");
+ if (data->auth_flavourlen != 1) {
+ dprintk("%s: Invalid number of RPC auth flavours %d.\n",
+ __FUNCTION__, data->auth_flavourlen);
+ err = -EINVAL;
+ goto out_fail;
+ }
if (copy_from_user(&authflavour, data->auth_flavours, sizeof(authflavour))) {
err = -EFAULT;
goto out_fail;
@@ -1610,21 +1678,22 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
}
down_write(&clp->cl_sem);
- if (clp->cl_rpcclient == NULL) {
+ if (IS_ERR(clp->cl_rpcclient)) {
xprt = xprt_create_proto(proto, &server->addr, &timeparms);
if (IS_ERR(xprt)) {
up_write(&clp->cl_sem);
- printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
err = PTR_ERR(xprt);
+ dprintk("%s: cannot create RPC transport. Error = %d\n",
+ __FUNCTION__, err);
goto out_fail;
}
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
server->rpc_ops->version, authflavour);
if (IS_ERR(clnt)) {
up_write(&clp->cl_sem);
- printk(KERN_WARNING "NFS: cannot create RPC client.\n");
- xprt_destroy(xprt);
err = PTR_ERR(clnt);
+ dprintk("%s: cannot create RPC client. Error = %d\n",
+ __FUNCTION__, err);
goto out_fail;
}
clnt->cl_intr = 1;
@@ -1656,21 +1725,26 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
clp = NULL;
if (IS_ERR(clnt)) {
- printk(KERN_WARNING "NFS: cannot create RPC client.\n");
- return PTR_ERR(clnt);
+ err = PTR_ERR(clnt);
+ dprintk("%s: cannot create RPC client. Error = %d\n",
+ __FUNCTION__, err);
+ return err;
}
server->client = clnt;
if (server->nfs4_state->cl_idmap == NULL) {
- printk(KERN_WARNING "NFS: failed to create idmapper.\n");
+ dprintk("%s: failed to create idmapper.\n", __FUNCTION__);
return -ENOMEM;
}
if (clnt->cl_auth->au_flavor != authflavour) {
- if (rpcauth_create(authflavour, clnt) == NULL) {
- printk(KERN_WARNING "NFS: couldn't create credcache!\n");
- return -ENOMEM;
+ struct rpc_auth *auth;
+
+ auth = rpcauth_create(authflavour, clnt);
+ if (IS_ERR(auth)) {
+ dprintk("%s: couldn't create credcache!\n", __FUNCTION__);
+ return PTR_ERR(auth);
}
}
@@ -1730,8 +1804,12 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
struct nfs4_mount_data *data = raw_data;
void *p;
- if (!data) {
- printk("nfs_read_super: missing data argument\n");
+ if (data == NULL) {
+ dprintk("%s: missing data argument\n", __FUNCTION__);
+ return ERR_PTR(-EINVAL);
+ }
+ if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) {
+ dprintk("%s: bad mount version\n", __FUNCTION__);
return ERR_PTR(-EINVAL);
}
@@ -1741,11 +1819,7 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
memset(server, 0, sizeof(struct nfs_server));
/* Zero out the NFS state stuff */
init_nfsv4_state(server);
-
- if (data->version != NFS4_MOUNT_VERSION) {
- printk("nfs warning: mount version %s than kernel\n",
- data->version < NFS4_MOUNT_VERSION ? "older" : "newer");
- }
+ server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
p = nfs_copy_user_string(NULL, &data->hostname, 256);
if (IS_ERR(p))
@@ -1773,11 +1847,20 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
}
if (server->addr.sin_family != AF_INET ||
server->addr.sin_addr.s_addr == INADDR_ANY) {
- printk("NFS: mount program didn't pass remote IP address!\n");
+ dprintk("%s: mount program didn't pass remote IP address!\n",
+ __FUNCTION__);
s = ERR_PTR(-EINVAL);
goto out_free;
}
+ /* Fire up rpciod if not yet running */
+ s = ERR_PTR(rpciod_up());
+ if (IS_ERR(s)) {
+ dprintk("%s: couldn't start rpciod! Error = %ld\n",
+ __FUNCTION__, PTR_ERR(s));
+ goto out_free;
+ }
+
s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
if (IS_ERR(s) || s->s_root)
@@ -1785,13 +1868,6 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
s->s_flags = flags;
- /* Fire up rpciod if not yet running */
- if (rpciod_up() != 0) {
- printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
- s = ERR_PTR(-EIO);
- goto out_free;
- }
-
error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) {
up_write(&s->s_umount);
@@ -1875,6 +1951,13 @@ static struct inode *nfs_alloc_inode(struct super_block *sb)
if (!nfsi)
return NULL;
nfsi->flags = 0;
+#ifdef CONFIG_NFS_V3_ACL
+ nfsi->acl_access = ERR_PTR(-EAGAIN);
+ nfsi->acl_default = ERR_PTR(-EAGAIN);
+#endif
+#ifdef CONFIG_NFS_V4
+ nfsi->nfs4_acl = NULL;
+#endif /* CONFIG_NFS_V4 */
return &nfsi->vfs_inode;
}
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 9d3ddad96d9..0e82617f2de 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -80,9 +80,7 @@ mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version,
clnt = rpc_create_client(xprt, hostname,
&mnt_program, version,
RPC_AUTH_UNIX);
- if (IS_ERR(clnt)) {
- xprt_destroy(xprt);
- } else {
+ if (!IS_ERR(clnt)) {
clnt->cl_softrtry = 1;
clnt->cl_chatty = 1;
clnt->cl_oneshot = 1;
diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c
new file mode 100644
index 00000000000..ee3536fc84a
--- /dev/null
+++ b/fs/nfs/nfs3acl.c
@@ -0,0 +1,403 @@
+#include <linux/fs.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+#include <linux/xattr_acl.h>
+#include <linux/nfsacl.h>
+
+#define NFSDBG_FACILITY NFSDBG_PROC
+
+ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+ struct inode *inode = dentry->d_inode;
+ struct posix_acl *acl;
+ int pos=0, len=0;
+
+# define output(s) do { \
+ if (pos + sizeof(s) <= size) { \
+ memcpy(buffer + pos, s, sizeof(s)); \
+ pos += sizeof(s); \
+ } \
+ len += sizeof(s); \
+ } while(0)
+
+ acl = nfs3_proc_getacl(inode, ACL_TYPE_ACCESS);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl) {
+ output("system.posix_acl_access");
+ posix_acl_release(acl);
+ }
+
+ if (S_ISDIR(inode->i_mode)) {
+ acl = nfs3_proc_getacl(inode, ACL_TYPE_DEFAULT);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl) {
+ output("system.posix_acl_default");
+ posix_acl_release(acl);
+ }
+ }
+
+# undef output
+
+ if (!buffer || len <= size)
+ return len;
+ return -ERANGE;
+}
+
+ssize_t nfs3_getxattr(struct dentry *dentry, const char *name,
+ void *buffer, size_t size)
+{
+ struct inode *inode = dentry->d_inode;
+ struct posix_acl *acl;
+ int type, error = 0;
+
+ if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
+ type = ACL_TYPE_ACCESS;
+ else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
+ type = ACL_TYPE_DEFAULT;
+ else
+ return -EOPNOTSUPP;
+
+ acl = nfs3_proc_getacl(inode, type);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ else if (acl) {
+ if (type == ACL_TYPE_ACCESS && acl->a_count == 0)
+ error = -ENODATA;
+ else
+ error = posix_acl_to_xattr(acl, buffer, size);
+ posix_acl_release(acl);
+ } else
+ error = -ENODATA;
+
+ return error;
+}
+
+int nfs3_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ struct posix_acl *acl;
+ int type, error;
+
+ if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
+ type = ACL_TYPE_ACCESS;
+ else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
+ type = ACL_TYPE_DEFAULT;
+ else
+ return -EOPNOTSUPP;
+
+ acl = posix_acl_from_xattr(value, size);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ error = nfs3_proc_setacl(inode, type, acl);
+ posix_acl_release(acl);
+
+ return error;
+}
+
+int nfs3_removexattr(struct dentry *dentry, const char *name)
+{
+ struct inode *inode = dentry->d_inode;
+ int type;
+
+ if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
+ type = ACL_TYPE_ACCESS;
+ else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
+ type = ACL_TYPE_DEFAULT;
+ else
+ return -EOPNOTSUPP;
+
+ return nfs3_proc_setacl(inode, type, NULL);
+}
+
+static void __nfs3_forget_cached_acls(struct nfs_inode *nfsi)
+{
+ if (!IS_ERR(nfsi->acl_access)) {
+ posix_acl_release(nfsi->acl_access);
+ nfsi->acl_access = ERR_PTR(-EAGAIN);
+ }
+ if (!IS_ERR(nfsi->acl_default)) {
+ posix_acl_release(nfsi->acl_default);
+ nfsi->acl_default = ERR_PTR(-EAGAIN);
+ }
+}
+
+void nfs3_forget_cached_acls(struct inode *inode)
+{
+ dprintk("NFS: nfs3_forget_cached_acls(%s/%ld)\n", inode->i_sb->s_id,
+ inode->i_ino);
+ spin_lock(&inode->i_lock);
+ __nfs3_forget_cached_acls(NFS_I(inode));
+ spin_unlock(&inode->i_lock);
+}
+
+static struct posix_acl *nfs3_get_cached_acl(struct inode *inode, int type)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+ struct posix_acl *acl = ERR_PTR(-EINVAL);
+
+ spin_lock(&inode->i_lock);
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ acl = nfsi->acl_access;
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ acl = nfsi->acl_default;
+ break;
+
+ default:
+ goto out;
+ }
+ if (IS_ERR(acl))
+ acl = ERR_PTR(-EAGAIN);
+ else
+ acl = posix_acl_dup(acl);
+out:
+ spin_unlock(&inode->i_lock);
+ dprintk("NFS: nfs3_get_cached_acl(%s/%ld, %d) = %p\n", inode->i_sb->s_id,
+ inode->i_ino, type, acl);
+ return acl;
+}
+
+static void nfs3_cache_acls(struct inode *inode, struct posix_acl *acl,
+ struct posix_acl *dfacl)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ dprintk("nfs3_cache_acls(%s/%ld, %p, %p)\n", inode->i_sb->s_id,
+ inode->i_ino, acl, dfacl);
+ spin_lock(&inode->i_lock);
+ __nfs3_forget_cached_acls(NFS_I(inode));
+ nfsi->acl_access = posix_acl_dup(acl);
+ nfsi->acl_default = posix_acl_dup(dfacl);
+ spin_unlock(&inode->i_lock);
+}
+
+struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
+{
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_fattr fattr;
+ struct page *pages[NFSACL_MAXPAGES] = { };
+ struct nfs3_getaclargs args = {
+ .fh = NFS_FH(inode),
+ /* The xdr layer may allocate pages here. */
+ .pages = pages,
+ };
+ struct nfs3_getaclres res = {
+ .fattr = &fattr,
+ };
+ struct posix_acl *acl;
+ int status, count;
+
+ if (!nfs_server_capable(inode, NFS_CAP_ACLS))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ status = nfs_revalidate_inode(server, inode);
+ if (status < 0)
+ return ERR_PTR(status);
+ acl = nfs3_get_cached_acl(inode, type);
+ if (acl != ERR_PTR(-EAGAIN))
+ return acl;
+ acl = NULL;
+
+ /*
+ * Only get the access acl when explicitly requested: We don't
+ * need it for access decisions, and only some applications use
+ * it. Applications which request the access acl first are not
+ * penalized from this optimization.
+ */
+ if (type == ACL_TYPE_ACCESS)
+ args.mask |= NFS_ACLCNT|NFS_ACL;
+ if (S_ISDIR(inode->i_mode))
+ args.mask |= NFS_DFACLCNT|NFS_DFACL;
+ if (args.mask == 0)
+ return NULL;
+
+ dprintk("NFS call getacl\n");
+ status = rpc_call(server->client_acl, ACLPROC3_GETACL,
+ &args, &res, 0);
+ dprintk("NFS reply getacl: %d\n", status);
+
+ /* pages may have been allocated at the xdr layer. */
+ for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++)
+ __free_page(args.pages[count]);
+
+ switch (status) {
+ case 0:
+ status = nfs_refresh_inode(inode, &fattr);
+ break;
+ case -EPFNOSUPPORT:
+ case -EPROTONOSUPPORT:
+ dprintk("NFS_V3_ACL extension not supported; disabling\n");
+ server->caps &= ~NFS_CAP_ACLS;
+ case -ENOTSUPP:
+ status = -EOPNOTSUPP;
+ default:
+ goto getout;
+ }
+ if ((args.mask & res.mask) != args.mask) {
+ status = -EIO;
+ goto getout;
+ }
+
+ if (res.acl_access != NULL) {
+ if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) {
+ posix_acl_release(res.acl_access);
+ res.acl_access = NULL;
+ }
+ }
+ nfs3_cache_acls(inode, res.acl_access, res.acl_default);
+
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ acl = res.acl_access;
+ res.acl_access = NULL;
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ acl = res.acl_default;
+ res.acl_default = NULL;
+ }
+
+getout:
+ posix_acl_release(res.acl_access);
+ posix_acl_release(res.acl_default);
+
+ if (status != 0) {
+ posix_acl_release(acl);
+ acl = ERR_PTR(status);
+ }
+ return acl;
+}
+
+static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
+ struct posix_acl *dfacl)
+{
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_fattr fattr;
+ struct page *pages[NFSACL_MAXPAGES] = { };
+ struct nfs3_setaclargs args = {
+ .inode = inode,
+ .mask = NFS_ACL,
+ .acl_access = acl,
+ .pages = pages,
+ };
+ int status, count;
+
+ status = -EOPNOTSUPP;
+ if (!nfs_server_capable(inode, NFS_CAP_ACLS))
+ goto out;
+
+ /* We are doing this here, because XDR marshalling can only
+ return -ENOMEM. */
+ status = -ENOSPC;
+ if (acl != NULL && acl->a_count > NFS_ACL_MAX_ENTRIES)
+ goto out;
+ if (dfacl != NULL && dfacl->a_count > NFS_ACL_MAX_ENTRIES)
+ goto out;
+ if (S_ISDIR(inode->i_mode)) {
+ args.mask |= NFS_DFACL;
+ args.acl_default = dfacl;
+ }
+
+ dprintk("NFS call setacl\n");
+ nfs_begin_data_update(inode);
+ status = rpc_call(server->client_acl, ACLPROC3_SETACL,
+ &args, &fattr, 0);
+ NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS;
+ nfs_end_data_update(inode);
+ dprintk("NFS reply setacl: %d\n", status);
+
+ /* pages may have been allocated at the xdr layer. */
+ for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++)
+ __free_page(args.pages[count]);
+
+ switch (status) {
+ case 0:
+ status = nfs_refresh_inode(inode, &fattr);
+ break;
+ case -EPFNOSUPPORT:
+ case -EPROTONOSUPPORT:
+ dprintk("NFS_V3_ACL SETACL RPC not supported"
+ "(will not retry)\n");
+ server->caps &= ~NFS_CAP_ACLS;
+ case -ENOTSUPP:
+ status = -EOPNOTSUPP;
+ }
+out:
+ return status;
+}
+
+int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl)
+{
+ struct posix_acl *alloc = NULL, *dfacl = NULL;
+ int status;
+
+ if (S_ISDIR(inode->i_mode)) {
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ alloc = dfacl = nfs3_proc_getacl(inode,
+ ACL_TYPE_DEFAULT);
+ if (IS_ERR(alloc))
+ goto fail;
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ dfacl = acl;
+ alloc = acl = nfs3_proc_getacl(inode,
+ ACL_TYPE_ACCESS);
+ if (IS_ERR(alloc))
+ goto fail;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else if (type != ACL_TYPE_ACCESS)
+ return -EINVAL;
+
+ if (acl == NULL) {
+ alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+ if (IS_ERR(alloc))
+ goto fail;
+ }
+ status = nfs3_proc_setacls(inode, acl, dfacl);
+ posix_acl_release(alloc);
+ return status;
+
+fail:
+ return PTR_ERR(alloc);
+}
+
+int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
+ mode_t mode)
+{
+ struct posix_acl *dfacl, *acl;
+ int error = 0;
+
+ dfacl = nfs3_proc_getacl(dir, ACL_TYPE_DEFAULT);
+ if (IS_ERR(dfacl)) {
+ error = PTR_ERR(dfacl);
+ return (error == -EOPNOTSUPP) ? 0 : error;
+ }
+ if (!dfacl)
+ return 0;
+ acl = posix_acl_clone(dfacl, GFP_KERNEL);
+ error = -ENOMEM;
+ if (!acl)
+ goto out_release_dfacl;
+ error = posix_acl_create_masq(acl, &mode);
+ if (error < 0)
+ goto out_release_acl;
+ error = nfs3_proc_setacls(inode, acl, S_ISDIR(inode->i_mode) ?
+ dfacl : NULL);
+out_release_acl:
+ posix_acl_release(acl);
+out_release_dfacl:
+ posix_acl_release(dfacl);
+ return error;
+}
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 3878494dfc2..7851569b31c 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -17,6 +17,7 @@
#include <linux/nfs_page.h>
#include <linux/lockd/bind.h>
#include <linux/smp_lock.h>
+#include <linux/nfs_mount.h>
#define NFSDBG_FACILITY NFSDBG_PROC
@@ -45,7 +46,7 @@ static inline int
nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
{
struct rpc_message msg = {
- .rpc_proc = &nfs3_procedures[proc],
+ .rpc_proc = &clnt->cl_procinfo[proc],
.rpc_argp = argp,
.rpc_resp = resp,
};
@@ -313,7 +314,8 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
.fh = &fhandle,
.fattr = &fattr
};
- int status;
+ mode_t mode = sattr->ia_mode;
+ int status;
dprintk("NFS call create %s\n", dentry->d_name.name);
arg.createmode = NFS3_CREATE_UNCHECKED;
@@ -323,6 +325,8 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
arg.verifier[1] = current->pid;
}
+ sattr->ia_mode &= ~current->fs->umask;
+
again:
dir_attr.valid = 0;
fattr.valid = 0;
@@ -369,6 +373,9 @@ again:
nfs_refresh_inode(dentry->d_inode, &fattr);
dprintk("NFS reply setattr (post-create): %d\n", status);
}
+ if (status != 0)
+ goto out;
+ status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
out:
dprintk("NFS reply create: %d\n", status);
return status;
@@ -538,15 +545,24 @@ nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
.fh = &fhandle,
.fattr = &fattr
};
- int status;
+ int mode = sattr->ia_mode;
+ int status;
dprintk("NFS call mkdir %s\n", dentry->d_name.name);
dir_attr.valid = 0;
fattr.valid = 0;
+
+ sattr->ia_mode &= ~current->fs->umask;
+
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKDIR, &arg, &res, 0);
nfs_refresh_inode(dir, &dir_attr);
- if (status == 0)
- status = nfs_instantiate(dentry, &fhandle, &fattr);
+ if (status != 0)
+ goto out;
+ status = nfs_instantiate(dentry, &fhandle, &fattr);
+ if (status != 0)
+ goto out;
+ status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
+out:
dprintk("NFS reply mkdir: %d\n", status);
return status;
}
@@ -641,6 +657,7 @@ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
.fh = &fh,
.fattr = &fattr
};
+ mode_t mode = sattr->ia_mode;
int status;
switch (sattr->ia_mode & S_IFMT) {
@@ -653,12 +670,20 @@ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name,
MAJOR(rdev), MINOR(rdev));
+
+ sattr->ia_mode &= ~current->fs->umask;
+
dir_attr.valid = 0;
fattr.valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, 0);
nfs_refresh_inode(dir, &dir_attr);
- if (status == 0)
- status = nfs_instantiate(dentry, &fh, &fattr);
+ if (status != 0)
+ goto out;
+ status = nfs_instantiate(dentry, &fh, &fattr);
+ if (status != 0)
+ goto out;
+ status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
+out:
dprintk("NFS reply mknod: %d\n", status);
return status;
}
@@ -825,7 +850,8 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
struct nfs_rpc_ops nfs_v3_clientops = {
.version = 3, /* protocol version */
.dentry_ops = &nfs_dentry_operations,
- .dir_inode_ops = &nfs_dir_inode_operations,
+ .dir_inode_ops = &nfs3_dir_inode_operations,
+ .file_inode_ops = &nfs3_file_inode_operations,
.getroot = nfs3_proc_get_root,
.getattr = nfs3_proc_getattr,
.setattr = nfs3_proc_setattr,
@@ -856,4 +882,5 @@ struct nfs_rpc_ops nfs_v3_clientops = {
.file_open = nfs_open,
.file_release = nfs_release,
.lock = nfs3_proc_lock,
+ .clear_acl_cache = nfs3_forget_cached_acls,
};
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index a3593d47e5a..db4a904810a 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -21,6 +21,7 @@
#include <linux/nfs.h>
#include <linux/nfs3.h>
#include <linux/nfs_fs.h>
+#include <linux/nfsacl.h>
#define NFSDBG_FACILITY NFSDBG_XDR
@@ -79,6 +80,11 @@ extern int nfs_stat_to_errno(int);
#define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6)
#define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2)
+#define ACL3_getaclargs_sz (NFS3_fh_sz+1)
+#define ACL3_setaclargs_sz (NFS3_fh_sz+1+2*(2+5*3))
+#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+2*(2+5*3))
+#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
+
/*
* Map file type to S_IFMT bits
*/
@@ -627,6 +633,74 @@ nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
return 0;
}
+#ifdef CONFIG_NFS_V3_ACL
+/*
+ * Encode GETACL arguments
+ */
+static int
+nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p,
+ struct nfs3_getaclargs *args)
+{
+ struct rpc_auth *auth = req->rq_task->tk_auth;
+ unsigned int replen;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(args->mask);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+ if (args->mask & (NFS_ACL | NFS_DFACL)) {
+ /* Inline the page array */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack +
+ ACL3_getaclres_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
+ NFSACL_MAXPAGES << PAGE_SHIFT);
+ }
+ return 0;
+}
+
+/*
+ * Encode SETACL arguments
+ */
+static int
+nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p,
+ struct nfs3_setaclargs *args)
+{
+ struct xdr_buf *buf = &req->rq_snd_buf;
+ unsigned int base, len_in_head, len = nfsacl_size(
+ (args->mask & NFS_ACL) ? args->acl_access : NULL,
+ (args->mask & NFS_DFACL) ? args->acl_default : NULL);
+ int count, err;
+
+ p = xdr_encode_fhandle(p, NFS_FH(args->inode));
+ *p++ = htonl(args->mask);
+ base = (char *)p - (char *)buf->head->iov_base;
+ /* put as much of the acls into head as possible. */
+ len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
+ len -= len_in_head;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
+
+ for (count = 0; (count << PAGE_SHIFT) < len; count++) {
+ args->pages[count] = alloc_page(GFP_KERNEL);
+ if (!args->pages[count]) {
+ while (count)
+ __free_page(args->pages[--count]);
+ return -ENOMEM;
+ }
+ }
+ xdr_encode_pages(buf, args->pages, 0, len);
+
+ err = nfsacl_encode(buf, base, args->inode,
+ (args->mask & NFS_ACL) ?
+ args->acl_access : NULL, 1, 0);
+ if (err > 0)
+ err = nfsacl_encode(buf, base + err, args->inode,
+ (args->mask & NFS_DFACL) ?
+ args->acl_default : NULL, 1,
+ NFS_ACL_DEFAULT);
+ return (err > 0) ? 0 : err;
+}
+#endif /* CONFIG_NFS_V3_ACL */
+
/*
* NFS XDR decode functions
*/
@@ -978,6 +1052,54 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
return 0;
}
+#ifdef CONFIG_NFS_V3_ACL
+/*
+ * Decode GETACL reply
+ */
+static int
+nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p,
+ struct nfs3_getaclres *res)
+{
+ struct xdr_buf *buf = &req->rq_rcv_buf;
+ int status = ntohl(*p++);
+ struct posix_acl **acl;
+ unsigned int *aclcnt;
+ int err, base;
+
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ res->mask = ntohl(*p++);
+ if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+ return -EINVAL;
+ base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
+
+ acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
+ aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
+ err = nfsacl_decode(buf, base, aclcnt, acl);
+
+ acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
+ aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
+ if (err > 0)
+ err = nfsacl_decode(buf, base + err, aclcnt, acl);
+ return (err > 0) ? 0 : err;
+}
+
+/*
+ * Decode setacl reply.
+ */
+static int
+nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+ int status = ntohl(*p++);
+
+ if (status)
+ return -nfs_stat_to_errno(status);
+ xdr_decode_post_op_attr(p, fattr);
+ return 0;
+}
+#endif /* CONFIG_NFS_V3_ACL */
+
#ifndef MAX
# define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
@@ -1021,3 +1143,28 @@ struct rpc_version nfs_version3 = {
.procs = nfs3_procedures
};
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_procinfo nfs3_acl_procedures[] = {
+ [ACLPROC3_GETACL] = {
+ .p_proc = ACLPROC3_GETACL,
+ .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
+ .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
+ .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2,
+ .p_timer = 1,
+ },
+ [ACLPROC3_SETACL] = {
+ .p_proc = ACLPROC3_SETACL,
+ .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
+ .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
+ .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2,
+ .p_timer = 0,
+ },
+};
+
+struct rpc_version nfsacl_version3 = {
+ .number = 3,
+ .nrprocs = sizeof(nfs3_acl_procedures)/
+ sizeof(nfs3_acl_procedures[0]),
+ .procs = nfs3_acl_procedures,
+};
+#endif /* CONFIG_NFS_V3_ACL */
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
new file mode 100644
index 00000000000..ec1a22d7b87
--- /dev/null
+++ b/fs/nfs/nfs4_fs.h
@@ -0,0 +1,253 @@
+/*
+ * linux/fs/nfs/nfs4_fs.h
+ *
+ * Copyright (C) 2005 Trond Myklebust
+ *
+ * NFSv4-specific filesystem definitions and declarations
+ */
+
+#ifndef __LINUX_FS_NFS_NFS4_FS_H
+#define __LINUX_FS_NFS_NFS4_FS_H
+
+#ifdef CONFIG_NFS_V4
+
+struct idmap;
+
+/*
+ * In a seqid-mutating op, this macro controls which error return
+ * values trigger incrementation of the seqid.
+ *
+ * from rfc 3010:
+ * The client MUST monotonically increment the sequence number for the
+ * CLOSE, LOCK, LOCKU, OPEN, OPEN_CONFIRM, and OPEN_DOWNGRADE
+ * operations. This is true even in the event that the previous
+ * operation that used the sequence number received an error. The only
+ * exception to this rule is if the previous operation received one of
+ * the following errors: NFSERR_STALE_CLIENTID, NFSERR_STALE_STATEID,
+ * NFSERR_BAD_STATEID, NFSERR_BAD_SEQID, NFSERR_BADXDR,
+ * NFSERR_RESOURCE, NFSERR_NOFILEHANDLE.
+ *
+ */
+#define seqid_mutating_err(err) \
+(((err) != NFSERR_STALE_CLIENTID) && \
+ ((err) != NFSERR_STALE_STATEID) && \
+ ((err) != NFSERR_BAD_STATEID) && \
+ ((err) != NFSERR_BAD_SEQID) && \
+ ((err) != NFSERR_BAD_XDR) && \
+ ((err) != NFSERR_RESOURCE) && \
+ ((err) != NFSERR_NOFILEHANDLE))
+
+enum nfs4_client_state {
+ NFS4CLNT_OK = 0,
+};
+
+/*
+ * The nfs4_client identifies our client state to the server.
+ */
+struct nfs4_client {
+ struct list_head cl_servers; /* Global list of servers */
+ struct in_addr cl_addr; /* Server identifier */
+ u64 cl_clientid; /* constant */
+ nfs4_verifier cl_confirm;
+ unsigned long cl_state;
+
+ u32 cl_lockowner_id;
+
+ /*
+ * The following rwsem ensures exclusive access to the server
+ * while we recover the state following a lease expiration.
+ */
+ struct rw_semaphore cl_sem;
+
+ struct list_head cl_delegations;
+ struct list_head cl_state_owners;
+ struct list_head cl_unused;
+ int cl_nunused;
+ spinlock_t cl_lock;
+ atomic_t cl_count;
+
+ struct rpc_clnt * cl_rpcclient;
+ struct rpc_cred * cl_cred;
+
+ struct list_head cl_superblocks; /* List of nfs_server structs */
+
+ unsigned long cl_lease_time;
+ unsigned long cl_last_renewal;
+ struct work_struct cl_renewd;
+ struct work_struct cl_recoverd;
+
+ wait_queue_head_t cl_waitq;
+ struct rpc_wait_queue cl_rpcwaitq;
+
+ /* used for the setclientid verifier */
+ struct timespec cl_boot_time;
+
+ /* idmapper */
+ struct idmap * cl_idmap;
+
+ /* Our own IP address, as a null-terminated string.
+ * This is used to generate the clientid, and the callback address.
+ */
+ char cl_ipaddr[16];
+ unsigned char cl_id_uniquifier;
+};
+
+/*
+ * NFS4 state_owners and lock_owners are simply labels for ordered
+ * sequences of RPC calls. Their sole purpose is to provide once-only
+ * semantics by allowing the server to identify replayed requests.
+ *
+ * The ->so_sema is held during all state_owner seqid-mutating operations:
+ * OPEN, OPEN_DOWNGRADE, and CLOSE. Its purpose is to properly serialize
+ * so_seqid.
+ */
+struct nfs4_state_owner {
+ struct list_head so_list; /* per-clientid list of state_owners */
+ struct nfs4_client *so_client;
+ u32 so_id; /* 32-bit identifier, unique */
+ struct semaphore so_sema;
+ u32 so_seqid; /* protected by so_sema */
+ atomic_t so_count;
+
+ struct rpc_cred *so_cred; /* Associated cred */
+ struct list_head so_states;
+ struct list_head so_delegations;
+};
+
+/*
+ * struct nfs4_state maintains the client-side state for a given
+ * (state_owner,inode) tuple (OPEN) or state_owner (LOCK).
+ *
+ * OPEN:
+ * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server,
+ * we need to know how many files are open for reading or writing on a
+ * given inode. This information too is stored here.
+ *
+ * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN)
+ */
+
+struct nfs4_lock_state {
+ struct list_head ls_locks; /* Other lock stateids */
+ struct nfs4_state * ls_state; /* Pointer to open state */
+ fl_owner_t ls_owner; /* POSIX lock owner */
+#define NFS_LOCK_INITIALIZED 1
+ int ls_flags;
+ u32 ls_seqid;
+ u32 ls_id;
+ nfs4_stateid ls_stateid;
+ atomic_t ls_count;
+};
+
+/* bits for nfs4_state->flags */
+enum {
+ LK_STATE_IN_USE,
+ NFS_DELEGATED_STATE,
+};
+
+struct nfs4_state {
+ struct list_head open_states; /* List of states for the same state_owner */
+ struct list_head inode_states; /* List of states for the same inode */
+ struct list_head lock_states; /* List of subservient lock stateids */
+
+ struct nfs4_state_owner *owner; /* Pointer to the open owner */
+ struct inode *inode; /* Pointer to the inode */
+
+ unsigned long flags; /* Do we hold any locks? */
+ struct semaphore lock_sema; /* Serializes file locking operations */
+ spinlock_t state_lock; /* Protects the lock_states list */
+
+ nfs4_stateid stateid;
+
+ unsigned int nreaders;
+ unsigned int nwriters;
+ int state; /* State on the server (R,W, or RW) */
+ atomic_t count;
+};
+
+
+struct nfs4_exception {
+ long timeout;
+ int retry;
+};
+
+struct nfs4_state_recovery_ops {
+ int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
+ int (*recover_lock)(struct nfs4_state *, struct file_lock *);
+};
+
+extern struct dentry_operations nfs4_dentry_operations;
+extern struct inode_operations nfs4_dir_inode_operations;
+
+/* inode.c */
+extern ssize_t nfs4_getxattr(struct dentry *, const char *, void *, size_t);
+extern int nfs4_setxattr(struct dentry *, const char *, const void *, size_t, int);
+extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t);
+
+
+/* nfs4proc.c */
+extern int nfs4_map_errors(int err);
+extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short);
+extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
+extern int nfs4_proc_async_renew(struct nfs4_client *);
+extern int nfs4_proc_renew(struct nfs4_client *);
+extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode);
+extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
+extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
+
+extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
+extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops;
+
+extern const u32 nfs4_fattr_bitmap[2];
+extern const u32 nfs4_statfs_bitmap[2];
+extern const u32 nfs4_pathconf_bitmap[2];
+extern const u32 nfs4_fsinfo_bitmap[2];
+
+/* nfs4renewd.c */
+extern void nfs4_schedule_state_renewal(struct nfs4_client *);
+extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
+extern void nfs4_kill_renewd(struct nfs4_client *);
+extern void nfs4_renew_state(void *);
+
+/* nfs4state.c */
+extern void init_nfsv4_state(struct nfs_server *);
+extern void destroy_nfsv4_state(struct nfs_server *);
+extern struct nfs4_client *nfs4_get_client(struct in_addr *);
+extern void nfs4_put_client(struct nfs4_client *clp);
+extern int nfs4_init_client(struct nfs4_client *clp);
+extern struct nfs4_client *nfs4_find_client(struct in_addr *);
+extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *);
+
+extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
+extern void nfs4_put_state_owner(struct nfs4_state_owner *);
+extern void nfs4_drop_state_owner(struct nfs4_state_owner *);
+extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
+extern void nfs4_put_open_state(struct nfs4_state *);
+extern void nfs4_close_state(struct nfs4_state *, mode_t);
+extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode);
+extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp);
+extern void nfs4_schedule_state_recovery(struct nfs4_client *);
+extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
+extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls);
+extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
+
+extern const nfs4_stateid zero_stateid;
+
+/* nfs4xdr.c */
+extern uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus);
+extern struct rpc_procinfo nfs4_procedures[];
+
+struct nfs4_mount_data;
+
+/* callback_xdr.c */
+extern struct svc_version nfs4_callback_version1;
+
+#else
+
+#define init_nfsv4_state(server) do { } while (0)
+#define destroy_nfsv4_state(server) do { } while (0)
+#define nfs4_put_state_owner(inode, owner) do { } while (0)
+#define nfs4_put_open_state(state) do { } while (0)
+#define nfs4_close_state(a, b) do { } while (0)
+
+#endif /* CONFIG_NFS_V4 */
+#endif /* __LINUX_FS_NFS_NFS4_FS.H */
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 1d5cb3e80c3..1b76f80aedb 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -48,6 +48,7 @@
#include <linux/smp_lock.h>
#include <linux/namei.h>
+#include "nfs4_fs.h"
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_PROC
@@ -62,8 +63,6 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[];
-extern nfs4_stateid zero_stateid;
-
/* Prevent leaks of NFSv4 errors into userland */
int nfs4_map_errors(int err)
{
@@ -104,7 +103,7 @@ const u32 nfs4_statfs_bitmap[2] = {
| FATTR4_WORD1_SPACE_TOTAL
};
-u32 nfs4_pathconf_bitmap[2] = {
+const u32 nfs4_pathconf_bitmap[2] = {
FATTR4_WORD0_MAXLINK
| FATTR4_WORD0_MAXNAME,
0
@@ -124,7 +123,7 @@ static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
BUG_ON(readdir->count < 80);
if (cookie > 2) {
- readdir->cookie = (cookie > 2) ? cookie : 0;
+ readdir->cookie = cookie;
memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier));
return;
}
@@ -270,14 +269,9 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta
int err;
do {
err = _nfs4_open_reclaim(sp, state);
- switch (err) {
- case 0:
- case -NFS4ERR_STALE_CLIENTID:
- case -NFS4ERR_STALE_STATEID:
- case -NFS4ERR_EXPIRED:
- return err;
- }
- err = nfs4_handle_exception(server, err, &exception);
+ if (err != -NFS4ERR_DELAY)
+ break;
+ nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
@@ -509,6 +503,20 @@ out_stale:
goto out_nodeleg;
}
+static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry)
+{
+ struct nfs_server *server = NFS_SERVER(dentry->d_inode);
+ struct nfs4_exception exception = { };
+ int err;
+
+ do {
+ err = _nfs4_open_expired(sp, state, dentry);
+ if (err == -NFS4ERR_DELAY)
+ nfs4_handle_exception(server, err, &exception);
+ } while (exception.retry);
+ return err;
+}
+
static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
struct nfs_inode *nfsi = NFS_I(state->inode);
@@ -521,7 +529,7 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
continue;
get_nfs_open_context(ctx);
spin_unlock(&state->inode->i_lock);
- status = _nfs4_open_expired(sp, state, ctx->dentry);
+ status = nfs4_do_open_expired(sp, state, ctx->dentry);
put_nfs_open_context(ctx);
return status;
}
@@ -748,11 +756,10 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
fattr->valid = 0;
- if (state != NULL)
+ if (state != NULL) {
msg.rpc_cred = state->owner->so_cred;
- if (sattr->ia_valid & ATTR_SIZE)
- nfs4_copy_stateid(&arg.stateid, state, NULL);
- else
+ nfs4_copy_stateid(&arg.stateid, state, current->files);
+ } else
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
return rpc_call_sync(server->client, &msg, 0);
@@ -1116,47 +1123,31 @@ static int
nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
struct iattr *sattr)
{
- struct inode * inode = dentry->d_inode;
- int size_change = sattr->ia_valid & ATTR_SIZE;
- struct nfs4_state *state = NULL;
- int need_iput = 0;
+ struct rpc_cred *cred;
+ struct inode *inode = dentry->d_inode;
+ struct nfs4_state *state;
int status;
fattr->valid = 0;
- if (size_change) {
- struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
- if (IS_ERR(cred))
- return PTR_ERR(cred);
+ cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
+ if (IS_ERR(cred))
+ return PTR_ERR(cred);
+ /* Search for an existing WRITE delegation first */
+ state = nfs4_open_delegated(inode, FMODE_WRITE, cred);
+ if (!IS_ERR(state)) {
+ /* NB: nfs4_open_delegated() bumps the inode->i_count */
+ iput(inode);
+ } else {
+ /* Search for an existing open(O_WRITE) stateid */
state = nfs4_find_state(inode, cred, FMODE_WRITE);
- if (state == NULL) {
- state = nfs4_open_delegated(dentry->d_inode,
- FMODE_WRITE, cred);
- if (IS_ERR(state))
- state = nfs4_do_open(dentry->d_parent->d_inode,
- dentry, FMODE_WRITE,
- NULL, cred);
- need_iput = 1;
- }
- put_rpccred(cred);
- if (IS_ERR(state))
- return PTR_ERR(state);
-
- if (state->inode != inode) {
- printk(KERN_WARNING "nfs: raced in setattr (%p != %p), returning -EIO\n", inode, state->inode);
- status = -EIO;
- goto out;
- }
}
+
status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
NFS_FH(inode), sattr, state);
-out:
- if (state) {
- inode = state->inode;
+ if (state != NULL)
nfs4_close_state(state, FMODE_WRITE);
- if (need_iput)
- iput(inode);
- }
+ put_rpccred(cred);
return status;
}
@@ -1731,6 +1722,10 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
};
int status;
+ dprintk("%s: dentry = %s/%s, cookie = %Lu\n", __FUNCTION__,
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name,
+ (unsigned long long)cookie);
lock_kernel();
nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
res.pgbase = args.pgbase;
@@ -1738,6 +1733,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
if (status == 0)
memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
unlock_kernel();
+ dprintk("%s: returns %d\n", __FUNCTION__, status);
return status;
}
@@ -2163,6 +2159,193 @@ nfs4_proc_file_release(struct inode *inode, struct file *filp)
return 0;
}
+static inline int nfs4_server_supports_acls(struct nfs_server *server)
+{
+ return (server->caps & NFS_CAP_ACLS)
+ && (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)
+ && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
+}
+
+/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that
+ * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on
+ * the stack.
+ */
+#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)
+
+static void buf_to_pages(const void *buf, size_t buflen,
+ struct page **pages, unsigned int *pgbase)
+{
+ const void *p = buf;
+
+ *pgbase = offset_in_page(buf);
+ p -= *pgbase;
+ while (p < buf + buflen) {
+ *(pages++) = virt_to_page(p);
+ p += PAGE_CACHE_SIZE;
+ }
+}
+
+struct nfs4_cached_acl {
+ int cached;
+ size_t len;
+ char data[0];
+};
+
+static void nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+
+ spin_lock(&inode->i_lock);
+ kfree(nfsi->nfs4_acl);
+ nfsi->nfs4_acl = acl;
+ spin_unlock(&inode->i_lock);
+}
+
+static void nfs4_zap_acl_attr(struct inode *inode)
+{
+ nfs4_set_cached_acl(inode, NULL);
+}
+
+static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+ struct nfs4_cached_acl *acl;
+ int ret = -ENOENT;
+
+ spin_lock(&inode->i_lock);
+ acl = nfsi->nfs4_acl;
+ if (acl == NULL)
+ goto out;
+ if (buf == NULL) /* user is just asking for length */
+ goto out_len;
+ if (acl->cached == 0)
+ goto out;
+ ret = -ERANGE; /* see getxattr(2) man page */
+ if (acl->len > buflen)
+ goto out;
+ memcpy(buf, acl->data, acl->len);
+out_len:
+ ret = acl->len;
+out:
+ spin_unlock(&inode->i_lock);
+ return ret;
+}
+
+static void nfs4_write_cached_acl(struct inode *inode, const char *buf, size_t acl_len)
+{
+ struct nfs4_cached_acl *acl;
+
+ if (buf && acl_len <= PAGE_SIZE) {
+ acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
+ if (acl == NULL)
+ goto out;
+ acl->cached = 1;
+ memcpy(acl->data, buf, acl_len);
+ } else {
+ acl = kmalloc(sizeof(*acl), GFP_KERNEL);
+ if (acl == NULL)
+ goto out;
+ acl->cached = 0;
+ }
+ acl->len = acl_len;
+out:
+ nfs4_set_cached_acl(inode, acl);
+}
+
+static inline ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+{
+ struct page *pages[NFS4ACL_MAXPAGES];
+ struct nfs_getaclargs args = {
+ .fh = NFS_FH(inode),
+ .acl_pages = pages,
+ .acl_len = buflen,
+ };
+ size_t resp_len = buflen;
+ void *resp_buf;
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
+ .rpc_argp = &args,
+ .rpc_resp = &resp_len,
+ };
+ struct page *localpage = NULL;
+ int ret;
+
+ if (buflen < PAGE_SIZE) {
+ /* As long as we're doing a round trip to the server anyway,
+ * let's be prepared for a page of acl data. */
+ localpage = alloc_page(GFP_KERNEL);
+ resp_buf = page_address(localpage);
+ if (localpage == NULL)
+ return -ENOMEM;
+ args.acl_pages[0] = localpage;
+ args.acl_pgbase = 0;
+ args.acl_len = PAGE_SIZE;
+ } else {
+ resp_buf = buf;
+ buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
+ }
+ ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ if (ret)
+ goto out_free;
+ if (resp_len > args.acl_len)
+ nfs4_write_cached_acl(inode, NULL, resp_len);
+ else
+ nfs4_write_cached_acl(inode, resp_buf, resp_len);
+ if (buf) {
+ ret = -ERANGE;
+ if (resp_len > buflen)
+ goto out_free;
+ if (localpage)
+ memcpy(buf, resp_buf, resp_len);
+ }
+ ret = resp_len;
+out_free:
+ if (localpage)
+ __free_page(localpage);
+ return ret;
+}
+
+static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
+{
+ struct nfs_server *server = NFS_SERVER(inode);
+ int ret;
+
+ if (!nfs4_server_supports_acls(server))
+ return -EOPNOTSUPP;
+ ret = nfs_revalidate_inode(server, inode);
+ if (ret < 0)
+ return ret;
+ ret = nfs4_read_cached_acl(inode, buf, buflen);
+ if (ret != -ENOENT)
+ return ret;
+ return nfs4_get_acl_uncached(inode, buf, buflen);
+}
+
+static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+{
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct page *pages[NFS4ACL_MAXPAGES];
+ struct nfs_setaclargs arg = {
+ .fh = NFS_FH(inode),
+ .acl_pages = pages,
+ .acl_len = buflen,
+ };
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETACL],
+ .rpc_argp = &arg,
+ .rpc_resp = NULL,
+ };
+ int ret;
+
+ if (!nfs4_server_supports_acls(server))
+ return -EOPNOTSUPP;
+ buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
+ ret = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
+ if (ret == 0)
+ nfs4_write_cached_acl(inode, buf, buflen);
+ return ret;
+}
+
static int
nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server)
{
@@ -2448,14 +2631,11 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
down_read(&clp->cl_sem);
nlo.clientid = clp->cl_clientid;
down(&state->lock_sema);
- lsp = nfs4_find_lock_state(state, request->fl_owner);
- if (lsp)
- nlo.id = lsp->ls_id;
- else {
- spin_lock(&clp->cl_lock);
- nlo.id = nfs4_alloc_lockowner_id(clp);
- spin_unlock(&clp->cl_lock);
- }
+ status = nfs4_set_lock_state(state, request);
+ if (status != 0)
+ goto out;
+ lsp = request->fl_u.nfs4_fl.owner;
+ nlo.id = lsp->ls_id;
arg.u.lockt = &nlo;
status = rpc_call_sync(server->client, &msg, 0);
if (!status) {
@@ -2476,8 +2656,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
request->fl_pid = 0;
status = 0;
}
- if (lsp)
- nfs4_put_lock_state(lsp);
+out:
up(&state->lock_sema);
up_read(&clp->cl_sem);
return status;
@@ -2537,28 +2716,26 @@ static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock
};
struct nfs4_lock_state *lsp;
struct nfs_locku_opargs luargs;
- int status = 0;
+ int status;
down_read(&clp->cl_sem);
down(&state->lock_sema);
- lsp = nfs4_find_lock_state(state, request->fl_owner);
- if (!lsp)
+ status = nfs4_set_lock_state(state, request);
+ if (status != 0)
goto out;
+ lsp = request->fl_u.nfs4_fl.owner;
/* We might have lost the locks! */
- if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) {
- luargs.seqid = lsp->ls_seqid;
- memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid));
- arg.u.locku = &luargs;
- status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- nfs4_increment_lock_seqid(status, lsp);
- }
+ if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0)
+ goto out;
+ luargs.seqid = lsp->ls_seqid;
+ memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid));
+ arg.u.locku = &luargs;
+ status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
+ nfs4_increment_lock_seqid(status, lsp);
- if (status == 0) {
+ if (status == 0)
memcpy(&lsp->ls_stateid, &res.u.stateid,
sizeof(lsp->ls_stateid));
- nfs4_notify_unlck(state, request, lsp);
- }
- nfs4_put_lock_state(lsp);
out:
up(&state->lock_sema);
if (status == 0)
@@ -2584,7 +2761,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *r
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
- struct nfs4_lock_state *lsp;
+ struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner;
struct nfs_lockargs arg = {
.fh = NFS_FH(inode),
.type = nfs4_lck_type(cmd, request),
@@ -2606,9 +2783,6 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *r
};
int status;
- lsp = nfs4_get_lock_state(state, request->fl_owner);
- if (lsp == NULL)
- return -ENOMEM;
if (!(lsp->ls_flags & NFS_LOCK_INITIALIZED)) {
struct nfs4_state_owner *owner = state->owner;
struct nfs_open_to_lock otl = {
@@ -2630,38 +2804,57 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *r
* seqid mutating errors */
nfs4_increment_seqid(status, owner);
up(&owner->so_sema);
+ if (status == 0) {
+ lsp->ls_flags |= NFS_LOCK_INITIALIZED;
+ lsp->ls_seqid++;
+ }
} else {
struct nfs_exist_lock el = {
.seqid = lsp->ls_seqid,
};
memcpy(&el.stateid, &lsp->ls_stateid, sizeof(el.stateid));
largs.u.exist_lock = &el;
- largs.new_lock_owner = 0;
arg.u.lock = &largs;
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
+ /* increment seqid on success, and * seqid mutating errors*/
+ nfs4_increment_lock_seqid(status, lsp);
}
- /* increment seqid on success, and * seqid mutating errors*/
- nfs4_increment_lock_seqid(status, lsp);
/* save the returned stateid. */
- if (status == 0) {
+ if (status == 0)
memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid));
- lsp->ls_flags |= NFS_LOCK_INITIALIZED;
- if (!reclaim)
- nfs4_notify_setlk(state, request, lsp);
- } else if (status == -NFS4ERR_DENIED)
+ else if (status == -NFS4ERR_DENIED)
status = -EAGAIN;
- nfs4_put_lock_state(lsp);
return status;
}
static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
{
- return _nfs4_do_setlk(state, F_SETLK, request, 1);
+ struct nfs_server *server = NFS_SERVER(state->inode);
+ struct nfs4_exception exception = { };
+ int err;
+
+ do {
+ err = _nfs4_do_setlk(state, F_SETLK, request, 1);
+ if (err != -NFS4ERR_DELAY)
+ break;
+ nfs4_handle_exception(server, err, &exception);
+ } while (exception.retry);
+ return err;
}
static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request)
{
- return _nfs4_do_setlk(state, F_SETLK, request, 0);
+ struct nfs_server *server = NFS_SERVER(state->inode);
+ struct nfs4_exception exception = { };
+ int err;
+
+ do {
+ err = _nfs4_do_setlk(state, F_SETLK, request, 0);
+ if (err != -NFS4ERR_DELAY)
+ break;
+ nfs4_handle_exception(server, err, &exception);
+ } while (exception.retry);
+ return err;
}
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
@@ -2671,7 +2864,9 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
down_read(&clp->cl_sem);
down(&state->lock_sema);
- status = _nfs4_do_setlk(state, cmd, request, 0);
+ status = nfs4_set_lock_state(state, request);
+ if (status == 0)
+ status = _nfs4_do_setlk(state, cmd, request, 0);
up(&state->lock_sema);
if (status == 0) {
/* Note: we always want to sleep here! */
@@ -2729,10 +2924,53 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
if (signalled())
break;
} while(status < 0);
-
return status;
}
+
+#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
+
+int nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf,
+ size_t buflen, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+
+ if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0)
+ return -EOPNOTSUPP;
+
+ if (!S_ISREG(inode->i_mode) &&
+ (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
+ return -EPERM;
+
+ return nfs4_proc_set_acl(inode, buf, buflen);
+}
+
+/* The getxattr man page suggests returning -ENODATA for unknown attributes,
+ * and that's what we'll do for e.g. user attributes that haven't been set.
+ * But we'll follow ext2/ext3's lead by returning -EOPNOTSUPP for unsupported
+ * attributes in kernel-managed attribute namespaces. */
+ssize_t nfs4_getxattr(struct dentry *dentry, const char *key, void *buf,
+ size_t buflen)
+{
+ struct inode *inode = dentry->d_inode;
+
+ if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0)
+ return -EOPNOTSUPP;
+
+ return nfs4_proc_get_acl(inode, buf, buflen);
+}
+
+ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen)
+{
+ size_t len = strlen(XATTR_NAME_NFSV4_ACL) + 1;
+
+ if (buf && buflen < len)
+ return -ERANGE;
+ if (buf)
+ memcpy(buf, XATTR_NAME_NFSV4_ACL, len);
+ return len;
+}
+
struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
.recover_open = nfs4_open_reclaim,
.recover_lock = nfs4_lock_reclaim,
@@ -2743,10 +2981,20 @@ struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops = {
.recover_lock = nfs4_lock_expired,
};
+static struct inode_operations nfs4_file_inode_operations = {
+ .permission = nfs_permission,
+ .getattr = nfs_getattr,
+ .setattr = nfs_setattr,
+ .getxattr = nfs4_getxattr,
+ .setxattr = nfs4_setxattr,
+ .listxattr = nfs4_listxattr,
+};
+
struct nfs_rpc_ops nfs_v4_clientops = {
.version = 4, /* protocol version */
.dentry_ops = &nfs4_dentry_operations,
.dir_inode_ops = &nfs4_dir_inode_operations,
+ .file_inode_ops = &nfs4_file_inode_operations,
.getroot = nfs4_proc_get_root,
.getattr = nfs4_proc_getattr,
.setattr = nfs4_proc_setattr,
@@ -2777,6 +3025,7 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.file_open = nfs4_proc_file_open,
.file_release = nfs4_proc_file_release,
.lock = nfs4_proc_lock,
+ .clear_acl_cache = nfs4_zap_acl_attr,
};
/*
diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c
index 667e06f1c64..a3001628ad3 100644
--- a/fs/nfs/nfs4renewd.c
+++ b/fs/nfs/nfs4renewd.c
@@ -53,6 +53,7 @@
#include <linux/nfs.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
+#include "nfs4_fs.h"
#define NFSDBG_FACILITY NFSDBG_PROC
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 231cebce3c8..afe587d82f1 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -46,24 +46,18 @@
#include <linux/workqueue.h>
#include <linux/bitops.h>
+#include "nfs4_fs.h"
#include "callback.h"
#include "delegation.h"
#define OPENOWNER_POOL_SIZE 8
-static DEFINE_SPINLOCK(state_spinlock);
-
-nfs4_stateid zero_stateid;
-
-#if 0
-nfs4_stateid one_stateid =
- { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-#endif
+const nfs4_stateid zero_stateid;
+static DEFINE_SPINLOCK(state_spinlock);
static LIST_HEAD(nfs4_clientid_list);
static void nfs4_recover_state(void *);
-extern void nfs4_renew_state(void *);
void
init_nfsv4_state(struct nfs_server *server)
@@ -116,6 +110,7 @@ nfs4_alloc_client(struct in_addr *addr)
INIT_LIST_HEAD(&clp->cl_superblocks);
init_waitqueue_head(&clp->cl_waitq);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
+ clp->cl_rpcclient = ERR_PTR(-EINVAL);
clp->cl_boot_time = CURRENT_TIME;
clp->cl_state = 1 << NFS4CLNT_OK;
return clp;
@@ -137,7 +132,7 @@ nfs4_free_client(struct nfs4_client *clp)
if (clp->cl_cred)
put_rpccred(clp->cl_cred);
nfs_idmap_delete(clp);
- if (clp->cl_rpcclient)
+ if (!IS_ERR(clp->cl_rpcclient))
rpc_shutdown_client(clp->cl_rpcclient);
kfree(clp);
nfs_callback_down();
@@ -365,7 +360,7 @@ nfs4_alloc_open_state(void)
atomic_set(&state->count, 1);
INIT_LIST_HEAD(&state->lock_states);
init_MUTEX(&state->lock_sema);
- rwlock_init(&state->state_lock);
+ spin_lock_init(&state->state_lock);
return state;
}
@@ -547,16 +542,6 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
return NULL;
}
-struct nfs4_lock_state *
-nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
-{
- struct nfs4_lock_state *lsp;
- read_lock(&state->state_lock);
- lsp = __nfs4_find_lock_state(state, fl_owner);
- read_unlock(&state->state_lock);
- return lsp;
-}
-
/*
* Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one.
@@ -573,14 +558,13 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
return NULL;
lsp->ls_flags = 0;
lsp->ls_seqid = 0; /* arbitrary */
- lsp->ls_id = -1;
memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data));
atomic_set(&lsp->ls_count, 1);
lsp->ls_owner = fl_owner;
- INIT_LIST_HEAD(&lsp->ls_locks);
spin_lock(&clp->cl_lock);
lsp->ls_id = nfs4_alloc_lockowner_id(clp);
spin_unlock(&clp->cl_lock);
+ INIT_LIST_HEAD(&lsp->ls_locks);
return lsp;
}
@@ -590,121 +574,112 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
*
* The caller must be holding state->lock_sema and clp->cl_sem
*/
-struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
+static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
{
- struct nfs4_lock_state * lsp;
+ struct nfs4_lock_state *lsp, *new = NULL;
- lsp = nfs4_find_lock_state(state, owner);
- if (lsp == NULL)
- lsp = nfs4_alloc_lock_state(state, owner);
+ for(;;) {
+ spin_lock(&state->state_lock);
+ lsp = __nfs4_find_lock_state(state, owner);
+ if (lsp != NULL)
+ break;
+ if (new != NULL) {
+ new->ls_state = state;
+ list_add(&new->ls_locks, &state->lock_states);
+ set_bit(LK_STATE_IN_USE, &state->flags);
+ lsp = new;
+ new = NULL;
+ break;
+ }
+ spin_unlock(&state->state_lock);
+ new = nfs4_alloc_lock_state(state, owner);
+ if (new == NULL)
+ return NULL;
+ }
+ spin_unlock(&state->state_lock);
+ kfree(new);
return lsp;
}
/*
- * Byte-range lock aware utility to initialize the stateid of read/write
- * requests.
+ * Release reference to lock_state, and free it if we see that
+ * it is no longer in use
*/
-void
-nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner)
+static void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
{
- if (test_bit(LK_STATE_IN_USE, &state->flags)) {
- struct nfs4_lock_state *lsp;
+ struct nfs4_state *state;
- lsp = nfs4_find_lock_state(state, fl_owner);
- if (lsp) {
- memcpy(dst, &lsp->ls_stateid, sizeof(*dst));
- nfs4_put_lock_state(lsp);
- return;
- }
- }
- memcpy(dst, &state->stateid, sizeof(*dst));
+ if (lsp == NULL)
+ return;
+ state = lsp->ls_state;
+ if (!atomic_dec_and_lock(&lsp->ls_count, &state->state_lock))
+ return;
+ list_del(&lsp->ls_locks);
+ if (list_empty(&state->lock_states))
+ clear_bit(LK_STATE_IN_USE, &state->flags);
+ spin_unlock(&state->state_lock);
+ kfree(lsp);
}
-/*
-* Called with state->lock_sema and clp->cl_sem held.
-*/
-void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp)
+static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
{
- if (status == NFS_OK || seqid_mutating_err(-status))
- lsp->ls_seqid++;
-}
+ struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner;
-/*
-* Check to see if the request lock (type FL_UNLK) effects the fl lock.
-*
-* fl and request must have the same posix owner
-*
-* return:
-* 0 -> fl not effected by request
-* 1 -> fl consumed by request
-*/
+ dst->fl_u.nfs4_fl.owner = lsp;
+ atomic_inc(&lsp->ls_count);
+}
-static int
-nfs4_check_unlock(struct file_lock *fl, struct file_lock *request)
+static void nfs4_fl_release_lock(struct file_lock *fl)
{
- if (fl->fl_start >= request->fl_start && fl->fl_end <= request->fl_end)
- return 1;
- return 0;
+ nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner);
}
-/*
- * Post an initialized lock_state on the state->lock_states list.
- */
-void nfs4_notify_setlk(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp)
+static struct file_lock_operations nfs4_fl_lock_ops = {
+ .fl_copy_lock = nfs4_fl_copy_lock,
+ .fl_release_private = nfs4_fl_release_lock,
+};
+
+int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
{
- if (!list_empty(&lsp->ls_locks))
- return;
- atomic_inc(&lsp->ls_count);
- write_lock(&state->state_lock);
- list_add(&lsp->ls_locks, &state->lock_states);
- set_bit(LK_STATE_IN_USE, &state->flags);
- write_unlock(&state->state_lock);
+ struct nfs4_lock_state *lsp;
+
+ if (fl->fl_ops != NULL)
+ return 0;
+ lsp = nfs4_get_lock_state(state, fl->fl_owner);
+ if (lsp == NULL)
+ return -ENOMEM;
+ fl->fl_u.nfs4_fl.owner = lsp;
+ fl->fl_ops = &nfs4_fl_lock_ops;
+ return 0;
}
-/*
- * to decide to 'reap' lock state:
- * 1) search i_flock for file_locks with fl.lock_state = to ls.
- * 2) determine if unlock will consume found lock.
- * if so, reap
- *
- * else, don't reap.
- *
+/*
+ * Byte-range lock aware utility to initialize the stateid of read/write
+ * requests.
*/
-void
-nfs4_notify_unlck(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp)
+void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner)
{
- struct inode *inode = state->inode;
- struct file_lock *fl;
+ struct nfs4_lock_state *lsp;
- for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (!(fl->fl_flags & FL_POSIX))
- continue;
- if (fl->fl_owner != lsp->ls_owner)
- continue;
- /* Exit if we find at least one lock which is not consumed */
- if (nfs4_check_unlock(fl,request) == 0)
- return;
- }
+ memcpy(dst, &state->stateid, sizeof(*dst));
+ if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
+ return;
- write_lock(&state->state_lock);
- list_del_init(&lsp->ls_locks);
- if (list_empty(&state->lock_states))
- clear_bit(LK_STATE_IN_USE, &state->flags);
- write_unlock(&state->state_lock);
+ spin_lock(&state->state_lock);
+ lsp = __nfs4_find_lock_state(state, fl_owner);
+ if (lsp != NULL && (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0)
+ memcpy(dst, &lsp->ls_stateid, sizeof(*dst));
+ spin_unlock(&state->state_lock);
nfs4_put_lock_state(lsp);
}
/*
- * Release reference to lock_state, and free it if we see that
- * it is no longer in use
- */
-void
-nfs4_put_lock_state(struct nfs4_lock_state *lsp)
+* Called with state->lock_sema and clp->cl_sem held.
+*/
+void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp)
{
- if (!atomic_dec_and_test(&lsp->ls_count))
- return;
- BUG_ON (!list_empty(&lsp->ls_locks));
- kfree(lsp);
+ if (status == NFS_OK || seqid_mutating_err(-status))
+ lsp->ls_seqid++;
}
/*
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 5f4de05763c..6c564ef9489 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -51,6 +51,7 @@
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h>
+#include "nfs4_fs.h"
#define NFSDBG_FACILITY NFSDBG_XDR
@@ -82,12 +83,16 @@ static int nfs_stat_to_errno(int);
#define encode_getfh_maxsz (op_encode_hdr_maxsz)
#define decode_getfh_maxsz (op_decode_hdr_maxsz + 1 + \
((3+NFS4_FHSIZE) >> 2))
-#define encode_getattr_maxsz (op_encode_hdr_maxsz + 3)
+#define nfs4_fattr_bitmap_maxsz 3
+#define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)
#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
-#define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz)
-#define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \
- nfs4_fattr_bitmap_maxsz)
+/* This is based on getfattr, which uses the most attributes: */
+#define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
+ 3 + 3 + 3 + 2 * nfs4_name_maxsz))
+#define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \
+ nfs4_fattr_value_maxsz)
+#define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
#define encode_savefh_maxsz (op_encode_hdr_maxsz)
#define decode_savefh_maxsz (op_decode_hdr_maxsz)
#define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2)
@@ -122,11 +127,11 @@ static int nfs_stat_to_errno(int);
#define encode_symlink_maxsz (op_encode_hdr_maxsz + \
1 + nfs4_name_maxsz + \
nfs4_path_maxsz + \
- nfs4_fattr_bitmap_maxsz)
+ nfs4_fattr_maxsz)
#define decode_symlink_maxsz (op_decode_hdr_maxsz + 8)
#define encode_create_maxsz (op_encode_hdr_maxsz + \
2 + nfs4_name_maxsz + \
- nfs4_fattr_bitmap_maxsz)
+ nfs4_fattr_maxsz)
#define decode_create_maxsz (op_decode_hdr_maxsz + 8)
#define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4)
#define decode_delegreturn_maxsz (op_decode_hdr_maxsz)
@@ -205,7 +210,7 @@ static int nfs_stat_to_errno(int);
#define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 4 + \
- nfs4_fattr_bitmap_maxsz + \
+ nfs4_fattr_maxsz + \
encode_getattr_maxsz)
#define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
@@ -360,6 +365,20 @@ static int nfs_stat_to_errno(int);
encode_delegreturn_maxsz)
#define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \
decode_delegreturn_maxsz)
+#define NFS4_enc_getacl_sz (compound_encode_hdr_maxsz + \
+ encode_putfh_maxsz + \
+ encode_getattr_maxsz)
+#define NFS4_dec_getacl_sz (compound_decode_hdr_maxsz + \
+ decode_putfh_maxsz + \
+ op_decode_hdr_maxsz + \
+ nfs4_fattr_bitmap_maxsz + 1)
+#define NFS4_enc_setacl_sz (compound_encode_hdr_maxsz + \
+ encode_putfh_maxsz + \
+ op_encode_hdr_maxsz + 4 + \
+ nfs4_fattr_bitmap_maxsz + 1)
+#define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \
+ decode_putfh_maxsz + \
+ op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)
static struct {
unsigned int mode;
@@ -459,7 +478,7 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
* In the worst-case, this would be
* 12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
* = 36 bytes, plus any contribution from variable-length fields
- * such as owner/group/acl's.
+ * such as owner/group.
*/
len = 16;
@@ -660,8 +679,6 @@ static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1
static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask)
{
- extern u32 nfs4_fattr_bitmap[];
-
return encode_getattr_two(xdr,
bitmask[0] & nfs4_fattr_bitmap[0],
bitmask[1] & nfs4_fattr_bitmap[1]);
@@ -669,8 +686,6 @@ static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask)
static int encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask)
{
- extern u32 nfs4_fsinfo_bitmap[];
-
return encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0],
bitmask[1] & nfs4_fsinfo_bitmap[1]);
}
@@ -969,7 +984,6 @@ static int encode_putrootfh(struct xdr_stream *xdr)
static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
{
- extern nfs4_stateid zero_stateid;
nfs4_stateid stateid;
uint32_t *p;
@@ -1000,6 +1014,10 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req)
{
struct rpc_auth *auth = req->rq_task->tk_auth;
+ uint32_t attrs[2] = {
+ FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID,
+ FATTR4_WORD1_MOUNTED_ON_FILEID,
+ };
int replen;
uint32_t *p;
@@ -1010,13 +1028,20 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
WRITE32(readdir->count >> 1); /* We're not doing readdirplus */
WRITE32(readdir->count);
WRITE32(2);
- if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) {
- WRITE32(0);
- WRITE32(FATTR4_WORD1_MOUNTED_ON_FILEID);
- } else {
- WRITE32(FATTR4_WORD0_FILEID);
- WRITE32(0);
- }
+ /* Switch to mounted_on_fileid if the server supports it */
+ if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
+ attrs[0] &= ~FATTR4_WORD0_FILEID;
+ else
+ attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
+ WRITE32(attrs[0] & readdir->bitmask[0]);
+ WRITE32(attrs[1] & readdir->bitmask[1]);
+ dprintk("%s: cookie = %Lu, verifier = 0x%x%x, bitmap = 0x%x%x\n",
+ __FUNCTION__,
+ (unsigned long long)readdir->cookie,
+ ((u32 *)readdir->verifier.data)[0],
+ ((u32 *)readdir->verifier.data)[1],
+ attrs[0] & readdir->bitmask[0],
+ attrs[1] & readdir->bitmask[1]);
/* set up reply kvec
* toplevel_status + taglen + rescount + OP_PUTFH + status
@@ -1025,6 +1050,9 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
replen = (RPC_REPHDRSIZE + auth->au_rslack + 9) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen, readdir->pages,
readdir->pgbase, readdir->count);
+ dprintk("%s: inlined page args = (%u, %p, %u, %u)\n",
+ __FUNCTION__, replen, readdir->pages,
+ readdir->pgbase, readdir->count);
return 0;
}
@@ -1089,6 +1117,25 @@ static int encode_renew(struct xdr_stream *xdr, const struct nfs4_client *client
}
static int
+encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
+{
+ uint32_t *p;
+
+ RESERVE_SPACE(4+sizeof(zero_stateid.data));
+ WRITE32(OP_SETATTR);
+ WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data));
+ RESERVE_SPACE(2*4);
+ WRITE32(1);
+ WRITE32(FATTR4_WORD0_ACL);
+ if (arg->acl_len % 4)
+ return -EINVAL;
+ RESERVE_SPACE(4);
+ WRITE32(arg->acl_len);
+ xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
+ return 0;
+}
+
+static int
encode_savefh(struct xdr_stream *xdr)
{
uint32_t *p;
@@ -1632,6 +1679,34 @@ out:
}
/*
+ * Encode a GETACL request
+ */
+static int
+nfs4_xdr_enc_getacl(struct rpc_rqst *req, uint32_t *p,
+ struct nfs_getaclargs *args)
+{
+ struct xdr_stream xdr;
+ struct rpc_auth *auth = req->rq_task->tk_auth;
+ struct compound_hdr hdr = {
+ .nops = 2,
+ };
+ int replen, status;
+
+ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+ encode_compound_hdr(&xdr, &hdr);
+ status = encode_putfh(&xdr, args->fh);
+ if (status)
+ goto out;
+ status = encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0);
+ /* set up reply buffer: */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen,
+ args->acl_pages, args->acl_pgbase, args->acl_len);
+out:
+ return status;
+}
+
+/*
* Encode a WRITE request
*/
static int nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args)
@@ -1697,7 +1772,6 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs4_fs
*/
static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, uint32_t *p, const struct nfs4_pathconf_arg *args)
{
- extern u32 nfs4_pathconf_bitmap[2];
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
@@ -1718,7 +1792,6 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, uint32_t *p, const struct
*/
static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, uint32_t *p, const struct nfs4_statfs_arg *args)
{
- extern u32 nfs4_statfs_bitmap[];
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
@@ -3003,6 +3076,11 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
return status;
READ_BUF(8);
COPYMEM(readdir->verifier.data, 8);
+ dprintk("%s: verifier = 0x%x%x\n",
+ __FUNCTION__,
+ ((u32 *)readdir->verifier.data)[0],
+ ((u32 *)readdir->verifier.data)[1]);
+
hdrlen = (char *) p - (char *) iov->iov_base;
recvd = rcvbuf->len - hdrlen;
@@ -3017,12 +3095,14 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
for (nr = 0; *p++; nr++) {
if (p + 3 > end)
goto short_pkt;
+ dprintk("cookie = %Lu, ", *((unsigned long long *)p));
p += 2; /* cookie */
len = ntohl(*p++); /* filename length */
if (len > NFS4_MAXNAMLEN) {
printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len);
goto err_unmap;
}
+ dprintk("filename = %*s\n", len, (char *)p);
p += XDR_QUADLEN(len);
if (p + 1 > end)
goto short_pkt;
@@ -3042,6 +3122,7 @@ out:
kunmap_atomic(kaddr, KM_USER0);
return 0;
short_pkt:
+ dprintk("%s: short packet at entry %d\n", __FUNCTION__, nr);
entry[0] = entry[1] = 0;
/* truncate listing ? */
if (!nr) {
@@ -3127,6 +3208,47 @@ static int decode_renew(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_RENEW);
}
+static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
+ size_t *acl_len)
+{
+ uint32_t *savep;
+ uint32_t attrlen,
+ bitmap[2] = {0};
+ struct kvec *iov = req->rq_rcv_buf.head;
+ int status;
+
+ *acl_len = 0;
+ if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
+ goto out;
+ if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
+ goto out;
+ if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
+ goto out;
+
+ if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
+ return -EIO;
+ if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
+ int hdrlen, recvd;
+
+ /* We ignore &savep and don't do consistency checks on
+ * the attr length. Let userspace figure it out.... */
+ hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
+ recvd = req->rq_rcv_buf.len - hdrlen;
+ if (attrlen > recvd) {
+ printk(KERN_WARNING "NFS: server cheating in getattr"
+ " acl reply: attrlen %u > recvd %u\n",
+ attrlen, recvd);
+ return -EINVAL;
+ }
+ if (attrlen <= *acl_len)
+ xdr_read_pages(xdr, attrlen);
+ *acl_len = attrlen;
+ }
+
+out:
+ return status;
+}
+
static int
decode_savefh(struct xdr_stream *xdr)
{
@@ -3418,6 +3540,71 @@ out:
}
+/*
+ * Encode an SETACL request
+ */
+static int
+nfs4_xdr_enc_setacl(struct rpc_rqst *req, uint32_t *p, struct nfs_setaclargs *args)
+{
+ struct xdr_stream xdr;
+ struct compound_hdr hdr = {
+ .nops = 2,
+ };
+ int status;
+
+ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+ encode_compound_hdr(&xdr, &hdr);
+ status = encode_putfh(&xdr, args->fh);
+ if (status)
+ goto out;
+ status = encode_setacl(&xdr, args);
+out:
+ return status;
+}
+/*
+ * Decode SETACL response
+ */
+static int
+nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, uint32_t *p, void *res)
+{
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_setattr(&xdr, res);
+out:
+ return status;
+}
+
+/*
+ * Decode GETACL response
+ */
+static int
+nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, uint32_t *p, size_t *acl_len)
+{
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_getacl(&xdr, rqstp, acl_len);
+
+out:
+ return status;
+}
/*
* Decode CLOSE response
@@ -3895,6 +4082,12 @@ uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
}
len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */
if (len > 0) {
+ if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) {
+ bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR;
+ /* Ignore the return value of rdattr_error for now */
+ p++;
+ len--;
+ }
if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID)
xdr_decode_hyper(p, &entry->ino);
else if (bitmap[0] == FATTR4_WORD0_FILEID)
@@ -3934,6 +4127,8 @@ static struct {
{ NFS4ERR_DQUOT, EDQUOT },
{ NFS4ERR_STALE, ESTALE },
{ NFS4ERR_BADHANDLE, EBADHANDLE },
+ { NFS4ERR_BADOWNER, EINVAL },
+ { NFS4ERR_BADNAME, EINVAL },
{ NFS4ERR_BAD_COOKIE, EBADCOOKIE },
{ NFS4ERR_NOTSUPP, ENOTSUPP },
{ NFS4ERR_TOOSMALL, ETOOSMALL },
@@ -4019,6 +4214,8 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(READDIR, enc_readdir, dec_readdir),
PROC(SERVER_CAPS, enc_server_caps, dec_server_caps),
PROC(DELEGRETURN, enc_delegreturn, dec_delegreturn),
+ PROC(GETACL, enc_getacl, dec_getacl),
+ PROC(SETACL, enc_setacl, dec_setacl),
};
struct rpc_version nfs_version4 = {
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index fd5bc596fe8..1b272a135a3 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -124,6 +124,7 @@ enum {
Opt_soft, Opt_hard, Opt_intr,
Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac,
Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp,
+ Opt_acl, Opt_noacl,
/* Error token */
Opt_err
};
@@ -158,6 +159,8 @@ static match_table_t __initdata tokens = {
{Opt_udp, "udp"},
{Opt_tcp, "proto=tcp"},
{Opt_tcp, "tcp"},
+ {Opt_acl, "acl"},
+ {Opt_noacl, "noacl"},
{Opt_err, NULL}
};
@@ -266,6 +269,12 @@ static int __init root_nfs_parse(char *name, char *buf)
case Opt_tcp:
nfs_data.flags |= NFS_MOUNT_TCP;
break;
+ case Opt_acl:
+ nfs_data.flags &= ~NFS_MOUNT_NOACL;
+ break;
+ case Opt_noacl:
+ nfs_data.flags |= NFS_MOUNT_NOACL;
+ break;
default :
return 0;
}
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 4f1ba723848..d53857b148e 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -107,11 +107,38 @@ void nfs_unlock_request(struct nfs_page *req)
smp_mb__before_clear_bit();
clear_bit(PG_BUSY, &req->wb_flags);
smp_mb__after_clear_bit();
- wake_up_all(&req->wb_context->waitq);
+ wake_up_bit(&req->wb_flags, PG_BUSY);
nfs_release_request(req);
}
/**
+ * nfs_set_page_writeback_locked - Lock a request for writeback
+ * @req:
+ */
+int nfs_set_page_writeback_locked(struct nfs_page *req)
+{
+ struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode);
+
+ if (!nfs_lock_request(req))
+ return 0;
+ radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_WRITEBACK);
+ return 1;
+}
+
+/**
+ * nfs_clear_page_writeback - Unlock request and wake up sleepers
+ */
+void nfs_clear_page_writeback(struct nfs_page *req)
+{
+ struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode);
+
+ spin_lock(&nfsi->req_lock);
+ radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_WRITEBACK);
+ spin_unlock(&nfsi->req_lock);
+ nfs_unlock_request(req);
+}
+
+/**
* nfs_clear_request - Free up all resources allocated to the request
* @req:
*
@@ -150,34 +177,15 @@ nfs_release_request(struct nfs_page *req)
nfs_page_free(req);
}
-/**
- * nfs_list_add_request - Insert a request into a sorted list
- * @req: request
- * @head: head of list into which to insert the request.
- *
- * Note that the wb_list is sorted by page index in order to facilitate
- * coalescing of requests.
- * We use an insertion sort that is optimized for the case of appended
- * writes.
- */
-void
-nfs_list_add_request(struct nfs_page *req, struct list_head *head)
+static int nfs_wait_bit_interruptible(void *word)
{
- struct list_head *pos;
+ int ret = 0;
-#ifdef NFS_PARANOIA
- if (!list_empty(&req->wb_list)) {
- printk(KERN_ERR "NFS: Add to list failed!\n");
- BUG();
- }
-#endif
- list_for_each_prev(pos, head) {
- struct nfs_page *p = nfs_list_entry(pos);
- if (p->wb_index < req->wb_index)
- break;
- }
- list_add(&req->wb_list, pos);
- req->wb_list_head = head;
+ if (signal_pending(current))
+ ret = -ERESTARTSYS;
+ else
+ schedule();
+ return ret;
}
/**
@@ -190,12 +198,22 @@ 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_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_context->waitq, !NFS_WBACK_BUSY(req));
+ struct rpc_clnt *clnt = NFS_CLIENT(req->wb_context->dentry->d_inode);
+ sigset_t oldmask;
+ int ret = 0;
+
+ if (!test_bit(PG_BUSY, &req->wb_flags))
+ goto out;
+ /*
+ * Note: the call to rpc_clnt_sigmask() suffices to ensure that we
+ * are not interrupted if intr flag is not set
+ */
+ rpc_clnt_sigmask(clnt, &oldmask);
+ ret = out_of_line_wait_on_bit(&req->wb_flags, PG_BUSY,
+ nfs_wait_bit_interruptible, TASK_INTERRUPTIBLE);
+ rpc_clnt_sigunmask(clnt, &oldmask);
+out:
+ return ret;
}
/**
@@ -243,6 +261,62 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
return npages;
}
+#define NFS_SCAN_MAXENTRIES 16
+/**
+ * nfs_scan_lock_dirty - Scan the radix tree for dirty requests
+ * @nfsi: NFS inode
+ * @dst: Destination list
+ * @idx_start: lower bound of page->index to scan
+ * @npages: idx_start + npages sets the upper bound to scan.
+ *
+ * Moves elements from one of the inode request lists.
+ * 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 inode's req_lock when calling this function
+ */
+int
+nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst,
+ unsigned long idx_start, unsigned int npages)
+{
+ struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES];
+ struct nfs_page *req;
+ unsigned long idx_end;
+ int found, i;
+ int res;
+
+ res = 0;
+ if (npages == 0)
+ idx_end = ~0;
+ else
+ idx_end = idx_start + npages - 1;
+
+ for (;;) {
+ found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree,
+ (void **)&pgvec[0], idx_start, NFS_SCAN_MAXENTRIES,
+ NFS_PAGE_TAG_DIRTY);
+ if (found <= 0)
+ break;
+ for (i = 0; i < found; i++) {
+ req = pgvec[i];
+ if (req->wb_index > idx_end)
+ goto out;
+
+ idx_start = req->wb_index + 1;
+
+ if (nfs_set_page_writeback_locked(req)) {
+ radix_tree_tag_clear(&nfsi->nfs_page_tree,
+ req->wb_index, NFS_PAGE_TAG_DIRTY);
+ nfs_list_remove_request(req);
+ nfs_list_add_request(req, dst);
+ res++;
+ }
+ }
+ }
+out:
+ return res;
+}
+
/**
* nfs_scan_list - Scan a list for matching requests
* @head: One of the NFS inode request lists
@@ -280,7 +354,7 @@ nfs_scan_list(struct list_head *head, struct list_head *dst,
if (req->wb_index > idx_end)
break;
- if (!nfs_lock_request(req))
+ if (!nfs_set_page_writeback_locked(req))
continue;
nfs_list_remove_request(req);
nfs_list_add_request(req, dst);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index d31b4d6e5a5..cedf636bcf3 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -622,6 +622,7 @@ struct nfs_rpc_ops nfs_v2_clientops = {
.version = 2, /* protocol version */
.dentry_ops = &nfs_dentry_operations,
.dir_inode_ops = &nfs_dir_inode_operations,
+ .file_inode_ops = &nfs_file_inode_operations,
.getroot = nfs_proc_get_root,
.getattr = nfs_proc_getattr,
.setattr = nfs_proc_setattr,
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index a0042fb5863..6f866b8aa2d 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -173,7 +173,6 @@ static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
if (len < PAGE_CACHE_SIZE)
memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len);
- nfs_lock_request(new);
nfs_list_add_request(new, &one_request);
nfs_pagein_one(&one_request, inode);
return 0;
@@ -185,7 +184,6 @@ static void nfs_readpage_release(struct nfs_page *req)
nfs_clear_request(req);
nfs_release_request(req);
- nfs_unlock_request(req);
dprintk("NFS: read done (%s/%Ld %d@%Ld)\n",
req->wb_context->dentry->d_inode->i_sb->s_id,
@@ -553,7 +551,6 @@ readpage_async_filler(void *data, struct page *page)
}
if (len < PAGE_CACHE_SIZE)
memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len);
- nfs_lock_request(new);
nfs_list_add_request(new, desc->head);
return 0;
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 6f7a4af3bc4..5130eda231d 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -220,7 +220,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
ClearPageError(page);
io_error:
- nfs_end_data_update_defer(inode);
+ nfs_end_data_update(inode);
nfs_writedata_free(wdata);
return written ? written : result;
}
@@ -352,7 +352,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
if (err < 0)
goto out;
}
- err = nfs_commit_inode(inode, 0, 0, wb_priority(wbc));
+ err = nfs_commit_inode(inode, wb_priority(wbc));
if (err > 0) {
wbc->nr_to_write -= err;
err = 0;
@@ -401,7 +401,7 @@ static void nfs_inode_remove_request(struct nfs_page *req)
nfsi->npages--;
if (!nfsi->npages) {
spin_unlock(&nfsi->req_lock);
- nfs_end_data_update_defer(inode);
+ nfs_end_data_update(inode);
iput(inode);
} else
spin_unlock(&nfsi->req_lock);
@@ -446,6 +446,8 @@ nfs_mark_request_dirty(struct nfs_page *req)
struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfsi->req_lock);
+ radix_tree_tag_set(&nfsi->nfs_page_tree,
+ req->wb_index, NFS_PAGE_TAG_DIRTY);
nfs_list_add_request(req, &nfsi->dirty);
nfsi->ndirty++;
spin_unlock(&nfsi->req_lock);
@@ -503,13 +505,12 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int
spin_lock(&nfsi->req_lock);
next = idx_start;
- while (radix_tree_gang_lookup(&nfsi->nfs_page_tree, (void **)&req, next, 1)) {
+ while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_WRITEBACK)) {
if (req->wb_index > idx_end)
break;
next = req->wb_index + 1;
- if (!NFS_WBACK_BUSY(req))
- continue;
+ BUG_ON(!NFS_WBACK_BUSY(req));
atomic_inc(&req->wb_count);
spin_unlock(&nfsi->req_lock);
@@ -538,12 +539,15 @@ static int
nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
{
struct nfs_inode *nfsi = NFS_I(inode);
- int res;
- res = nfs_scan_list(&nfsi->dirty, dst, idx_start, npages);
- nfsi->ndirty -= res;
- sub_page_state(nr_dirty,res);
- if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty))
- printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+ int res = 0;
+
+ if (nfsi->ndirty != 0) {
+ res = nfs_scan_lock_dirty(nfsi, dst, idx_start, npages);
+ nfsi->ndirty -= res;
+ sub_page_state(nr_dirty,res);
+ if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty))
+ printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+ }
return res;
}
@@ -562,11 +566,14 @@ static int
nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
{
struct nfs_inode *nfsi = NFS_I(inode);
- int res;
- res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages);
- nfsi->ncommit -= res;
- if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
- printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+ int res = 0;
+
+ if (nfsi->ncommit != 0) {
+ res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages);
+ nfsi->ncommit -= res;
+ if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
+ printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+ }
return res;
}
#endif
@@ -750,7 +757,7 @@ int nfs_updatepage(struct file *file, struct page *page,
* is entirely in cache, it may be more efficient to avoid
* fragmenting write requests.
*/
- if (PageUptodate(page) && inode->i_flock == NULL) {
+ if (PageUptodate(page) && inode->i_flock == NULL && !(file->f_mode & O_SYNC)) {
loff_t end_offs = i_size_read(inode) - 1;
unsigned long end_index = end_offs >> PAGE_CACHE_SHIFT;
@@ -821,7 +828,7 @@ out:
#else
nfs_inode_remove_request(req);
#endif
- nfs_unlock_request(req);
+ nfs_clear_page_writeback(req);
}
static inline int flush_task_priority(int how)
@@ -952,7 +959,7 @@ out_bad:
nfs_writedata_free(data);
}
nfs_mark_request_dirty(req);
- nfs_unlock_request(req);
+ nfs_clear_page_writeback(req);
return -ENOMEM;
}
@@ -1002,7 +1009,7 @@ static int nfs_flush_one(struct list_head *head, struct inode *inode, int how)
struct nfs_page *req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_mark_request_dirty(req);
- nfs_unlock_request(req);
+ nfs_clear_page_writeback(req);
}
return -ENOMEM;
}
@@ -1029,7 +1036,7 @@ nfs_flush_list(struct list_head *head, int wpages, int how)
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_mark_request_dirty(req);
- nfs_unlock_request(req);
+ nfs_clear_page_writeback(req);
}
return error;
}
@@ -1121,7 +1128,7 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status)
nfs_inode_remove_request(req);
#endif
next:
- nfs_unlock_request(req);
+ nfs_clear_page_writeback(req);
}
}
@@ -1210,36 +1217,24 @@ static void nfs_commit_rpcsetup(struct list_head *head,
struct nfs_write_data *data, int how)
{
struct rpc_task *task = &data->task;
- struct nfs_page *first, *last;
+ struct nfs_page *first;
struct inode *inode;
- loff_t start, end, len;
/* Set up the RPC argument and reply structs
* NB: take care not to mess about with data->commit et al. */
list_splice_init(head, &data->pages);
first = nfs_list_entry(data->pages.next);
- last = nfs_list_entry(data->pages.prev);
inode = first->wb_context->dentry->d_inode;
- /*
- * Determine the offset range of requests in the COMMIT call.
- * We rely on the fact that data->pages is an ordered list...
- */
- start = req_offset(first);
- end = req_offset(last) + last->wb_bytes;
- len = end - start;
- /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */
- if (end >= i_size_read(inode) || len < 0 || len > (~((u32)0) >> 1))
- len = 0;
-
data->inode = inode;
data->cred = first->wb_context->cred;
data->args.fh = NFS_FH(data->inode);
- data->args.offset = start;
- data->args.count = len;
- data->res.count = len;
+ /* Note: we always request a commit of the entire inode */
+ data->args.offset = 0;
+ data->args.count = 0;
+ data->res.count = 0;
data->res.fattr = &data->fattr;
data->res.verf = &data->verf;
@@ -1278,7 +1273,7 @@ nfs_commit_list(struct list_head *head, int how)
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_mark_request_commit(req);
- nfs_unlock_request(req);
+ nfs_clear_page_writeback(req);
}
return -ENOMEM;
}
@@ -1324,7 +1319,7 @@ nfs_commit_done(struct rpc_task *task)
dprintk(" mismatch\n");
nfs_mark_request_dirty(req);
next:
- nfs_unlock_request(req);
+ nfs_clear_page_writeback(req);
res++;
}
sub_page_state(nr_unstable,res);
@@ -1342,16 +1337,23 @@ static int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
spin_lock(&nfsi->req_lock);
res = nfs_scan_dirty(inode, &head, idx_start, npages);
spin_unlock(&nfsi->req_lock);
- if (res)
- error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
+ if (res) {
+ struct nfs_server *server = NFS_SERVER(inode);
+
+ /* For single writes, FLUSH_STABLE is more efficient */
+ if (res == nfsi->npages && nfsi->npages <= server->wpages) {
+ if (res > 1 || nfs_list_entry(head.next)->wb_bytes <= server->wsize)
+ how |= FLUSH_STABLE;
+ }
+ error = nfs_flush_list(&head, server->wpages, how);
+ }
if (error < 0)
return error;
return res;
}
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
-int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
- unsigned int npages, int how)
+int nfs_commit_inode(struct inode *inode, int how)
{
struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head);
@@ -1359,15 +1361,13 @@ int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
error = 0;
spin_lock(&nfsi->req_lock);
- res = nfs_scan_commit(inode, &head, idx_start, npages);
+ res = nfs_scan_commit(inode, &head, 0, 0);
+ spin_unlock(&nfsi->req_lock);
if (res) {
- res += nfs_scan_commit(inode, &head, 0, 0);
- spin_unlock(&nfsi->req_lock);
error = nfs_commit_list(&head, how);
- } else
- spin_unlock(&nfsi->req_lock);
- if (error < 0)
- return error;
+ if (error < 0)
+ return error;
+ }
return res;
}
#endif
@@ -1389,7 +1389,7 @@ int nfs_sync_inode(struct inode *inode, unsigned long idx_start,
error = nfs_flush_inode(inode, idx_start, npages, how);
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
if (error == 0)
- error = nfs_commit_inode(inode, idx_start, npages, how);
+ error = nfs_commit_inode(inode, how);
#endif
} while (error > 0);
return error;
diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile
new file mode 100644
index 00000000000..f689ed82af3
--- /dev/null
+++ b/fs/nfs_common/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Linux filesystem routines that are shared by client and server.
+#
+
+obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
+
+nfs_acl-objs := nfsacl.o
diff --git a/fs/nfs_common/nfsacl.c b/fs/nfs_common/nfsacl.c
new file mode 100644
index 00000000000..18c58c32e32
--- /dev/null
+++ b/fs/nfs_common/nfsacl.c
@@ -0,0 +1,257 @@
+/*
+ * fs/nfs_common/nfsacl.c
+ *
+ * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+
+/*
+ * The Solaris nfsacl protocol represents some ACLs slightly differently
+ * than POSIX 1003.1e draft 17 does (and we do):
+ *
+ * - Minimal ACLs always have an ACL_MASK entry, so they have
+ * four instead of three entries.
+ * - The ACL_MASK entry in such minimal ACLs always has the same
+ * permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
+ * the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
+ * - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
+ * entries contain the identifiers of the owner and owning group.
+ * (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
+ * - ACL entries in the kernel are kept sorted in ascending order
+ * of (e_tag, e_id). Solaris ACLs are unsorted.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/nfsacl.h>
+#include <linux/nfs3.h>
+#include <linux/sort.h>
+
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nfsacl_encode);
+EXPORT_SYMBOL(nfsacl_decode);
+
+struct nfsacl_encode_desc {
+ struct xdr_array2_desc desc;
+ unsigned int count;
+ struct posix_acl *acl;
+ int typeflag;
+ uid_t uid;
+ gid_t gid;
+};
+
+static int
+xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
+{
+ struct nfsacl_encode_desc *nfsacl_desc =
+ (struct nfsacl_encode_desc *) desc;
+ u32 *p = (u32 *) elem;
+
+ if (nfsacl_desc->count < nfsacl_desc->acl->a_count) {
+ struct posix_acl_entry *entry =
+ &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
+
+ *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
+ switch(entry->e_tag) {
+ case ACL_USER_OBJ:
+ *p++ = htonl(nfsacl_desc->uid);
+ break;
+ case ACL_GROUP_OBJ:
+ *p++ = htonl(nfsacl_desc->gid);
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ *p++ = htonl(entry->e_id);
+ break;
+ default: /* Solaris depends on that! */
+ *p++ = 0;
+ break;
+ }
+ *p++ = htonl(entry->e_perm & S_IRWXO);
+ } else {
+ const struct posix_acl_entry *pa, *pe;
+ int group_obj_perm = ACL_READ|ACL_WRITE|ACL_EXECUTE;
+
+ FOREACH_ACL_ENTRY(pa, nfsacl_desc->acl, pe) {
+ if (pa->e_tag == ACL_GROUP_OBJ) {
+ group_obj_perm = pa->e_perm & S_IRWXO;
+ break;
+ }
+ }
+ /* fake up ACL_MASK entry */
+ *p++ = htonl(ACL_MASK | nfsacl_desc->typeflag);
+ *p++ = htonl(0);
+ *p++ = htonl(group_obj_perm);
+ }
+
+ return 0;
+}
+
+unsigned int
+nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
+ struct posix_acl *acl, int encode_entries, int typeflag)
+{
+ int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
+ struct nfsacl_encode_desc nfsacl_desc = {
+ .desc = {
+ .elem_size = 12,
+ .array_len = encode_entries ? entries : 0,
+ .xcode = xdr_nfsace_encode,
+ },
+ .acl = acl,
+ .typeflag = typeflag,
+ .uid = inode->i_uid,
+ .gid = inode->i_gid,
+ };
+ int err;
+
+ if (entries > NFS_ACL_MAX_ENTRIES ||
+ xdr_encode_word(buf, base, entries))
+ return -EINVAL;
+ err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
+ if (!err)
+ err = 8 + nfsacl_desc.desc.elem_size *
+ nfsacl_desc.desc.array_len;
+ return err;
+}
+
+struct nfsacl_decode_desc {
+ struct xdr_array2_desc desc;
+ unsigned int count;
+ struct posix_acl *acl;
+};
+
+static int
+xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
+{
+ struct nfsacl_decode_desc *nfsacl_desc =
+ (struct nfsacl_decode_desc *) desc;
+ u32 *p = (u32 *) elem;
+ struct posix_acl_entry *entry;
+
+ if (!nfsacl_desc->acl) {
+ if (desc->array_len > NFS_ACL_MAX_ENTRIES)
+ return -EINVAL;
+ nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
+ if (!nfsacl_desc->acl)
+ return -ENOMEM;
+ nfsacl_desc->count = 0;
+ }
+
+ entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
+ entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
+ entry->e_id = ntohl(*p++);
+ entry->e_perm = ntohl(*p++);
+
+ switch(entry->e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_USER:
+ case ACL_GROUP_OBJ:
+ case ACL_GROUP:
+ case ACL_OTHER:
+ if (entry->e_perm & ~S_IRWXO)
+ return -EINVAL;
+ break;
+ case ACL_MASK:
+ /* Solaris sometimes sets additonal bits in the mask */
+ entry->e_perm &= S_IRWXO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+cmp_acl_entry(const void *x, const void *y)
+{
+ const struct posix_acl_entry *a = x, *b = y;
+
+ if (a->e_tag != b->e_tag)
+ return a->e_tag - b->e_tag;
+ else if (a->e_id > b->e_id)
+ return 1;
+ else if (a->e_id < b->e_id)
+ return -1;
+ else
+ return 0;
+}
+
+/*
+ * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
+ */
+static int
+posix_acl_from_nfsacl(struct posix_acl *acl)
+{
+ struct posix_acl_entry *pa, *pe,
+ *group_obj = NULL, *mask = NULL;
+
+ if (!acl)
+ return 0;
+
+ sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
+ cmp_acl_entry, NULL);
+
+ /* Clear undefined identifier fields and find the ACL_GROUP_OBJ
+ and ACL_MASK entries. */
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ switch(pa->e_tag) {
+ case ACL_USER_OBJ:
+ pa->e_id = ACL_UNDEFINED_ID;
+ break;
+ case ACL_GROUP_OBJ:
+ pa->e_id = ACL_UNDEFINED_ID;
+ group_obj = pa;
+ break;
+ case ACL_MASK:
+ mask = pa;
+ /* fall through */
+ case ACL_OTHER:
+ pa->e_id = ACL_UNDEFINED_ID;
+ break;
+ }
+ }
+ if (acl->a_count == 4 && group_obj && mask &&
+ mask->e_perm == group_obj->e_perm) {
+ /* remove bogus ACL_MASK entry */
+ memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
+ sizeof(struct posix_acl_entry));
+ acl->a_count = 3;
+ }
+ return 0;
+}
+
+unsigned int
+nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
+ struct posix_acl **pacl)
+{
+ struct nfsacl_decode_desc nfsacl_desc = {
+ .desc = {
+ .elem_size = 12,
+ .xcode = pacl ? xdr_nfsace_decode : NULL,
+ },
+ };
+ u32 entries;
+ int err;
+
+ if (xdr_decode_word(buf, base, &entries) ||
+ entries > NFS_ACL_MAX_ENTRIES)
+ return -EINVAL;
+ err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
+ if (err)
+ return err;
+ if (pacl) {
+ if (entries != nfsacl_desc.desc.array_len ||
+ posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
+ posix_acl_release(nfsacl_desc.acl);
+ return -EINVAL;
+ }
+ *pacl = nfsacl_desc.acl;
+ }
+ if (aclcnt)
+ *aclcnt = entries;
+ return 8 + nfsacl_desc.desc.elem_size *
+ nfsacl_desc.desc.array_len;
+}
diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile
index b8680a247f8..9f043f44c92 100644
--- a/fs/nfsd/Makefile
+++ b/fs/nfsd/Makefile
@@ -6,7 +6,9 @@ obj-$(CONFIG_NFSD) += nfsd.o
nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
+nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
+nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
nfs4acl.o nfs4callback.o
nfsd-objs := $(nfsd-y)
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
new file mode 100644
index 00000000000..7cbf0682b2f
--- /dev/null
+++ b/fs/nfsd/nfs2acl.c
@@ -0,0 +1,336 @@
+/*
+ * linux/fs/nfsd/nfsacl.c
+ *
+ * Process version 2 NFSACL requests.
+ *
+ * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfs.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/nfsd/xdr3.h>
+#include <linux/posix_acl.h>
+#include <linux/nfsacl.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_PROC
+#define RETURN_STATUS(st) { resp->status = (st); return (st); }
+
+/*
+ * NULL call.
+ */
+static int
+nfsacld_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+ return nfs_ok;
+}
+
+/*
+ * Get the Access and/or Default ACL of a file.
+ */
+static int nfsacld_proc_getacl(struct svc_rqst * rqstp,
+ struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
+{
+ svc_fh *fh;
+ struct posix_acl *acl;
+ int nfserr = 0;
+
+ dprintk("nfsd: GETACL(2acl) %s\n", SVCFH_fmt(&argp->fh));
+
+ fh = fh_copy(&resp->fh, &argp->fh);
+ if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP)))
+ RETURN_STATUS(nfserr_inval);
+
+ if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+ RETURN_STATUS(nfserr_inval);
+ resp->mask = argp->mask;
+
+ if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
+ acl = nfsd_get_posix_acl(fh, ACL_TYPE_ACCESS);
+ if (IS_ERR(acl)) {
+ int err = PTR_ERR(acl);
+
+ if (err == -ENODATA || err == -EOPNOTSUPP)
+ acl = NULL;
+ else {
+ nfserr = nfserrno(err);
+ goto fail;
+ }
+ }
+ if (acl == NULL) {
+ /* Solaris returns the inode's minimum ACL. */
+
+ struct inode *inode = fh->fh_dentry->d_inode;
+ acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+ }
+ resp->acl_access = acl;
+ }
+ if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
+ /* Check how Solaris handles requests for the Default ACL
+ of a non-directory! */
+
+ acl = nfsd_get_posix_acl(fh, ACL_TYPE_DEFAULT);
+ if (IS_ERR(acl)) {
+ int err = PTR_ERR(acl);
+
+ if (err == -ENODATA || err == -EOPNOTSUPP)
+ acl = NULL;
+ else {
+ nfserr = nfserrno(err);
+ goto fail;
+ }
+ }
+ resp->acl_default = acl;
+ }
+
+ /* resp->acl_{access,default} are released in nfssvc_release_getacl. */
+ RETURN_STATUS(0);
+
+fail:
+ posix_acl_release(resp->acl_access);
+ posix_acl_release(resp->acl_default);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Set the Access and/or Default ACL of a file.
+ */
+static int nfsacld_proc_setacl(struct svc_rqst * rqstp,
+ struct nfsd3_setaclargs *argp,
+ struct nfsd_attrstat *resp)
+{
+ svc_fh *fh;
+ int nfserr = 0;
+
+ dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh));
+
+ fh = fh_copy(&resp->fh, &argp->fh);
+ nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+
+ if (!nfserr) {
+ nfserr = nfserrno( nfsd_set_posix_acl(
+ fh, ACL_TYPE_ACCESS, argp->acl_access) );
+ }
+ if (!nfserr) {
+ nfserr = nfserrno( nfsd_set_posix_acl(
+ fh, ACL_TYPE_DEFAULT, argp->acl_default) );
+ }
+
+ /* argp->acl_{access,default} may have been allocated in
+ nfssvc_decode_setaclargs. */
+ posix_acl_release(argp->acl_access);
+ posix_acl_release(argp->acl_default);
+ return nfserr;
+}
+
+/*
+ * Check file attributes
+ */
+static int nfsacld_proc_getattr(struct svc_rqst * rqstp,
+ struct nfsd_fhandle *argp, struct nfsd_attrstat *resp)
+{
+ dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh));
+
+ fh_copy(&resp->fh, &argp->fh);
+ return fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+}
+
+/*
+ * Check file access
+ */
+static int nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
+ struct nfsd3_accessres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: ACCESS(2acl) %s 0x%x\n",
+ SVCFH_fmt(&argp->fh),
+ argp->access);
+
+ fh_copy(&resp->fh, &argp->fh);
+ resp->access = argp->access;
+ nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
+ return nfserr;
+}
+
+/*
+ * XDR decode functions
+ */
+static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_getaclargs *argp)
+{
+ if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+ return 0;
+ argp->mask = ntohl(*p); p++;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+
+static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_setaclargs *argp)
+{
+ struct kvec *head = rqstp->rq_arg.head;
+ unsigned int base;
+ int n;
+
+ if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+ return 0;
+ argp->mask = ntohl(*p++);
+ if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
+ !xdr_argsize_check(rqstp, p))
+ return 0;
+
+ base = (char *)p - (char *)head->iov_base;
+ n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
+ (argp->mask & NFS_ACL) ?
+ &argp->acl_access : NULL);
+ if (n > 0)
+ n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
+ (argp->mask & NFS_DFACL) ?
+ &argp->acl_default : NULL);
+ return (n > 0);
+}
+
+static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_fhandle *argp)
+{
+ if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+ return 0;
+ return xdr_argsize_check(rqstp, p);
+}
+
+static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_accessargs *argp)
+{
+ if (!(p = nfs2svc_decode_fh(p, &argp->fh)))
+ return 0;
+ argp->access = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+/*
+ * XDR encode functions
+ */
+
+/* GETACL */
+static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_getaclres *resp)
+{
+ struct dentry *dentry = resp->fh.fh_dentry;
+ struct inode *inode = dentry->d_inode;
+ int w = nfsacl_size(
+ (resp->mask & NFS_ACL) ? resp->acl_access : NULL,
+ (resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
+ struct kvec *head = rqstp->rq_res.head;
+ unsigned int base;
+ int n;
+
+ if (dentry == NULL || dentry->d_inode == NULL)
+ return 0;
+ inode = dentry->d_inode;
+
+ p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
+ *p++ = htonl(resp->mask);
+ if (!xdr_ressize_check(rqstp, p))
+ return 0;
+ base = (char *)p - (char *)head->iov_base;
+
+ rqstp->rq_res.page_len = w;
+ while (w > 0) {
+ if (!svc_take_res_page(rqstp))
+ return 0;
+ w -= PAGE_SIZE;
+ }
+
+ n = nfsacl_encode(&rqstp->rq_res, base, inode,
+ resp->acl_access,
+ resp->mask & NFS_ACL, 0);
+ if (n > 0)
+ n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
+ resp->acl_default,
+ resp->mask & NFS_DFACL,
+ NFS_ACL_DEFAULT);
+ if (n <= 0)
+ return 0;
+ return 1;
+}
+
+static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_attrstat *resp)
+{
+ p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* ACCESS */
+static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_accessres *resp)
+{
+ p = nfs2svc_encode_fattr(rqstp, p, &resp->fh);
+ *p++ = htonl(resp->access);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * XDR release functions
+ */
+static int nfsaclsvc_release_getacl(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_getaclres *resp)
+{
+ fh_put(&resp->fh);
+ posix_acl_release(resp->acl_access);
+ posix_acl_release(resp->acl_default);
+ return 1;
+}
+
+static int nfsaclsvc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_fhandle *resp)
+{
+ fh_put(&resp->fh);
+ return 1;
+}
+
+#define nfsaclsvc_decode_voidargs NULL
+#define nfsaclsvc_encode_voidres NULL
+#define nfsaclsvc_release_void NULL
+#define nfsd3_fhandleargs nfsd_fhandle
+#define nfsd3_attrstatres nfsd_attrstat
+#define nfsd3_voidres nfsd3_voidargs
+struct nfsd3_voidargs { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache, respsize) \
+ { (svc_procfunc) nfsacld_proc_##name, \
+ (kxdrproc_t) nfsaclsvc_decode_##argt##args, \
+ (kxdrproc_t) nfsaclsvc_encode_##rest##res, \
+ (kxdrproc_t) nfsaclsvc_release_##relt, \
+ sizeof(struct nfsd3_##argt##args), \
+ sizeof(struct nfsd3_##rest##res), \
+ 0, \
+ cache, \
+ respsize, \
+ }
+
+#define ST 1 /* status*/
+#define AT 21 /* attributes */
+#define pAT (1+AT) /* post attributes - conditional */
+#define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */
+
+static struct svc_procedure nfsd_acl_procedures2[] = {
+ PROC(null, void, void, void, RC_NOCACHE, ST),
+ PROC(getacl, getacl, getacl, getacl, RC_NOCACHE, ST+1+2*(1+ACL)),
+ PROC(setacl, setacl, attrstat, fhandle, RC_NOCACHE, ST+AT),
+ PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT),
+ PROC(access, access, access, fhandle, RC_NOCACHE, ST+AT+1),
+};
+
+struct svc_version nfsd_acl_version2 = {
+ .vs_vers = 2,
+ .vs_nproc = 5,
+ .vs_proc = nfsd_acl_procedures2,
+ .vs_dispatch = nfsd_dispatch,
+ .vs_xdrsize = NFS3_SVC_XDRSIZE,
+};
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
new file mode 100644
index 00000000000..64ba40572fe
--- /dev/null
+++ b/fs/nfsd/nfs3acl.c
@@ -0,0 +1,267 @@
+/*
+ * linux/fs/nfsd/nfs3acl.c
+ *
+ * Process version 3 NFSACL requests.
+ *
+ * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfs3.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr3.h>
+#include <linux/posix_acl.h>
+#include <linux/nfsacl.h>
+
+#define RETURN_STATUS(st) { resp->status = (st); return (st); }
+
+/*
+ * NULL call.
+ */
+static int
+nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+ return nfs_ok;
+}
+
+/*
+ * Get the Access and/or Default ACL of a file.
+ */
+static int nfsd3_proc_getacl(struct svc_rqst * rqstp,
+ struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
+{
+ svc_fh *fh;
+ struct posix_acl *acl;
+ int nfserr = 0;
+
+ fh = fh_copy(&resp->fh, &argp->fh);
+ if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP)))
+ RETURN_STATUS(nfserr_inval);
+
+ if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+ RETURN_STATUS(nfserr_inval);
+ resp->mask = argp->mask;
+
+ if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
+ acl = nfsd_get_posix_acl(fh, ACL_TYPE_ACCESS);
+ if (IS_ERR(acl)) {
+ int err = PTR_ERR(acl);
+
+ if (err == -ENODATA || err == -EOPNOTSUPP)
+ acl = NULL;
+ else {
+ nfserr = nfserrno(err);
+ goto fail;
+ }
+ }
+ if (acl == NULL) {
+ /* Solaris returns the inode's minimum ACL. */
+
+ struct inode *inode = fh->fh_dentry->d_inode;
+ acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+ }
+ resp->acl_access = acl;
+ }
+ if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
+ /* Check how Solaris handles requests for the Default ACL
+ of a non-directory! */
+
+ acl = nfsd_get_posix_acl(fh, ACL_TYPE_DEFAULT);
+ if (IS_ERR(acl)) {
+ int err = PTR_ERR(acl);
+
+ if (err == -ENODATA || err == -EOPNOTSUPP)
+ acl = NULL;
+ else {
+ nfserr = nfserrno(err);
+ goto fail;
+ }
+ }
+ resp->acl_default = acl;
+ }
+
+ /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */
+ RETURN_STATUS(0);
+
+fail:
+ posix_acl_release(resp->acl_access);
+ posix_acl_release(resp->acl_default);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Set the Access and/or Default ACL of a file.
+ */
+static int nfsd3_proc_setacl(struct svc_rqst * rqstp,
+ struct nfsd3_setaclargs *argp,
+ struct nfsd3_attrstat *resp)
+{
+ svc_fh *fh;
+ int nfserr = 0;
+
+ fh = fh_copy(&resp->fh, &argp->fh);
+ nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+
+ if (!nfserr) {
+ nfserr = nfserrno( nfsd_set_posix_acl(
+ fh, ACL_TYPE_ACCESS, argp->acl_access) );
+ }
+ if (!nfserr) {
+ nfserr = nfserrno( nfsd_set_posix_acl(
+ fh, ACL_TYPE_DEFAULT, argp->acl_default) );
+ }
+
+ /* argp->acl_{access,default} may have been allocated in
+ nfs3svc_decode_setaclargs. */
+ posix_acl_release(argp->acl_access);
+ posix_acl_release(argp->acl_default);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * XDR decode functions
+ */
+static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_getaclargs *args)
+{
+ if (!(p = nfs3svc_decode_fh(p, &args->fh)))
+ return 0;
+ args->mask = ntohl(*p); p++;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+
+static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_setaclargs *args)
+{
+ struct kvec *head = rqstp->rq_arg.head;
+ unsigned int base;
+ int n;
+
+ if (!(p = nfs3svc_decode_fh(p, &args->fh)))
+ return 0;
+ args->mask = ntohl(*p++);
+ if (args->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
+ !xdr_argsize_check(rqstp, p))
+ return 0;
+
+ base = (char *)p - (char *)head->iov_base;
+ n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
+ (args->mask & NFS_ACL) ?
+ &args->acl_access : NULL);
+ if (n > 0)
+ n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
+ (args->mask & NFS_DFACL) ?
+ &args->acl_default : NULL);
+ return (n > 0);
+}
+
+/*
+ * XDR encode functions
+ */
+
+/* GETACL */
+static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_getaclres *resp)
+{
+ struct dentry *dentry = resp->fh.fh_dentry;
+
+ p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
+ if (resp->status == 0 && dentry && dentry->d_inode) {
+ struct inode *inode = dentry->d_inode;
+ int w = nfsacl_size(
+ (resp->mask & NFS_ACL) ? resp->acl_access : NULL,
+ (resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
+ struct kvec *head = rqstp->rq_res.head;
+ unsigned int base;
+ int n;
+
+ *p++ = htonl(resp->mask);
+ if (!xdr_ressize_check(rqstp, p))
+ return 0;
+ base = (char *)p - (char *)head->iov_base;
+
+ rqstp->rq_res.page_len = w;
+ while (w > 0) {
+ if (!svc_take_res_page(rqstp))
+ return 0;
+ w -= PAGE_SIZE;
+ }
+
+ n = nfsacl_encode(&rqstp->rq_res, base, inode,
+ resp->acl_access,
+ resp->mask & NFS_ACL, 0);
+ if (n > 0)
+ n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
+ resp->acl_default,
+ resp->mask & NFS_DFACL,
+ NFS_ACL_DEFAULT);
+ if (n <= 0)
+ return 0;
+ } else
+ if (!xdr_ressize_check(rqstp, p))
+ return 0;
+
+ return 1;
+}
+
+/* SETACL */
+static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_attrstat *resp)
+{
+ p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
+
+ return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * XDR release functions
+ */
+static int nfs3svc_release_getacl(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_getaclres *resp)
+{
+ fh_put(&resp->fh);
+ posix_acl_release(resp->acl_access);
+ posix_acl_release(resp->acl_default);
+ return 1;
+}
+
+#define nfs3svc_decode_voidargs NULL
+#define nfs3svc_release_void NULL
+#define nfsd3_setaclres nfsd3_attrstat
+#define nfsd3_voidres nfsd3_voidargs
+struct nfsd3_voidargs { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache, respsize) \
+ { (svc_procfunc) nfsd3_proc_##name, \
+ (kxdrproc_t) nfs3svc_decode_##argt##args, \
+ (kxdrproc_t) nfs3svc_encode_##rest##res, \
+ (kxdrproc_t) nfs3svc_release_##relt, \
+ sizeof(struct nfsd3_##argt##args), \
+ sizeof(struct nfsd3_##rest##res), \
+ 0, \
+ cache, \
+ respsize, \
+ }
+
+#define ST 1 /* status*/
+#define AT 21 /* attributes */
+#define pAT (1+AT) /* post attributes - conditional */
+#define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */
+
+static struct svc_procedure nfsd_acl_procedures3[] = {
+ PROC(null, void, void, void, RC_NOCACHE, ST),
+ PROC(getacl, getacl, getacl, getacl, RC_NOCACHE, ST+1+2*(1+ACL)),
+ PROC(setacl, setacl, setacl, fhandle, RC_NOCACHE, ST+pAT),
+};
+
+struct svc_version nfsd_acl_version3 = {
+ .vs_vers = 3,
+ .vs_nproc = 3,
+ .vs_proc = nfsd_acl_procedures3,
+ .vs_dispatch = nfsd_dispatch,
+ .vs_xdrsize = NFS3_SVC_XDRSIZE,
+};
+
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 11f806835c5..e0e134d6bab 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -71,6 +71,12 @@ decode_fh(u32 *p, struct svc_fh *fhp)
return p + XDR_QUADLEN(size);
}
+/* Helper function for NFSv3 ACL code */
+u32 *nfs3svc_decode_fh(u32 *p, struct svc_fh *fhp)
+{
+ return decode_fh(p, fhp);
+}
+
static inline u32 *
encode_fh(u32 *p, struct svc_fh *fhp)
{
@@ -233,6 +239,13 @@ encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
return p;
}
+/* Helper for NFSv3 ACLs */
+u32 *
+nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ return encode_post_op_attr(rqstp, p, fhp);
+}
+
/*
* Enocde weak cache consistency data
*/
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 1a55dfcb74b..634465e9cfc 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -430,7 +430,7 @@ nfsd4_probe_callback(struct nfs4_client *clp)
clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX);
if (IS_ERR(clnt)) {
dprintk("NFSD: couldn't create callback client\n");
- goto out_xprt;
+ goto out_err;
}
clnt->cl_intr = 0;
clnt->cl_softrtry = 1;
@@ -465,8 +465,6 @@ out_rpciod:
out_clnt:
rpc_shutdown_client(clnt);
goto out_err;
-out_xprt:
- xprt_destroy(xprt);
out_err:
dprintk("NFSD: warning: no callback path to client %.*s\n",
(int)clp->cl_name.len, clp->cl_name.data);
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 757f9d20803..0aa1b9603d7 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -591,6 +591,7 @@ nfserrno (int errno)
{ nfserr_dropit, -ENOMEM },
{ nfserr_badname, -ESRCH },
{ nfserr_io, -ETXTBSY },
+ { nfserr_notsupp, -EOPNOTSUPP },
{ -1, -EIO }
};
int i;
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 02ded7cfbdc..904df604e86 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -31,6 +31,7 @@
#include <linux/nfsd/stats.h>
#include <linux/nfsd/cache.h>
#include <linux/lockd/bind.h>
+#include <linux/nfsacl.h>
#define NFSDDBG_FACILITY NFSDDBG_SVC
@@ -362,6 +363,32 @@ nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp)
return 1;
}
+#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
+static struct svc_stat nfsd_acl_svcstats;
+static struct svc_version * nfsd_acl_version[] = {
+ [2] = &nfsd_acl_version2,
+ [3] = &nfsd_acl_version3,
+};
+
+#define NFSD_ACL_NRVERS (sizeof(nfsd_acl_version)/sizeof(nfsd_acl_version[0]))
+static struct svc_program nfsd_acl_program = {
+ .pg_prog = NFS_ACL_PROGRAM,
+ .pg_nvers = NFSD_ACL_NRVERS,
+ .pg_vers = nfsd_acl_version,
+ .pg_name = "nfsd",
+ .pg_class = "nfsd",
+ .pg_stats = &nfsd_acl_svcstats,
+};
+
+static struct svc_stat nfsd_acl_svcstats = {
+ .program = &nfsd_acl_program,
+};
+
+#define nfsd_acl_program_p &nfsd_acl_program
+#else
+#define nfsd_acl_program_p NULL
+#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
+
extern struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
static struct svc_version * nfsd_version[] = {
@@ -376,6 +403,7 @@ static struct svc_version * nfsd_version[] = {
#define NFSD_NRVERS (sizeof(nfsd_version)/sizeof(nfsd_version[0]))
struct svc_program nfsd_program = {
+ .pg_next = nfsd_acl_program_p,
.pg_prog = NFS_PROGRAM, /* program number */
.pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */
.pg_vers = nfsd_version, /* version table */
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index 948b08287c9..b45999ff33e 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -49,6 +49,12 @@ decode_fh(u32 *p, struct svc_fh *fhp)
return p + (NFS_FHSIZE >> 2);
}
+/* Helper function for NFSv2 ACL code */
+u32 *nfs2svc_decode_fh(u32 *p, struct svc_fh *fhp)
+{
+ return decode_fh(p, fhp);
+}
+
static inline u32 *
encode_fh(u32 *p, struct svc_fh *fhp)
{
@@ -190,6 +196,11 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
return p;
}
+/* Helper function for NFSv2 ACL code */
+u32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ return encode_fattr(rqstp, p, fhp);
+}
/*
* XDR decode functions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index e3e9d217236..ae3940dc85c 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -46,8 +46,9 @@
#include <linux/nfsd/nfsfh.h>
#include <linux/quotaops.h>
#include <linux/dnotify.h>
-#ifdef CONFIG_NFSD_V4
+#include <linux/xattr_acl.h>
#include <linux/posix_acl.h>
+#ifdef CONFIG_NFSD_V4
#include <linux/posix_acl_xattr.h>
#include <linux/xattr_acl.h>
#include <linux/xattr.h>
@@ -1857,3 +1858,107 @@ nfsd_racache_init(int cache_size)
nfsdstats.ra_size = cache_size;
return 0;
}
+
+#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
+struct posix_acl *
+nfsd_get_posix_acl(struct svc_fh *fhp, int type)
+{
+ struct inode *inode = fhp->fh_dentry->d_inode;
+ char *name;
+ void *value = NULL;
+ ssize_t size;
+ struct posix_acl *acl;
+
+ if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->getxattr)
+ return ERR_PTR(-EOPNOTSUPP);
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ name = XATTR_NAME_ACL_ACCESS;
+ break;
+ case ACL_TYPE_DEFAULT:
+ name = XATTR_NAME_ACL_DEFAULT;
+ break;
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ size = inode->i_op->getxattr(fhp->fh_dentry, name, NULL, 0);
+
+ if (size < 0) {
+ acl = ERR_PTR(size);
+ goto getout;
+ } else if (size > 0) {
+ value = kmalloc(size, GFP_KERNEL);
+ if (!value) {
+ acl = ERR_PTR(-ENOMEM);
+ goto getout;
+ }
+ size = inode->i_op->getxattr(fhp->fh_dentry, name, value, size);
+ if (size < 0) {
+ acl = ERR_PTR(size);
+ goto getout;
+ }
+ }
+ acl = posix_acl_from_xattr(value, size);
+
+getout:
+ kfree(value);
+ return acl;
+}
+
+int
+nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
+{
+ struct inode *inode = fhp->fh_dentry->d_inode;
+ char *name;
+ void *value = NULL;
+ size_t size;
+ int error;
+
+ if (!IS_POSIXACL(inode) || !inode->i_op ||
+ !inode->i_op->setxattr || !inode->i_op->removexattr)
+ return -EOPNOTSUPP;
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ name = XATTR_NAME_ACL_ACCESS;
+ break;
+ case ACL_TYPE_DEFAULT:
+ name = XATTR_NAME_ACL_DEFAULT;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (acl && acl->a_count) {
+ size = xattr_acl_size(acl->a_count);
+ value = kmalloc(size, GFP_KERNEL);
+ if (!value)
+ return -ENOMEM;
+ size = posix_acl_to_xattr(acl, value, size);
+ if (size < 0) {
+ error = size;
+ goto getout;
+ }
+ } else
+ size = 0;
+
+ if (!fhp->fh_locked)
+ fh_lock(fhp); /* unlocking is done automatically */
+ if (size)
+ error = inode->i_op->setxattr(fhp->fh_dentry, name,
+ value, size, 0);
+ else {
+ if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
+ error = 0;
+ else {
+ error = inode->i_op->removexattr(fhp->fh_dentry, name);
+ if (error == -ENODATA)
+ error = 0;
+ }
+ }
+
+getout:
+ kfree(value);
+ return error;
+}
+#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 9b8b696d4f1..e5a8db00df2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -674,6 +674,7 @@ struct file_lock {
struct lock_manager_operations *fl_lmops; /* Callbacks for lockmanagers */
union {
struct nfs_lock_info nfs_fl;
+ struct nfs4_lock_info nfs4_fl;
} fl_u;
};
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index 0d9d2257821..16d4e5a08e1 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -72,6 +72,8 @@ struct nlm_lockowner {
uint32_t pid;
};
+struct nlm_wait;
+
/*
* Memory chunk for NLM client RPC request.
*/
@@ -81,6 +83,7 @@ struct nlm_rqst {
struct nlm_host * a_host; /* host handle */
struct nlm_args a_args; /* arguments */
struct nlm_res a_res; /* result */
+ struct nlm_wait * a_block;
char a_owner[NLMCLNT_OHSIZE];
};
@@ -142,7 +145,9 @@ extern unsigned long nlmsvc_timeout;
* Lockd client functions
*/
struct nlm_rqst * nlmclnt_alloc_call(void);
-int nlmclnt_block(struct nlm_host *, struct file_lock *, u32 *);
+int nlmclnt_prepare_block(struct nlm_rqst *req, struct nlm_host *host, struct file_lock *fl);
+void nlmclnt_finish_block(struct nlm_rqst *req);
+long nlmclnt_block(struct nlm_rqst *req, long timeout);
int nlmclnt_cancel(struct nlm_host *, struct file_lock *);
u32 nlmclnt_grant(struct nlm_lock *);
void nlmclnt_recovery(struct nlm_host *, u32);
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 5ca8a8d8ccd..5bb5b2fd7ba 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -382,6 +382,8 @@ enum {
NFSPROC4_CLNT_READDIR,
NFSPROC4_CLNT_SERVER_CAPS,
NFSPROC4_CLNT_DELEGRETURN,
+ NFSPROC4_CLNT_GETACL,
+ NFSPROC4_CLNT_SETACL,
};
#endif
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index dbac7f363e5..8ea249110fb 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -15,7 +15,6 @@
#include <linux/pagemap.h>
#include <linux/rwsem.h>
#include <linux/wait.h>
-#include <linux/uio.h>
#include <linux/nfs_fs_sb.h>
@@ -29,7 +28,6 @@
#include <linux/nfs4.h>
#include <linux/nfs_xdr.h>
#include <linux/rwsem.h>
-#include <linux/workqueue.h>
#include <linux/mempool.h>
/*
@@ -44,13 +42,6 @@
#define NFS_DEF_FILE_IO_BUFFER_SIZE 4096
/*
- * The upper limit on timeouts for the exponential backoff algorithm.
- */
-#define NFS_WRITEBACK_DELAY (5*HZ)
-#define NFS_WRITEBACK_LOCKDELAY (60*HZ)
-#define NFS_COMMIT_DELAY (5*HZ)
-
-/*
* superblock magic number for NFS
*/
#define NFS_SUPER_MAGIC 0x6969
@@ -60,9 +51,6 @@
*/
#define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
-#define NFS_RW_SYNC 0x0001 /* O_SYNC handling */
-#define NFS_RW_SWAP 0x0002 /* This is a swap request */
-
/*
* When flushing a cluster of dirty pages, there can be different
* strategies:
@@ -96,7 +84,8 @@ struct nfs_open_context {
int error;
struct list_head list;
- wait_queue_head_t waitq;
+
+ __u64 dir_cookie;
};
/*
@@ -104,6 +93,8 @@ struct nfs_open_context {
*/
struct nfs_delegation;
+struct posix_acl;
+
/*
* nfs fs inode data in memory
*/
@@ -140,7 +131,6 @@ struct nfs_inode {
*
* mtime != read_cache_mtime
*/
- unsigned long readdir_timestamp;
unsigned long read_cache_jiffies;
unsigned long attrtimeo;
unsigned long attrtimeo_timestamp;
@@ -158,6 +148,10 @@ struct nfs_inode {
atomic_t data_updates;
struct nfs_access_entry cache_access;
+#ifdef CONFIG_NFS_V3_ACL
+ struct posix_acl *acl_access;
+ struct posix_acl *acl_default;
+#endif
/*
* This is the cookie verifier used for NFSv3 readdir
@@ -183,13 +177,13 @@ struct nfs_inode {
wait_queue_head_t nfs_i_wait;
#ifdef CONFIG_NFS_V4
+ struct nfs4_cached_acl *nfs4_acl;
/* NFSv4 state */
struct list_head open_states;
struct nfs_delegation *delegation;
int delegation_state;
struct rw_semaphore rwsem;
#endif /* CONFIG_NFS_V4*/
-
struct inode vfs_inode;
};
@@ -203,6 +197,8 @@ struct nfs_inode {
#define NFS_INO_INVALID_DATA 0x0010 /* cached data is invalid */
#define NFS_INO_INVALID_ATIME 0x0020 /* cached atime is invalid */
#define NFS_INO_INVALID_ACCESS 0x0040 /* cached access cred invalid */
+#define NFS_INO_INVALID_ACL 0x0080 /* cached acls are invalid */
+#define NFS_INO_REVAL_PAGECACHE 0x1000 /* must revalidate pagecache */
static inline struct nfs_inode *NFS_I(struct inode *inode)
{
@@ -294,12 +290,12 @@ extern int nfs_release(struct inode *, struct file *);
extern int nfs_attribute_timeout(struct inode *inode);
extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
+extern void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
extern int nfs_setattr(struct dentry *, struct iattr *);
extern void nfs_begin_attr_update(struct inode *);
extern void nfs_end_attr_update(struct inode *);
extern void nfs_begin_data_update(struct inode *);
extern void nfs_end_data_update(struct inode *);
-extern void nfs_end_data_update_defer(struct inode *);
extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred);
extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
extern void put_nfs_open_context(struct nfs_open_context *ctx);
@@ -314,6 +310,9 @@ extern u32 root_nfs_parse_addr(char *name); /*__init*/
* linux/fs/nfs/file.c
*/
extern struct inode_operations nfs_file_inode_operations;
+#ifdef CONFIG_NFS_V3
+extern struct inode_operations nfs3_file_inode_operations;
+#endif /* CONFIG_NFS_V3 */
extern struct file_operations nfs_file_operations;
extern struct address_space_operations nfs_file_aops;
@@ -329,6 +328,22 @@ static inline struct rpc_cred *nfs_file_cred(struct file *file)
}
/*
+ * linux/fs/nfs/xattr.c
+ */
+#ifdef CONFIG_NFS_V3_ACL
+extern ssize_t nfs3_listxattr(struct dentry *, char *, size_t);
+extern ssize_t nfs3_getxattr(struct dentry *, const char *, void *, size_t);
+extern int nfs3_setxattr(struct dentry *, const char *,
+ const void *, size_t, int);
+extern int nfs3_removexattr (struct dentry *, const char *name);
+#else
+# define nfs3_listxattr NULL
+# define nfs3_getxattr NULL
+# define nfs3_setxattr NULL
+# define nfs3_removexattr NULL
+#endif
+
+/*
* linux/fs/nfs/direct.c
*/
extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t,
@@ -342,6 +357,9 @@ extern ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf,
* linux/fs/nfs/dir.c
*/
extern struct inode_operations nfs_dir_inode_operations;
+#ifdef CONFIG_NFS_V3
+extern struct inode_operations nfs3_dir_inode_operations;
+#endif /* CONFIG_NFS_V3 */
extern struct file_operations nfs_dir_operations;
extern struct dentry_operations nfs_dentry_operations;
@@ -377,10 +395,10 @@ extern void nfs_commit_done(struct rpc_task *);
*/
extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int);
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
-extern int nfs_commit_inode(struct inode *, unsigned long, unsigned int, int);
+extern int nfs_commit_inode(struct inode *, int);
#else
static inline int
-nfs_commit_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how)
+nfs_commit_inode(struct inode *inode, int how)
{
return 0;
}
@@ -434,11 +452,6 @@ static inline void nfs_writedata_free(struct nfs_write_data *p)
mempool_free(p, nfs_wdata_mempool);
}
-/* Hack for future NFS swap support */
-#ifndef IS_SWAPFILE
-# define IS_SWAPFILE(inode) (0)
-#endif
-
/*
* linux/fs/nfs/read.c
*/
@@ -468,6 +481,29 @@ static inline void nfs_readdata_free(struct nfs_read_data *p)
extern void nfs_readdata_release(struct rpc_task *task);
/*
+ * linux/fs/nfs3proc.c
+ */
+#ifdef CONFIG_NFS_V3_ACL
+extern struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type);
+extern int nfs3_proc_setacl(struct inode *inode, int type,
+ struct posix_acl *acl);
+extern int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
+ mode_t mode);
+extern void nfs3_forget_cached_acls(struct inode *inode);
+#else
+static inline int nfs3_proc_set_default_acl(struct inode *dir,
+ struct inode *inode,
+ mode_t mode)
+{
+ return 0;
+}
+
+static inline void nfs3_forget_cached_acls(struct inode *inode)
+{
+}
+#endif /* CONFIG_NFS_V3_ACL */
+
+/*
* linux/fs/mount_clnt.c
* (Used only by nfsroot module)
*/
@@ -515,230 +551,6 @@ extern void * nfs_root_data(void);
#define NFS_JUKEBOX_RETRY_TIME (5 * HZ)
-#ifdef CONFIG_NFS_V4
-
-struct idmap;
-
-/*
- * In a seqid-mutating op, this macro controls which error return
- * values trigger incrementation of the seqid.
- *
- * from rfc 3010:
- * The client MUST monotonically increment the sequence number for the
- * CLOSE, LOCK, LOCKU, OPEN, OPEN_CONFIRM, and OPEN_DOWNGRADE
- * operations. This is true even in the event that the previous
- * operation that used the sequence number received an error. The only
- * exception to this rule is if the previous operation received one of
- * the following errors: NFSERR_STALE_CLIENTID, NFSERR_STALE_STATEID,
- * NFSERR_BAD_STATEID, NFSERR_BAD_SEQID, NFSERR_BADXDR,
- * NFSERR_RESOURCE, NFSERR_NOFILEHANDLE.
- *
- */
-#define seqid_mutating_err(err) \
-(((err) != NFSERR_STALE_CLIENTID) && \
- ((err) != NFSERR_STALE_STATEID) && \
- ((err) != NFSERR_BAD_STATEID) && \
- ((err) != NFSERR_BAD_SEQID) && \
- ((err) != NFSERR_BAD_XDR) && \
- ((err) != NFSERR_RESOURCE) && \
- ((err) != NFSERR_NOFILEHANDLE))
-
-enum nfs4_client_state {
- NFS4CLNT_OK = 0,
-};
-
-/*
- * The nfs4_client identifies our client state to the server.
- */
-struct nfs4_client {
- struct list_head cl_servers; /* Global list of servers */
- struct in_addr cl_addr; /* Server identifier */
- u64 cl_clientid; /* constant */
- nfs4_verifier cl_confirm;
- unsigned long cl_state;
-
- u32 cl_lockowner_id;
-
- /*
- * The following rwsem ensures exclusive access to the server
- * while we recover the state following a lease expiration.
- */
- struct rw_semaphore cl_sem;
-
- struct list_head cl_delegations;
- struct list_head cl_state_owners;
- struct list_head cl_unused;
- int cl_nunused;
- spinlock_t cl_lock;
- atomic_t cl_count;
-
- struct rpc_clnt * cl_rpcclient;
- struct rpc_cred * cl_cred;
-
- struct list_head cl_superblocks; /* List of nfs_server structs */
-
- unsigned long cl_lease_time;
- unsigned long cl_last_renewal;
- struct work_struct cl_renewd;
- struct work_struct cl_recoverd;
-
- wait_queue_head_t cl_waitq;
- struct rpc_wait_queue cl_rpcwaitq;
-
- /* used for the setclientid verifier */
- struct timespec cl_boot_time;
-
- /* idmapper */
- struct idmap * cl_idmap;
-
- /* Our own IP address, as a null-terminated string.
- * This is used to generate the clientid, and the callback address.
- */
- char cl_ipaddr[16];
- unsigned char cl_id_uniquifier;
-};
-
-/*
- * NFS4 state_owners and lock_owners are simply labels for ordered
- * sequences of RPC calls. Their sole purpose is to provide once-only
- * semantics by allowing the server to identify replayed requests.
- *
- * The ->so_sema is held during all state_owner seqid-mutating operations:
- * OPEN, OPEN_DOWNGRADE, and CLOSE. Its purpose is to properly serialize
- * so_seqid.
- */
-struct nfs4_state_owner {
- struct list_head so_list; /* per-clientid list of state_owners */
- struct nfs4_client *so_client;
- u32 so_id; /* 32-bit identifier, unique */
- struct semaphore so_sema;
- u32 so_seqid; /* protected by so_sema */
- atomic_t so_count;
-
- struct rpc_cred *so_cred; /* Associated cred */
- struct list_head so_states;
- struct list_head so_delegations;
-};
-
-/*
- * struct nfs4_state maintains the client-side state for a given
- * (state_owner,inode) tuple (OPEN) or state_owner (LOCK).
- *
- * OPEN:
- * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server,
- * we need to know how many files are open for reading or writing on a
- * given inode. This information too is stored here.
- *
- * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN)
- */
-
-struct nfs4_lock_state {
- struct list_head ls_locks; /* Other lock stateids */
- fl_owner_t ls_owner; /* POSIX lock owner */
-#define NFS_LOCK_INITIALIZED 1
- int ls_flags;
- u32 ls_seqid;
- u32 ls_id;
- nfs4_stateid ls_stateid;
- atomic_t ls_count;
-};
-
-/* bits for nfs4_state->flags */
-enum {
- LK_STATE_IN_USE,
- NFS_DELEGATED_STATE,
-};
-
-struct nfs4_state {
- struct list_head open_states; /* List of states for the same state_owner */
- struct list_head inode_states; /* List of states for the same inode */
- struct list_head lock_states; /* List of subservient lock stateids */
-
- struct nfs4_state_owner *owner; /* Pointer to the open owner */
- struct inode *inode; /* Pointer to the inode */
-
- unsigned long flags; /* Do we hold any locks? */
- struct semaphore lock_sema; /* Serializes file locking operations */
- rwlock_t state_lock; /* Protects the lock_states list */
-
- nfs4_stateid stateid;
-
- unsigned int nreaders;
- unsigned int nwriters;
- int state; /* State on the server (R,W, or RW) */
- atomic_t count;
-};
-
-
-struct nfs4_exception {
- long timeout;
- int retry;
-};
-
-struct nfs4_state_recovery_ops {
- int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
- int (*recover_lock)(struct nfs4_state *, struct file_lock *);
-};
-
-extern struct dentry_operations nfs4_dentry_operations;
-extern struct inode_operations nfs4_dir_inode_operations;
-
-/* nfs4proc.c */
-extern int nfs4_map_errors(int err);
-extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short);
-extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
-extern int nfs4_proc_async_renew(struct nfs4_client *);
-extern int nfs4_proc_renew(struct nfs4_client *);
-extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode);
-extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
-extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
-
-extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
-extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops;
-
-/* nfs4renewd.c */
-extern void nfs4_schedule_state_renewal(struct nfs4_client *);
-extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
-extern void nfs4_kill_renewd(struct nfs4_client *);
-
-/* nfs4state.c */
-extern void init_nfsv4_state(struct nfs_server *);
-extern void destroy_nfsv4_state(struct nfs_server *);
-extern struct nfs4_client *nfs4_get_client(struct in_addr *);
-extern void nfs4_put_client(struct nfs4_client *clp);
-extern int nfs4_init_client(struct nfs4_client *clp);
-extern struct nfs4_client *nfs4_find_client(struct in_addr *);
-extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *);
-
-extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
-extern void nfs4_put_state_owner(struct nfs4_state_owner *);
-extern void nfs4_drop_state_owner(struct nfs4_state_owner *);
-extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
-extern void nfs4_put_open_state(struct nfs4_state *);
-extern void nfs4_close_state(struct nfs4_state *, mode_t);
-extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode);
-extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp);
-extern void nfs4_schedule_state_recovery(struct nfs4_client *);
-extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t);
-extern struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t);
-extern void nfs4_put_lock_state(struct nfs4_lock_state *state);
-extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls);
-extern void nfs4_notify_setlk(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *);
-extern void nfs4_notify_unlck(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *);
-extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
-
-
-
-struct nfs4_mount_data;
-#else
-#define init_nfsv4_state(server) do { } while (0)
-#define destroy_nfsv4_state(server) do { } while (0)
-#define nfs4_put_state_owner(inode, owner) do { } while (0)
-#define nfs4_put_open_state(state) do { } while (0)
-#define nfs4_close_state(a, b) do { } while (0)
-#define nfs4_renewd_prepare_shutdown(server) do { } while (0)
-#endif
-
#endif /* __KERNEL__ */
/*
diff --git a/include/linux/nfs_fs_i.h b/include/linux/nfs_fs_i.h
index e9a749588a7..e2c18dabff8 100644
--- a/include/linux/nfs_fs_i.h
+++ b/include/linux/nfs_fs_i.h
@@ -16,6 +16,11 @@ struct nfs_lock_info {
struct nlm_lockowner *owner;
};
+struct nfs4_lock_state;
+struct nfs4_lock_info {
+ struct nfs4_lock_state *owner;
+};
+
/*
* Lock flag values
*/
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index fc51645d61e..3d3a305488c 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -10,6 +10,7 @@
struct nfs_server {
struct rpc_clnt * client; /* RPC client handle */
struct rpc_clnt * client_sys; /* 2nd handle for FSINFO */
+ struct rpc_clnt * client_acl; /* ACL RPC client handle */
struct nfs_rpc_ops * rpc_ops; /* NFS protocol vector */
struct backing_dev_info backing_dev_info;
int flags; /* various flags */
diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h
index 0071428231f..659c7543845 100644
--- a/include/linux/nfs_mount.h
+++ b/include/linux/nfs_mount.h
@@ -58,6 +58,7 @@ struct nfs_mount_data {
#define NFS_MOUNT_KERBEROS 0x0100 /* 3 */
#define NFS_MOUNT_NONLM 0x0200 /* 3 */
#define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */
+#define NFS_MOUNT_NOACL 0x0800 /* 4 */
#define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */
#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */
#define NFS_MOUNT_FLAGMASK 0xFFFF
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 39e4895bcdb..da2e077b65e 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -20,12 +20,19 @@
#include <asm/atomic.h>
/*
+ * Valid flags for the radix tree
+ */
+#define NFS_PAGE_TAG_DIRTY 0
+#define NFS_PAGE_TAG_WRITEBACK 1
+
+/*
* Valid flags for a dirty buffer
*/
#define PG_BUSY 0
#define PG_NEED_COMMIT 1
#define PG_NEED_RESCHED 2
+struct nfs_inode;
struct nfs_page {
struct list_head wb_list, /* Defines state of page: */
*wb_list_head; /* read/write/commit */
@@ -54,14 +61,17 @@ extern void nfs_clear_request(struct nfs_page *req);
extern void nfs_release_request(struct nfs_page *req);
-extern void nfs_list_add_request(struct nfs_page *, struct list_head *);
-
+extern int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst,
+ unsigned long idx_start, unsigned int npages);
extern int nfs_scan_list(struct list_head *, struct list_head *,
unsigned long, unsigned int);
extern int nfs_coalesce_requests(struct list_head *, struct list_head *,
unsigned int);
extern int nfs_wait_on_request(struct nfs_page *);
extern void nfs_unlock_request(struct nfs_page *req);
+extern int nfs_set_page_writeback_locked(struct nfs_page *req);
+extern void nfs_clear_page_writeback(struct nfs_page *req);
+
/*
* Lock the page of an asynchronous request without incrementing the wb_count
@@ -86,6 +96,18 @@ nfs_lock_request(struct nfs_page *req)
return 1;
}
+/**
+ * nfs_list_add_request - Insert a request into a list
+ * @req: request
+ * @head: head of list into which to insert the request.
+ */
+static inline void
+nfs_list_add_request(struct nfs_page *req, struct list_head *head)
+{
+ list_add_tail(&req->wb_list, head);
+ req->wb_list_head = head;
+}
+
/**
* nfs_list_remove_request - Remove a request from its wb_list
@@ -96,10 +118,6 @@ nfs_list_remove_request(struct nfs_page *req)
{
if (list_empty(&req->wb_list))
return;
- if (!NFS_WBACK_BUSY(req)) {
- printk(KERN_ERR "NFS: unlocked request attempted removed from list!\n");
- BUG();
- }
list_del_init(&req->wb_list);
req->wb_list_head = NULL;
}
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 47037d9521c..a2bf6914ff1 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -2,6 +2,7 @@
#define _LINUX_NFS_XDR_H
#include <linux/sunrpc/xprt.h>
+#include <linux/nfsacl.h>
struct nfs4_fsid {
__u64 major;
@@ -326,6 +327,20 @@ struct nfs_setattrargs {
const u32 * bitmask;
};
+struct nfs_setaclargs {
+ struct nfs_fh * fh;
+ size_t acl_len;
+ unsigned int acl_pgbase;
+ struct page ** acl_pages;
+};
+
+struct nfs_getaclargs {
+ struct nfs_fh * fh;
+ size_t acl_len;
+ unsigned int acl_pgbase;
+ struct page ** acl_pages;
+};
+
struct nfs_setattrres {
struct nfs_fattr * fattr;
const struct nfs_server * server;
@@ -354,6 +369,20 @@ struct nfs_readdirargs {
struct page ** pages;
};
+struct nfs3_getaclargs {
+ struct nfs_fh * fh;
+ int mask;
+ struct page ** pages;
+};
+
+struct nfs3_setaclargs {
+ struct inode * inode;
+ int mask;
+ struct posix_acl * acl_access;
+ struct posix_acl * acl_default;
+ struct page ** pages;
+};
+
struct nfs_diropok {
struct nfs_fh * fh;
struct nfs_fattr * fattr;
@@ -477,6 +506,15 @@ struct nfs3_readdirres {
int plus;
};
+struct nfs3_getaclres {
+ struct nfs_fattr * fattr;
+ int mask;
+ unsigned int acl_access_count;
+ unsigned int acl_default_count;
+ struct posix_acl * acl_access;
+ struct posix_acl * acl_default;
+};
+
#ifdef CONFIG_NFS_V4
typedef u64 clientid4;
@@ -667,6 +705,7 @@ struct nfs_rpc_ops {
int version; /* Protocol version */
struct dentry_operations *dentry_ops;
struct inode_operations *dir_inode_ops;
+ struct inode_operations *file_inode_ops;
int (*getroot) (struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *);
@@ -713,6 +752,7 @@ struct nfs_rpc_ops {
int (*file_open) (struct inode *, struct file *);
int (*file_release) (struct inode *, struct file *);
int (*lock)(struct file *, int, struct file_lock *);
+ void (*clear_acl_cache)(struct inode *);
};
/*
@@ -732,4 +772,7 @@ extern struct rpc_version nfs_version2;
extern struct rpc_version nfs_version3;
extern struct rpc_version nfs_version4;
+extern struct rpc_version nfsacl_version3;
+extern struct rpc_program nfsacl_program;
+
#endif
diff --git a/include/linux/nfsacl.h b/include/linux/nfsacl.h
new file mode 100644
index 00000000000..54487a99beb
--- /dev/null
+++ b/include/linux/nfsacl.h
@@ -0,0 +1,58 @@
+/*
+ * File: linux/nfsacl.h
+ *
+ * (C) 2003 Andreas Gruenbacher <agruen@suse.de>
+ */
+#ifndef __LINUX_NFSACL_H
+#define __LINUX_NFSACL_H
+
+#define NFS_ACL_PROGRAM 100227
+
+#define ACLPROC2_GETACL 1
+#define ACLPROC2_SETACL 2
+#define ACLPROC2_GETATTR 3
+#define ACLPROC2_ACCESS 4
+
+#define ACLPROC3_GETACL 1
+#define ACLPROC3_SETACL 2
+
+
+/* Flags for the getacl/setacl mode */
+#define NFS_ACL 0x0001
+#define NFS_ACLCNT 0x0002
+#define NFS_DFACL 0x0004
+#define NFS_DFACLCNT 0x0008
+
+/* Flag for Default ACL entries */
+#define NFS_ACL_DEFAULT 0x1000
+
+#ifdef __KERNEL__
+
+#include <linux/posix_acl.h>
+
+/* Maximum number of ACL entries over NFS */
+#define NFS_ACL_MAX_ENTRIES 1024
+
+#define NFSACL_MAXWORDS (2*(2+3*NFS_ACL_MAX_ENTRIES))
+#define NFSACL_MAXPAGES ((2*(8+12*NFS_ACL_MAX_ENTRIES) + PAGE_SIZE-1) \
+ >> PAGE_SHIFT)
+
+static inline unsigned int
+nfsacl_size(struct posix_acl *acl_access, struct posix_acl *acl_default)
+{
+ unsigned int w = 16;
+ w += max(acl_access ? (int)acl_access->a_count : 3, 4) * 12;
+ if (acl_default)
+ w += max((int)acl_default->a_count, 4) * 12;
+ return w;
+}
+
+extern unsigned int
+nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
+ struct posix_acl *acl, int encode_entries, int typeflag);
+extern unsigned int
+nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
+ struct posix_acl **pacl);
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_NFSACL_H */
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index 8f85d9a5960..4bf931d5ff5 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -15,6 +15,7 @@
#include <linux/unistd.h>
#include <linux/dirent.h>
#include <linux/fs.h>
+#include <linux/posix_acl.h>
#include <linux/mount.h>
#include <linux/nfsd/debug.h>
@@ -124,6 +125,21 @@ int nfsd_statfs(struct svc_rqst *, struct svc_fh *,
int nfsd_notify_change(struct inode *, struct iattr *);
int nfsd_permission(struct svc_export *, struct dentry *, int);
+#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
+#ifdef CONFIG_NFSD_V2_ACL
+extern struct svc_version nfsd_acl_version2;
+#else
+#define nfsd_acl_version2 NULL
+#endif
+#ifdef CONFIG_NFSD_V3_ACL
+extern struct svc_version nfsd_acl_version3;
+#else
+#define nfsd_acl_version3 NULL
+#endif
+struct posix_acl *nfsd_get_posix_acl(struct svc_fh *, int);
+int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *);
+#endif
+
/*
* NFSv4 State
diff --git a/include/linux/nfsd/xdr.h b/include/linux/nfsd/xdr.h
index ecccef777da..130d4f588a3 100644
--- a/include/linux/nfsd/xdr.h
+++ b/include/linux/nfsd/xdr.h
@@ -169,4 +169,8 @@ int nfssvc_encode_entry(struct readdir_cd *, const char *name,
int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *);
+/* Helper functions for NFSv2 ACL code */
+u32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp);
+u32 *nfs2svc_decode_fh(u32 *p, struct svc_fh *fhp);
+
#endif /* LINUX_NFSD_H */
diff --git a/include/linux/nfsd/xdr3.h b/include/linux/nfsd/xdr3.h
index 0ae9e0ef5f6..21e18ce7ca6 100644
--- a/include/linux/nfsd/xdr3.h
+++ b/include/linux/nfsd/xdr3.h
@@ -110,6 +110,19 @@ struct nfsd3_commitargs {
__u32 count;
};
+struct nfsd3_getaclargs {
+ struct svc_fh fh;
+ int mask;
+};
+
+struct posix_acl;
+struct nfsd3_setaclargs {
+ struct svc_fh fh;
+ int mask;
+ struct posix_acl *acl_access;
+ struct posix_acl *acl_default;
+};
+
struct nfsd3_attrstat {
__u32 status;
struct svc_fh fh;
@@ -209,6 +222,14 @@ struct nfsd3_commitres {
struct svc_fh fh;
};
+struct nfsd3_getaclres {
+ __u32 status;
+ struct svc_fh fh;
+ int mask;
+ struct posix_acl *acl_access;
+ struct posix_acl *acl_default;
+};
+
/* dummy type for release */
struct nfsd3_fhandle_pair {
__u32 dummy;
@@ -241,6 +262,7 @@ union nfsd3_xdrstore {
struct nfsd3_fsinfores fsinfores;
struct nfsd3_pathconfres pathconfres;
struct nfsd3_commitres commitres;
+ struct nfsd3_getaclres getaclres;
};
#define NFS3_SVC_XDRSIZE sizeof(union nfsd3_xdrstore)
@@ -316,6 +338,10 @@ int nfs3svc_encode_entry(struct readdir_cd *, const char *name,
int nfs3svc_encode_entry_plus(struct readdir_cd *, const char *name,
int namlen, loff_t offset, ino_t ino,
unsigned int);
+/* Helper functions for NFSv3 ACL code */
+u32 *nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, u32 *p,
+ struct svc_fh *fhp);
+u32 *nfs3svc_decode_fh(u32 *p, struct svc_fh *fhp);
#endif /* _LINUX_NFSD_XDR3_H */
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 2709caf4d12..ab151bbb66d 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -111,6 +111,11 @@ struct rpc_procinfo {
struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname,
struct rpc_program *info,
u32 version, rpc_authflavor_t authflavor);
+struct rpc_clnt *rpc_new_client(struct rpc_xprt *xprt, char *servname,
+ struct rpc_program *info,
+ u32 version, rpc_authflavor_t authflavor);
+struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
+ struct rpc_program *, int);
struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
int rpc_shutdown_client(struct rpc_clnt *);
int rpc_destroy_client(struct rpc_clnt *);
@@ -129,6 +134,7 @@ void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
size_t rpc_max_payload(struct rpc_clnt *);
+int rpc_ping(struct rpc_clnt *clnt, int flags);
static __inline__
int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
index 99d17ed7ceb..4d77e90d0b3 100644
--- a/include/linux/sunrpc/sched.h
+++ b/include/linux/sunrpc/sched.h
@@ -31,7 +31,6 @@ struct rpc_wait_queue;
struct rpc_wait {
struct list_head list; /* wait queue links */
struct list_head links; /* Links to related tasks */
- wait_queue_head_t waitq; /* sync: sleep on this q */
struct rpc_wait_queue * rpc_waitq; /* RPC wait queue we're on */
};
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 37003970cf2..5af8800e0ce 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -185,6 +185,17 @@ xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
return vec->iov_len <= PAGE_SIZE;
}
+static inline struct page *
+svc_take_res_page(struct svc_rqst *rqstp)
+{
+ if (rqstp->rq_arghi <= rqstp->rq_argused)
+ return NULL;
+ rqstp->rq_arghi--;
+ rqstp->rq_respages[rqstp->rq_resused] =
+ rqstp->rq_argpages[rqstp->rq_arghi];
+ return rqstp->rq_respages[rqstp->rq_resused++];
+}
+
static inline int svc_take_page(struct svc_rqst *rqstp)
{
if (rqstp->rq_arghi <= rqstp->rq_argused)
@@ -240,9 +251,10 @@ struct svc_deferred_req {
};
/*
- * RPC program
+ * List of RPC programs on the same transport endpoint
*/
struct svc_program {
+ struct svc_program * pg_next; /* other programs (same xprt) */
u32 pg_prog; /* program number */
unsigned int pg_lovers; /* lowest version */
unsigned int pg_hivers; /* lowest version */
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 541dcf838ab..34ec3e8d99b 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -146,7 +146,8 @@ extern void xdr_shift_buf(struct xdr_buf *, size_t);
extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *);
extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int);
extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int);
-extern int read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len);
+extern int read_bytes_from_xdr_buf(struct xdr_buf *, int, void *, int);
+extern int write_bytes_to_xdr_buf(struct xdr_buf *, int, void *, int);
/*
* Helper structure for copying from an sk_buff.
@@ -160,7 +161,7 @@ typedef struct {
typedef size_t (*skb_read_actor_t)(skb_reader_t *desc, void *to, size_t len);
-extern void xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int,
+extern ssize_t xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int,
skb_reader_t *, skb_read_actor_t);
struct socket;
@@ -168,6 +169,22 @@ struct sockaddr;
extern int xdr_sendpages(struct socket *, struct sockaddr *, int,
struct xdr_buf *, unsigned int, int);
+extern int xdr_encode_word(struct xdr_buf *, int, u32);
+extern int xdr_decode_word(struct xdr_buf *, int, u32 *);
+
+struct xdr_array2_desc;
+typedef int (*xdr_xcode_elem_t)(struct xdr_array2_desc *desc, void *elem);
+struct xdr_array2_desc {
+ unsigned int elem_size;
+ unsigned int array_len;
+ xdr_xcode_elem_t xcode;
+};
+
+extern int xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
+ struct xdr_array2_desc *desc);
+extern int xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
+ struct xdr_array2_desc *desc);
+
/*
* Provide some simple tools for XDR buffer overflow-checking etc.
*/
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 9bcec9b927b..505e2d4b3d6 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -66,10 +66,10 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
u32 flavor = pseudoflavor_to_flavor(pseudoflavor);
if (flavor >= RPC_AUTH_MAXFLAVOR || !(ops = auth_flavors[flavor]))
- return NULL;
+ return ERR_PTR(-EINVAL);
auth = ops->create(clnt, pseudoflavor);
- if (!auth)
- return NULL;
+ if (IS_ERR(auth))
+ return auth;
if (clnt->cl_auth)
rpcauth_destroy(clnt->cl_auth);
clnt->cl_auth = auth;
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index a33b627cbef..2f7b867161d 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -660,14 +660,16 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
{
struct gss_auth *gss_auth;
struct rpc_auth * auth;
+ int err = -ENOMEM; /* XXX? */
dprintk("RPC: creating GSS authenticator for client %p\n",clnt);
if (!try_module_get(THIS_MODULE))
- return NULL;
+ return ERR_PTR(err);
if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
goto out_dec;
gss_auth->client = clnt;
+ err = -EINVAL;
gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
if (!gss_auth->mech) {
printk(KERN_WARNING "%s: Pseudoflavor %d not found!",
@@ -675,9 +677,8 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
goto err_free;
}
gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
- /* FIXME: Will go away once privacy support is merged in */
- if (gss_auth->service == RPC_GSS_SVC_PRIVACY)
- gss_auth->service = RPC_GSS_SVC_INTEGRITY;
+ if (gss_auth->service == 0)
+ goto err_put_mech;
INIT_LIST_HEAD(&gss_auth->upcalls);
spin_lock_init(&gss_auth->lock);
auth = &gss_auth->rpc_auth;
@@ -687,15 +688,18 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
auth->au_flavor = flavor;
atomic_set(&auth->au_count, 1);
- if (rpcauth_init_credcache(auth, GSS_CRED_EXPIRE) < 0)
+ err = rpcauth_init_credcache(auth, GSS_CRED_EXPIRE);
+ if (err)
goto err_put_mech;
snprintf(gss_auth->path, sizeof(gss_auth->path), "%s/%s",
clnt->cl_pathname,
gss_auth->mech->gm_name);
gss_auth->dentry = rpc_mkpipe(gss_auth->path, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
- if (IS_ERR(gss_auth->dentry))
+ if (IS_ERR(gss_auth->dentry)) {
+ err = PTR_ERR(gss_auth->dentry);
goto err_put_mech;
+ }
return auth;
err_put_mech:
@@ -704,7 +708,7 @@ err_free:
kfree(gss_auth);
out_dec:
module_put(THIS_MODULE);
- return NULL;
+ return ERR_PTR(err);
}
static void
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 02bc029d46f..f17e6153b68 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -97,12 +97,13 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
* made to sleep too long.
*/
struct rpc_clnt *
-rpc_create_client(struct rpc_xprt *xprt, char *servname,
+rpc_new_client(struct rpc_xprt *xprt, char *servname,
struct rpc_program *program, u32 vers,
rpc_authflavor_t flavor)
{
struct rpc_version *version;
struct rpc_clnt *clnt = NULL;
+ struct rpc_auth *auth;
int err;
int len;
@@ -157,10 +158,11 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
if (err < 0)
goto out_no_path;
- err = -ENOMEM;
- if (!rpcauth_create(flavor, clnt)) {
+ auth = rpcauth_create(flavor, clnt);
+ if (IS_ERR(auth)) {
printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n",
flavor);
+ err = PTR_ERR(auth);
goto out_no_auth;
}
@@ -178,6 +180,37 @@ out_no_path:
kfree(clnt->cl_server);
kfree(clnt);
out_err:
+ xprt_destroy(xprt);
+ return ERR_PTR(err);
+}
+
+/**
+ * Create an RPC client
+ * @xprt - pointer to xprt struct
+ * @servname - name of server
+ * @info - rpc_program
+ * @version - rpc_program version
+ * @authflavor - rpc_auth flavour to use
+ *
+ * Creates an RPC client structure, then pings the server in order to
+ * determine if it is up, and if it supports this program and version.
+ *
+ * This function should never be called by asynchronous tasks such as
+ * the portmapper.
+ */
+struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname,
+ struct rpc_program *info, u32 version, rpc_authflavor_t authflavor)
+{
+ struct rpc_clnt *clnt;
+ int err;
+
+ clnt = rpc_new_client(xprt, servname, info, version, authflavor);
+ if (IS_ERR(clnt))
+ return clnt;
+ err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
+ if (err == 0)
+ return clnt;
+ rpc_shutdown_client(clnt);
return ERR_PTR(err);
}
@@ -208,6 +241,8 @@ rpc_clone_client(struct rpc_clnt *clnt)
rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count);
+ new->cl_pmap = &new->cl_pmap_default;
+ rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
return new;
out_no_clnt:
printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__);
@@ -296,6 +331,44 @@ rpc_release_client(struct rpc_clnt *clnt)
rpc_destroy_client(clnt);
}
+/**
+ * rpc_bind_new_program - bind a new RPC program to an existing client
+ * @old - old rpc_client
+ * @program - rpc program to set
+ * @vers - rpc program version
+ *
+ * Clones the rpc client and sets up a new RPC program. This is mainly
+ * of use for enabling different RPC programs to share the same transport.
+ * The Sun NFSv2/v3 ACL protocol can do this.
+ */
+struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
+ struct rpc_program *program,
+ int vers)
+{
+ struct rpc_clnt *clnt;
+ struct rpc_version *version;
+ int err;
+
+ BUG_ON(vers >= program->nrvers || !program->version[vers]);
+ version = program->version[vers];
+ clnt = rpc_clone_client(old);
+ if (IS_ERR(clnt))
+ goto out;
+ clnt->cl_procinfo = version->procs;
+ clnt->cl_maxproc = version->nrprocs;
+ clnt->cl_protname = program->name;
+ clnt->cl_prog = program->number;
+ clnt->cl_vers = version->number;
+ clnt->cl_stats = program->stats;
+ err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
+ if (err != 0) {
+ rpc_shutdown_client(clnt);
+ clnt = ERR_PTR(err);
+ }
+out:
+ return clnt;
+}
+
/*
* Default callback for async RPC calls
*/
@@ -305,38 +378,41 @@ rpc_default_callback(struct rpc_task *task)
}
/*
- * Export the signal mask handling for aysnchronous code that
+ * Export the signal mask handling for synchronous code that
* sleeps on RPC calls
*/
+#define RPC_INTR_SIGNALS (sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGKILL))
+static void rpc_save_sigmask(sigset_t *oldset, int intr)
+{
+ unsigned long sigallow = 0;
+ sigset_t sigmask;
+
+ /* Block all signals except those listed in sigallow */
+ if (intr)
+ sigallow |= RPC_INTR_SIGNALS;
+ siginitsetinv(&sigmask, sigallow);
+ sigprocmask(SIG_BLOCK, &sigmask, oldset);
+}
+
+static inline void rpc_task_sigmask(struct rpc_task *task, sigset_t *oldset)
+{
+ rpc_save_sigmask(oldset, !RPC_TASK_UNINTERRUPTIBLE(task));
+}
+
+static inline void rpc_restore_sigmask(sigset_t *oldset)
+{
+ sigprocmask(SIG_SETMASK, oldset, NULL);
+}
+
void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset)
{
- unsigned long sigallow = sigmask(SIGKILL);
- unsigned long irqflags;
-
- /* Turn off various signals */
- if (clnt->cl_intr) {
- struct k_sigaction *action = current->sighand->action;
- if (action[SIGINT-1].sa.sa_handler == SIG_DFL)
- sigallow |= sigmask(SIGINT);
- if (action[SIGQUIT-1].sa.sa_handler == SIG_DFL)
- sigallow |= sigmask(SIGQUIT);
- }
- spin_lock_irqsave(&current->sighand->siglock, irqflags);
- *oldset = current->blocked;
- siginitsetinv(&current->blocked, sigallow & ~oldset->sig[0]);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
+ rpc_save_sigmask(oldset, clnt->cl_intr);
}
void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
{
- unsigned long irqflags;
-
- spin_lock_irqsave(&current->sighand->siglock, irqflags);
- current->blocked = *oldset;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
+ rpc_restore_sigmask(oldset);
}
/*
@@ -354,26 +430,26 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
BUG_ON(flags & RPC_TASK_ASYNC);
- rpc_clnt_sigmask(clnt, &oldset);
-
status = -ENOMEM;
task = rpc_new_task(clnt, NULL, flags);
if (task == NULL)
goto out;
+ /* Mask signals on RPC calls _and_ GSS_AUTH upcalls */
+ rpc_task_sigmask(task, &oldset);
+
rpc_call_setup(task, msg, 0);
/* Set up the call info struct and execute the task */
- if (task->tk_status == 0)
+ if (task->tk_status == 0) {
status = rpc_execute(task);
- else {
+ } else {
status = task->tk_status;
rpc_release_task(task);
}
+ rpc_restore_sigmask(&oldset);
out:
- rpc_clnt_sigunmask(clnt, &oldset);
-
return status;
}
@@ -394,8 +470,6 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
flags |= RPC_TASK_ASYNC;
- rpc_clnt_sigmask(clnt, &oldset);
-
/* Create/initialize a new RPC task */
if (!callback)
callback = rpc_default_callback;
@@ -404,6 +478,9 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
goto out;
task->tk_calldata = data;
+ /* Mask signals on GSS_AUTH upcalls */
+ rpc_task_sigmask(task, &oldset);
+
rpc_call_setup(task, msg, 0);
/* Set up the call info struct and execute the task */
@@ -413,9 +490,8 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
else
rpc_release_task(task);
+ rpc_restore_sigmask(&oldset);
out:
- rpc_clnt_sigunmask(clnt, &oldset);
-
return status;
}
@@ -593,7 +669,7 @@ call_allocate(struct rpc_task *task)
return;
printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task);
- if (RPC_IS_ASYNC(task) || !(task->tk_client->cl_intr && signalled())) {
+ if (RPC_IS_ASYNC(task) || !signalled()) {
xprt_release(task);
task->tk_action = call_reserve;
rpc_delay(task, HZ>>4);
@@ -957,7 +1033,9 @@ call_header(struct rpc_task *task)
*p++ = htonl(clnt->cl_prog); /* program number */
*p++ = htonl(clnt->cl_vers); /* program version */
*p++ = htonl(task->tk_msg.rpc_proc->p_proc); /* procedure */
- return rpcauth_marshcred(task, p);
+ p = rpcauth_marshcred(task, p);
+ req->rq_slen = xdr_adjust_iovec(&req->rq_svec[0], p);
+ return p;
}
/*
@@ -986,10 +1064,11 @@ call_verify(struct rpc_task *task)
case RPC_AUTH_ERROR:
break;
case RPC_MISMATCH:
- printk(KERN_WARNING "%s: RPC call version mismatch!\n", __FUNCTION__);
- goto out_eio;
+ dprintk("%s: RPC call version mismatch!\n", __FUNCTION__);
+ error = -EPROTONOSUPPORT;
+ goto out_err;
default:
- printk(KERN_WARNING "%s: RPC call rejected, unknown error: %x\n", __FUNCTION__, n);
+ dprintk("%s: RPC call rejected, unknown error: %x\n", __FUNCTION__, n);
goto out_eio;
}
if (--len < 0)
@@ -1040,23 +1119,26 @@ call_verify(struct rpc_task *task)
case RPC_SUCCESS:
return p;
case RPC_PROG_UNAVAIL:
- printk(KERN_WARNING "RPC: call_verify: program %u is unsupported by server %s\n",
+ dprintk("RPC: call_verify: program %u is unsupported by server %s\n",
(unsigned int)task->tk_client->cl_prog,
task->tk_client->cl_server);
- goto out_eio;
+ error = -EPFNOSUPPORT;
+ goto out_err;
case RPC_PROG_MISMATCH:
- printk(KERN_WARNING "RPC: call_verify: program %u, version %u unsupported by server %s\n",
+ dprintk("RPC: call_verify: program %u, version %u unsupported by server %s\n",
(unsigned int)task->tk_client->cl_prog,
(unsigned int)task->tk_client->cl_vers,
task->tk_client->cl_server);
- goto out_eio;
+ error = -EPROTONOSUPPORT;
+ goto out_err;
case RPC_PROC_UNAVAIL:
- printk(KERN_WARNING "RPC: call_verify: proc %p unsupported by program %u, version %u on server %s\n",
+ dprintk("RPC: call_verify: proc %p unsupported by program %u, version %u on server %s\n",
task->tk_msg.rpc_proc,
task->tk_client->cl_prog,
task->tk_client->cl_vers,
task->tk_client->cl_server);
- goto out_eio;
+ error = -EOPNOTSUPP;
+ goto out_err;
case RPC_GARBAGE_ARGS:
dprintk("RPC: %4d %s: server saw garbage\n", task->tk_pid, __FUNCTION__);
break; /* retry */
@@ -1069,7 +1151,7 @@ out_retry:
task->tk_client->cl_stats->rpcgarbage++;
if (task->tk_garb_retry) {
task->tk_garb_retry--;
- dprintk(KERN_WARNING "RPC %s: retrying %4d\n", __FUNCTION__, task->tk_pid);
+ dprintk("RPC %s: retrying %4d\n", __FUNCTION__, task->tk_pid);
task->tk_action = call_bind;
return NULL;
}
@@ -1083,3 +1165,30 @@ out_overflow:
printk(KERN_WARNING "RPC %s: server reply was truncated.\n", __FUNCTION__);
goto out_retry;
}
+
+static int rpcproc_encode_null(void *rqstp, u32 *data, void *obj)
+{
+ return 0;
+}
+
+static int rpcproc_decode_null(void *rqstp, u32 *data, void *obj)
+{
+ return 0;
+}
+
+static struct rpc_procinfo rpcproc_null = {
+ .p_encode = rpcproc_encode_null,
+ .p_decode = rpcproc_decode_null,
+};
+
+int rpc_ping(struct rpc_clnt *clnt, int flags)
+{
+ struct rpc_message msg = {
+ .rpc_proc = &rpcproc_null,
+ };
+ int err;
+ msg.rpc_cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+ err = rpc_call_sync(clnt, &msg, flags);
+ put_rpccred(msg.rpc_cred);
+ return err;
+}
diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c
index d0b1d2c34a4..4e81f276692 100644
--- a/net/sunrpc/pmap_clnt.c
+++ b/net/sunrpc/pmap_clnt.c
@@ -53,6 +53,9 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
task->tk_pid, clnt->cl_server,
map->pm_prog, map->pm_vers, map->pm_prot);
+ /* Autobind on cloned rpc clients is discouraged */
+ BUG_ON(clnt->cl_parent != clnt);
+
spin_lock(&pmap_lock);
if (map->pm_binding) {
rpc_sleep_on(&map->pm_bindwait, task, NULL, NULL);
@@ -207,12 +210,10 @@ pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto)
xprt->addr.sin_port = htons(RPC_PMAP_PORT);
/* printk("pmap: create clnt\n"); */
- clnt = rpc_create_client(xprt, hostname,
+ clnt = rpc_new_client(xprt, hostname,
&pmap_program, RPC_PMAP_VERSION,
RPC_AUTH_UNIX);
- if (IS_ERR(clnt)) {
- xprt_destroy(xprt);
- } else {
+ if (!IS_ERR(clnt)) {
clnt->cl_softrtry = 1;
clnt->cl_chatty = 1;
clnt->cl_oneshot = 1;
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index c06614d0e31..2d9eb7fbd52 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -290,7 +290,7 @@ static void rpc_make_runnable(struct rpc_task *task)
return;
}
} else
- wake_up(&task->u.tk_wait.waitq);
+ wake_up_bit(&task->tk_runstate, RPC_TASK_QUEUED);
}
/*
@@ -555,6 +555,38 @@ __rpc_atrun(struct rpc_task *task)
}
/*
+ * Helper that calls task->tk_exit if it exists and then returns
+ * true if we should exit __rpc_execute.
+ */
+static inline int __rpc_do_exit(struct rpc_task *task)
+{
+ if (task->tk_exit != NULL) {
+ lock_kernel();
+ task->tk_exit(task);
+ unlock_kernel();
+ /* If tk_action is non-null, we should restart the call */
+ if (task->tk_action != NULL) {
+ if (!RPC_ASSASSINATED(task)) {
+ /* Release RPC slot and buffer memory */
+ xprt_release(task);
+ rpc_free(task);
+ return 0;
+ }
+ printk(KERN_ERR "RPC: dead task tried to walk away.\n");
+ }
+ }
+ return 1;
+}
+
+static int rpc_wait_bit_interruptible(void *word)
+{
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ schedule();
+ return 0;
+}
+
+/*
* This is the RPC `scheduler' (or rather, the finite state machine).
*/
static int __rpc_execute(struct rpc_task *task)
@@ -566,8 +598,7 @@ static int __rpc_execute(struct rpc_task *task)
BUG_ON(RPC_IS_QUEUED(task));
- restarted:
- while (1) {
+ for (;;) {
/*
* Garbage collection of pending timers...
*/
@@ -600,11 +631,12 @@ static int __rpc_execute(struct rpc_task *task)
* by someone else.
*/
if (!RPC_IS_QUEUED(task)) {
- if (!task->tk_action)
+ if (task->tk_action != NULL) {
+ lock_kernel();
+ task->tk_action(task);
+ unlock_kernel();
+ } else if (__rpc_do_exit(task))
break;
- lock_kernel();
- task->tk_action(task);
- unlock_kernel();
}
/*
@@ -624,44 +656,26 @@ static int __rpc_execute(struct rpc_task *task)
/* sync task: sleep here */
dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid);
- if (RPC_TASK_UNINTERRUPTIBLE(task)) {
- __wait_event(task->u.tk_wait.waitq, !RPC_IS_QUEUED(task));
- } else {
- __wait_event_interruptible(task->u.tk_wait.waitq, !RPC_IS_QUEUED(task), status);
+ /* Note: Caller should be using rpc_clnt_sigmask() */
+ status = out_of_line_wait_on_bit(&task->tk_runstate,
+ RPC_TASK_QUEUED, rpc_wait_bit_interruptible,
+ TASK_INTERRUPTIBLE);
+ if (status == -ERESTARTSYS) {
/*
* When a sync task receives a signal, it exits with
* -ERESTARTSYS. In order to catch any callbacks that
* clean up after sleeping on some queue, we don't
* break the loop here, but go around once more.
*/
- if (status == -ERESTARTSYS) {
- dprintk("RPC: %4d got signal\n", task->tk_pid);
- task->tk_flags |= RPC_TASK_KILLED;
- rpc_exit(task, -ERESTARTSYS);
- rpc_wake_up_task(task);
- }
+ dprintk("RPC: %4d got signal\n", task->tk_pid);
+ task->tk_flags |= RPC_TASK_KILLED;
+ rpc_exit(task, -ERESTARTSYS);
+ rpc_wake_up_task(task);
}
rpc_set_running(task);
dprintk("RPC: %4d sync task resuming\n", task->tk_pid);
}
- if (task->tk_exit) {
- lock_kernel();
- task->tk_exit(task);
- unlock_kernel();
- /* If tk_action is non-null, the user wants us to restart */
- if (task->tk_action) {
- if (!RPC_ASSASSINATED(task)) {
- /* Release RPC slot and buffer memory */
- if (task->tk_rqstp)
- xprt_release(task);
- rpc_free(task);
- goto restarted;
- }
- printk(KERN_ERR "RPC: dead task tries to walk away.\n");
- }
- }
-
dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
status = task->tk_status;
@@ -759,8 +773,6 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, rpc_action call
/* Initialize workqueue for async tasks */
task->tk_workqueue = rpciod_workqueue;
- if (!RPC_IS_ASYNC(task))
- init_waitqueue_head(&task->u.tk_wait.waitq);
if (clnt) {
atomic_inc(&clnt->cl_users);
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index d4f26bf9e73..32e8acbc60f 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -42,6 +42,7 @@ EXPORT_SYMBOL(rpc_release_task);
/* RPC client functions */
EXPORT_SYMBOL(rpc_create_client);
EXPORT_SYMBOL(rpc_clone_client);
+EXPORT_SYMBOL(rpc_bind_new_program);
EXPORT_SYMBOL(rpc_destroy_client);
EXPORT_SYMBOL(rpc_shutdown_client);
EXPORT_SYMBOL(rpc_release_client);
@@ -61,7 +62,6 @@ EXPORT_SYMBOL(rpc_mkpipe);
/* Client transport */
EXPORT_SYMBOL(xprt_create_proto);
-EXPORT_SYMBOL(xprt_destroy);
EXPORT_SYMBOL(xprt_set_timeout);
EXPORT_SYMBOL(xprt_udp_slot_table_entries);
EXPORT_SYMBOL(xprt_tcp_slot_table_entries);
@@ -129,6 +129,10 @@ EXPORT_SYMBOL(xdr_encode_netobj);
EXPORT_SYMBOL(xdr_encode_pages);
EXPORT_SYMBOL(xdr_inline_pages);
EXPORT_SYMBOL(xdr_shift_buf);
+EXPORT_SYMBOL(xdr_encode_word);
+EXPORT_SYMBOL(xdr_decode_word);
+EXPORT_SYMBOL(xdr_encode_array2);
+EXPORT_SYMBOL(xdr_decode_array2);
EXPORT_SYMBOL(xdr_buf_from_iov);
EXPORT_SYMBOL(xdr_buf_subsegment);
EXPORT_SYMBOL(xdr_buf_read_netobj);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index bb2d99f3331..e9bd91265f7 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -35,20 +35,24 @@ svc_create(struct svc_program *prog, unsigned int bufsize)
if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL)))
return NULL;
memset(serv, 0, sizeof(*serv));
+ serv->sv_name = prog->pg_name;
serv->sv_program = prog;
serv->sv_nrthreads = 1;
serv->sv_stats = prog->pg_stats;
serv->sv_bufsz = bufsize? bufsize : 4096;
- prog->pg_lovers = prog->pg_nvers-1;
xdrsize = 0;
- for (vers=0; vers<prog->pg_nvers ; vers++)
- if (prog->pg_vers[vers]) {
- prog->pg_hivers = vers;
- if (prog->pg_lovers > vers)
- prog->pg_lovers = vers;
- if (prog->pg_vers[vers]->vs_xdrsize > xdrsize)
- xdrsize = prog->pg_vers[vers]->vs_xdrsize;
- }
+ while (prog) {
+ prog->pg_lovers = prog->pg_nvers-1;
+ for (vers=0; vers<prog->pg_nvers ; vers++)
+ if (prog->pg_vers[vers]) {
+ prog->pg_hivers = vers;
+ if (prog->pg_lovers > vers)
+ prog->pg_lovers = vers;
+ if (prog->pg_vers[vers]->vs_xdrsize > xdrsize)
+ xdrsize = prog->pg_vers[vers]->vs_xdrsize;
+ }
+ prog = prog->pg_next;
+ }
serv->sv_xdrsize = xdrsize;
INIT_LIST_HEAD(&serv->sv_threads);
INIT_LIST_HEAD(&serv->sv_sockets);
@@ -56,8 +60,6 @@ svc_create(struct svc_program *prog, unsigned int bufsize)
INIT_LIST_HEAD(&serv->sv_permsocks);
spin_lock_init(&serv->sv_lock);
- serv->sv_name = prog->pg_name;
-
/* Remove any stale portmap registrations */
svc_register(serv, 0, 0);
@@ -281,6 +283,7 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
rqstp->rq_res.len = 0;
rqstp->rq_res.page_base = 0;
rqstp->rq_res.page_len = 0;
+ rqstp->rq_res.buflen = PAGE_SIZE;
rqstp->rq_res.tail[0].iov_len = 0;
/* tcp needs a space for the record length... */
if (rqstp->rq_prot == IPPROTO_TCP)
@@ -338,7 +341,10 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
goto sendit;
}
- if (prog != progp->pg_prog)
+ for (progp = serv->sv_program; progp; progp = progp->pg_next)
+ if (prog == progp->pg_prog)
+ break;
+ if (progp == NULL)
goto err_bad_prog;
if (vers >= progp->pg_nvers ||
@@ -451,11 +457,7 @@ err_bad_auth:
goto sendit;
err_bad_prog:
-#ifdef RPC_PARANOIA
- if (prog != 100227 || progp->pg_prog != 100003)
- printk("svc: unknown program %d (me %d)\n", prog, progp->pg_prog);
- /* else it is just a Solaris client seeing if ACLs are supported */
-#endif
+ dprintk("svc: unknown program %d\n", prog);
serv->sv_stats->rpcbadfmt++;
svc_putu32(resv, rpc_prog_unavail);
goto sendit;
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 67b9f035ba8..8a4d9c106af 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -176,21 +176,23 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
xdr->buflen += len;
}
-void
+ssize_t
xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
skb_reader_t *desc,
skb_read_actor_t copy_actor)
{
struct page **ppage = xdr->pages;
unsigned int len, pglen = xdr->page_len;
+ ssize_t copied = 0;
int ret;
len = xdr->head[0].iov_len;
if (base < len) {
len -= base;
ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len);
+ copied += ret;
if (ret != len || !desc->count)
- return;
+ goto out;
base = 0;
} else
base -= len;
@@ -210,6 +212,17 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
do {
char *kaddr;
+ /* ACL likes to be lazy in allocating pages - ACLs
+ * are small by default but can get huge. */
+ if (unlikely(*ppage == NULL)) {
+ *ppage = alloc_page(GFP_ATOMIC);
+ if (unlikely(*ppage == NULL)) {
+ if (copied == 0)
+ copied = -ENOMEM;
+ goto out;
+ }
+ }
+
len = PAGE_CACHE_SIZE;
kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA);
if (base) {
@@ -225,14 +238,17 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
}
flush_dcache_page(*ppage);
kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA);
+ copied += ret;
if (ret != len || !desc->count)
- return;
+ goto out;
ppage++;
} while ((pglen -= len) != 0);
copy_tail:
len = xdr->tail[0].iov_len;
if (base < len)
- copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base);
+ copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base);
+out:
+ return copied;
}
@@ -616,12 +632,24 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p)
{
struct kvec *iov = buf->head;
+ int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
+ BUG_ON(scratch_len < 0);
xdr->buf = buf;
xdr->iov = iov;
- xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len);
- buf->len = iov->iov_len = (char *)p - (char *)iov->iov_base;
- xdr->p = p;
+ xdr->p = (uint32_t *)((char *)iov->iov_base + iov->iov_len);
+ xdr->end = (uint32_t *)((char *)iov->iov_base + scratch_len);
+ BUG_ON(iov->iov_len > scratch_len);
+
+ if (p != xdr->p && p != NULL) {
+ size_t len;
+
+ BUG_ON(p < xdr->p || p > xdr->end);
+ len = (char *)p - (char *)xdr->p;
+ xdr->p = p;
+ buf->len += len;
+ iov->iov_len += len;
+ }
}
EXPORT_SYMBOL(xdr_init_encode);
@@ -859,8 +887,34 @@ out:
return status;
}
-static int
-read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
+/* obj is assumed to point to allocated memory of size at least len: */
+int
+write_bytes_to_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len)
+{
+ struct xdr_buf subbuf;
+ int this_len;
+ int status;
+
+ status = xdr_buf_subsegment(buf, &subbuf, base, len);
+ if (status)
+ goto out;
+ this_len = min(len, (int)subbuf.head[0].iov_len);
+ memcpy(subbuf.head[0].iov_base, obj, this_len);
+ len -= this_len;
+ obj += this_len;
+ this_len = min(len, (int)subbuf.page_len);
+ if (this_len)
+ _copy_to_pages(subbuf.pages, subbuf.page_base, obj, this_len);
+ len -= this_len;
+ obj += this_len;
+ this_len = min(len, (int)subbuf.tail[0].iov_len);
+ memcpy(subbuf.tail[0].iov_base, obj, this_len);
+out:
+ return status;
+}
+
+int
+xdr_decode_word(struct xdr_buf *buf, int base, u32 *obj)
{
u32 raw;
int status;
@@ -872,6 +926,14 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
return 0;
}
+int
+xdr_encode_word(struct xdr_buf *buf, int base, u32 obj)
+{
+ u32 raw = htonl(obj);
+
+ return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
+}
+
/* If the netobj starting offset bytes from the start of xdr_buf is contained
* entirely in the head or the tail, set object to point to it; otherwise
* try to find space for it at the end of the tail, copy it there, and
@@ -882,7 +944,7 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset)
u32 tail_offset = buf->head[0].iov_len + buf->page_len;
u32 obj_end_offset;
- if (read_u32_from_xdr_buf(buf, offset, &obj->len))
+ if (xdr_decode_word(buf, offset, &obj->len))
goto out;
obj_end_offset = offset + 4 + obj->len;
@@ -915,3 +977,219 @@ xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset)
out:
return -1;
}
+
+/* Returns 0 on success, or else a negative error code. */
+static int
+xdr_xcode_array2(struct xdr_buf *buf, unsigned int base,
+ struct xdr_array2_desc *desc, int encode)
+{
+ char *elem = NULL, *c;
+ unsigned int copied = 0, todo, avail_here;
+ struct page **ppages = NULL;
+ int err;
+
+ if (encode) {
+ if (xdr_encode_word(buf, base, desc->array_len) != 0)
+ return -EINVAL;
+ } else {
+ if (xdr_decode_word(buf, base, &desc->array_len) != 0 ||
+ (unsigned long) base + 4 + desc->array_len *
+ desc->elem_size > buf->len)
+ return -EINVAL;
+ }
+ base += 4;
+
+ if (!desc->xcode)
+ return 0;
+
+ todo = desc->array_len * desc->elem_size;
+
+ /* process head */
+ if (todo && base < buf->head->iov_len) {
+ c = buf->head->iov_base + base;
+ avail_here = min_t(unsigned int, todo,
+ buf->head->iov_len - base);
+ todo -= avail_here;
+
+ while (avail_here >= desc->elem_size) {
+ err = desc->xcode(desc, c);
+ if (err)
+ goto out;
+ c += desc->elem_size;
+ avail_here -= desc->elem_size;
+ }
+ if (avail_here) {
+ if (!elem) {
+ elem = kmalloc(desc->elem_size, GFP_KERNEL);
+ err = -ENOMEM;
+ if (!elem)
+ goto out;
+ }
+ if (encode) {
+ err = desc->xcode(desc, elem);
+ if (err)
+ goto out;
+ memcpy(c, elem, avail_here);
+ } else
+ memcpy(elem, c, avail_here);
+ copied = avail_here;
+ }
+ base = buf->head->iov_len; /* align to start of pages */
+ }
+
+ /* process pages array */
+ base -= buf->head->iov_len;
+ if (todo && base < buf->page_len) {
+ unsigned int avail_page;
+
+ avail_here = min(todo, buf->page_len - base);
+ todo -= avail_here;
+
+ base += buf->page_base;
+ ppages = buf->pages + (base >> PAGE_CACHE_SHIFT);
+ base &= ~PAGE_CACHE_MASK;
+ avail_page = min_t(unsigned int, PAGE_CACHE_SIZE - base,
+ avail_here);
+ c = kmap(*ppages) + base;
+
+ while (avail_here) {
+ avail_here -= avail_page;
+ if (copied || avail_page < desc->elem_size) {
+ unsigned int l = min(avail_page,
+ desc->elem_size - copied);
+ if (!elem) {
+ elem = kmalloc(desc->elem_size,
+ GFP_KERNEL);
+ err = -ENOMEM;
+ if (!elem)
+ goto out;
+ }
+ if (encode) {
+ if (!copied) {
+ err = desc->xcode(desc, elem);
+ if (err)
+ goto out;
+ }
+ memcpy(c, elem + copied, l);
+ copied += l;
+ if (copied == desc->elem_size)
+ copied = 0;
+ } else {
+ memcpy(elem + copied, c, l);
+ copied += l;
+ if (copied == desc->elem_size) {
+ err = desc->xcode(desc, elem);
+ if (err)
+ goto out;
+ copied = 0;
+ }
+ }
+ avail_page -= l;
+ c += l;
+ }
+ while (avail_page >= desc->elem_size) {
+ err = desc->xcode(desc, c);
+ if (err)
+ goto out;
+ c += desc->elem_size;
+ avail_page -= desc->elem_size;
+ }
+ if (avail_page) {
+ unsigned int l = min(avail_page,
+ desc->elem_size - copied);
+ if (!elem) {
+ elem = kmalloc(desc->elem_size,
+ GFP_KERNEL);
+ err = -ENOMEM;
+ if (!elem)
+ goto out;
+ }
+ if (encode) {
+ if (!copied) {
+ err = desc->xcode(desc, elem);
+ if (err)
+ goto out;
+ }
+ memcpy(c, elem + copied, l);
+ copied += l;
+ if (copied == desc->elem_size)
+ copied = 0;
+ } else {
+ memcpy(elem + copied, c, l);
+ copied += l;
+ if (copied == desc->elem_size) {
+ err = desc->xcode(desc, elem);
+ if (err)
+ goto out;
+ copied = 0;
+ }
+ }
+ }
+ if (avail_here) {
+ kunmap(*ppages);
+ ppages++;
+ c = kmap(*ppages);
+ }
+
+ avail_page = min(avail_here,
+ (unsigned int) PAGE_CACHE_SIZE);
+ }
+ base = buf->page_len; /* align to start of tail */
+ }
+
+ /* process tail */
+ base -= buf->page_len;
+ if (todo) {
+ c = buf->tail->iov_base + base;
+ if (copied) {
+ unsigned int l = desc->elem_size - copied;
+
+ if (encode)
+ memcpy(c, elem + copied, l);
+ else {
+ memcpy(elem + copied, c, l);
+ err = desc->xcode(desc, elem);
+ if (err)
+ goto out;
+ }
+ todo -= l;
+ c += l;
+ }
+ while (todo) {
+ err = desc->xcode(desc, c);
+ if (err)
+ goto out;
+ c += desc->elem_size;
+ todo -= desc->elem_size;
+ }
+ }
+ err = 0;
+
+out:
+ if (elem)
+ kfree(elem);
+ if (ppages)
+ kunmap(*ppages);
+ return err;
+}
+
+int
+xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
+ struct xdr_array2_desc *desc)
+{
+ if (base >= buf->len)
+ return -EINVAL;
+
+ return xdr_xcode_array2(buf, base, desc, 0);
+}
+
+int
+xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
+ struct xdr_array2_desc *desc)
+{
+ if ((unsigned long) base + 4 + desc->array_len * desc->elem_size >
+ buf->head->iov_len + buf->page_len + buf->tail->iov_len)
+ return -EINVAL;
+
+ return xdr_xcode_array2(buf, base, desc, 1);
+}
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index c74a6bb9407..eca92405948 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -569,8 +569,11 @@ void xprt_connect(struct rpc_task *task)
if (xprt->sock != NULL)
schedule_delayed_work(&xprt->sock_connect,
RPC_REESTABLISH_TIMEOUT);
- else
+ else {
schedule_work(&xprt->sock_connect);
+ if (!RPC_IS_ASYNC(task))
+ flush_scheduled_work();
+ }
}
return;
out_write:
@@ -725,7 +728,8 @@ csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
goto no_checksum;
desc.csum = csum_partial(skb->data, desc.offset, skb->csum);
- xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_and_csum_bits);
+ if (xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_and_csum_bits) < 0)
+ return -1;
if (desc.offset != skb->len) {
unsigned int csum2;
csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0);
@@ -737,7 +741,8 @@ csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
return -1;
return 0;
no_checksum:
- xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits);
+ if (xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits) < 0)
+ return -1;
if (desc.count)
return -1;
return 0;
@@ -821,10 +826,15 @@ tcp_copy_data(skb_reader_t *desc, void *p, size_t len)
{
if (len > desc->count)
len = desc->count;
- if (skb_copy_bits(desc->skb, desc->offset, p, len))
+ if (skb_copy_bits(desc->skb, desc->offset, p, len)) {
+ dprintk("RPC: failed to copy %zu bytes from skb. %zu bytes remain\n",
+ len, desc->count);
return 0;
+ }
desc->offset += len;
desc->count -= len;
+ dprintk("RPC: copied %zu bytes from skb. %zu bytes remain\n",
+ len, desc->count);
return len;
}
@@ -863,6 +873,8 @@ tcp_read_fraghdr(struct rpc_xprt *xprt, skb_reader_t *desc)
static void
tcp_check_recm(struct rpc_xprt *xprt)
{
+ dprintk("RPC: xprt = %p, tcp_copied = %lu, tcp_offset = %u, tcp_reclen = %u, tcp_flags = %lx\n",
+ xprt, xprt->tcp_copied, xprt->tcp_offset, xprt->tcp_reclen, xprt->tcp_flags);
if (xprt->tcp_offset == xprt->tcp_reclen) {
xprt->tcp_flags |= XPRT_COPY_RECM;
xprt->tcp_offset = 0;
@@ -907,6 +919,7 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc)
struct rpc_rqst *req;
struct xdr_buf *rcvbuf;
size_t len;
+ ssize_t r;
/* Find and lock the request corresponding to this xid */
spin_lock(&xprt->sock_lock);
@@ -927,15 +940,40 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc)
len = xprt->tcp_reclen - xprt->tcp_offset;
memcpy(&my_desc, desc, sizeof(my_desc));
my_desc.count = len;
- xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied,
+ r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied,
&my_desc, tcp_copy_data);
- desc->count -= len;
- desc->offset += len;
+ desc->count -= r;
+ desc->offset += r;
} else
- xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied,
+ r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied,
desc, tcp_copy_data);
- xprt->tcp_copied += len;
- xprt->tcp_offset += len;
+
+ if (r > 0) {
+ xprt->tcp_copied += r;
+ xprt->tcp_offset += r;
+ }
+ if (r != len) {
+ /* Error when copying to the receive buffer,
+ * usually because we weren't able to allocate
+ * additional buffer pages. All we can do now
+ * is turn off XPRT_COPY_DATA, so the request
+ * will not receive any additional updates,
+ * and time out.
+ * Any remaining data from this record will
+ * be discarded.
+ */
+ xprt->tcp_flags &= ~XPRT_COPY_DATA;
+ dprintk("RPC: XID %08x truncated request\n",
+ ntohl(xprt->tcp_xid));
+ dprintk("RPC: xprt = %p, tcp_copied = %lu, tcp_offset = %u, tcp_reclen = %u\n",
+ xprt, xprt->tcp_copied, xprt->tcp_offset, xprt->tcp_reclen);
+ goto out;
+ }
+
+ dprintk("RPC: XID %08x read %u bytes\n",
+ ntohl(xprt->tcp_xid), r);
+ dprintk("RPC: xprt = %p, tcp_copied = %lu, tcp_offset = %u, tcp_reclen = %u\n",
+ xprt, xprt->tcp_copied, xprt->tcp_offset, xprt->tcp_reclen);
if (xprt->tcp_copied == req->rq_private_buf.buflen)
xprt->tcp_flags &= ~XPRT_COPY_DATA;
@@ -944,6 +982,7 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc)
xprt->tcp_flags &= ~XPRT_COPY_DATA;
}
+out:
if (!(xprt->tcp_flags & XPRT_COPY_DATA)) {
dprintk("RPC: %4d received reply complete\n",
req->rq_task->tk_pid);
@@ -967,6 +1006,7 @@ tcp_read_discard(struct rpc_xprt *xprt, skb_reader_t *desc)
desc->count -= len;
desc->offset += len;
xprt->tcp_offset += len;
+ dprintk("RPC: discarded %u bytes\n", len);
tcp_check_recm(xprt);
}
@@ -1064,8 +1104,7 @@ tcp_state_change(struct sock *sk)
case TCP_SYN_RECV:
break;
default:
- if (xprt_test_and_clear_connected(xprt))
- rpc_wake_up_status(&xprt->pending, -ENOTCONN);
+ xprt_disconnect(xprt);
break;
}
out:
@@ -1203,6 +1242,8 @@ xprt_transmit(struct rpc_task *task)
list_add_tail(&req->rq_list, &xprt->recv);
spin_unlock_bh(&xprt->sock_lock);
xprt_reset_majortimeo(req);
+ /* Turn off autodisconnect */
+ del_singleshot_timer_sync(&xprt->timer);
}
} else if (!req->rq_bytes_sent)
return;
@@ -1333,8 +1374,6 @@ xprt_reserve(struct rpc_task *task)
spin_lock(&xprt->xprt_lock);
do_xprt_reserve(task);
spin_unlock(&xprt->xprt_lock);
- if (task->tk_rqstp)
- del_timer_sync(&xprt->timer);
}
}
@@ -1649,6 +1688,10 @@ xprt_shutdown(struct rpc_xprt *xprt)
rpc_wake_up(&xprt->backlog);
wake_up(&xprt->cong_wait);
del_timer_sync(&xprt->timer);
+
+ /* synchronously wait for connect worker to finish */
+ cancel_delayed_work(&xprt->sock_connect);
+ flush_scheduled_work();
}
/*