Merge branch 'for-3.2' of git://linux-nfs.org/~bfields/linux
* 'for-3.2' of git://linux-nfs.org/~bfields/linux: (103 commits) nfs41: implement DESTROY_CLIENTID operation nfsd4: typo logical vs bitwise negate for want_mask nfsd4: allow NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL | NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED nfsd4: seq->status_flags may be used unitialized nfsd41: use SEQ4_STATUS_BACKCHANNEL_FAULT when cb_sequence is invalid nfsd4: implement new 4.1 open reclaim types nfsd4: remove unneeded CLAIM_DELEGATE_CUR workaround nfsd4: warn on open failure after create nfsd4: preallocate open stateid in process_open1() nfsd4: do idr preallocation with stateid allocation nfsd4: preallocate nfs4_file in process_open1() nfsd4: clean up open owners on OPEN failure nfsd4: simplify process_open1 logic nfsd4: make is_open_owner boolean nfsd4: centralize renew_client() calls nfsd4: typo logical vs bitwise negate nfs: fix bug about IPv6 address scope checking nfsd4: more robust ignoring of WANT bits in OPEN nfsd4: move name-length checks to xdr nfsd4: move access/deny validity checks to xdr code ...
This commit is contained in:
commit
1442d1678c
|
@ -51,8 +51,6 @@
|
|||
#define F_EXLCK 16 /* or 3 */
|
||||
#define F_SHLCK 32 /* or 4 */
|
||||
|
||||
#define F_INPROGRESS 64
|
||||
|
||||
#include <asm-generic/fcntl.h>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include <linux/dirent.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/highuid.h>
|
||||
#include <linux/nfsd/syscall.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/tsacct_kern.h>
|
||||
|
|
|
@ -316,14 +316,8 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
|
|||
struct hlist_node *pos;
|
||||
struct nlm_host *host = NULL;
|
||||
struct nsm_handle *nsm = NULL;
|
||||
struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
};
|
||||
struct sockaddr_in6 sin6 = {
|
||||
.sin6_family = AF_INET6,
|
||||
};
|
||||
struct sockaddr *src_sap;
|
||||
size_t src_len = rqstp->rq_addrlen;
|
||||
struct sockaddr *src_sap = svc_daddr(rqstp);
|
||||
size_t src_len = rqstp->rq_daddrlen;
|
||||
struct nlm_lookup_host_info ni = {
|
||||
.server = 1,
|
||||
.sap = svc_addr(rqstp),
|
||||
|
@ -340,21 +334,6 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
|
|||
|
||||
mutex_lock(&nlm_host_mutex);
|
||||
|
||||
switch (ni.sap->sa_family) {
|
||||
case AF_INET:
|
||||
sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
|
||||
src_sap = (struct sockaddr *)&sin;
|
||||
break;
|
||||
case AF_INET6:
|
||||
ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
|
||||
src_sap = (struct sockaddr *)&sin6;
|
||||
break;
|
||||
default:
|
||||
dprintk("lockd: %s failed; unrecognized address family\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (time_after_eq(jiffies, next_gc))
|
||||
nlm_gc_hosts();
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ int lockd_up(void)
|
|||
/*
|
||||
* Create the kernel thread and wait for it to start.
|
||||
*/
|
||||
nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
|
||||
nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
|
||||
if (IS_ERR(nlmsvc_rqst)) {
|
||||
error = PTR_ERR(nlmsvc_rqst);
|
||||
nlmsvc_rqst = NULL;
|
||||
|
|
251
fs/locks.c
251
fs/locks.c
|
@ -133,6 +133,20 @@
|
|||
#define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
|
||||
#define IS_LEASE(fl) (fl->fl_flags & FL_LEASE)
|
||||
|
||||
static bool lease_breaking(struct file_lock *fl)
|
||||
{
|
||||
return fl->fl_flags & (FL_UNLOCK_PENDING | FL_DOWNGRADE_PENDING);
|
||||
}
|
||||
|
||||
static int target_leasetype(struct file_lock *fl)
|
||||
{
|
||||
if (fl->fl_flags & FL_UNLOCK_PENDING)
|
||||
return F_UNLCK;
|
||||
if (fl->fl_flags & FL_DOWNGRADE_PENDING)
|
||||
return F_RDLCK;
|
||||
return fl->fl_type;
|
||||
}
|
||||
|
||||
int leases_enable = 1;
|
||||
int lease_break_time = 45;
|
||||
|
||||
|
@ -1119,6 +1133,17 @@ int locks_mandatory_area(int read_write, struct inode *inode,
|
|||
|
||||
EXPORT_SYMBOL(locks_mandatory_area);
|
||||
|
||||
static void lease_clear_pending(struct file_lock *fl, int arg)
|
||||
{
|
||||
switch (arg) {
|
||||
case F_UNLCK:
|
||||
fl->fl_flags &= ~FL_UNLOCK_PENDING;
|
||||
/* fall through: */
|
||||
case F_RDLCK:
|
||||
fl->fl_flags &= ~FL_DOWNGRADE_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
/* We already had a lease on this file; just change its type */
|
||||
int lease_modify(struct file_lock **before, int arg)
|
||||
{
|
||||
|
@ -1127,6 +1152,7 @@ int lease_modify(struct file_lock **before, int arg)
|
|||
|
||||
if (error)
|
||||
return error;
|
||||
lease_clear_pending(fl, arg);
|
||||
locks_wake_up_blocks(fl);
|
||||
if (arg == F_UNLCK)
|
||||
locks_delete_lock(before);
|
||||
|
@ -1135,19 +1161,25 @@ int lease_modify(struct file_lock **before, int arg)
|
|||
|
||||
EXPORT_SYMBOL(lease_modify);
|
||||
|
||||
static bool past_time(unsigned long then)
|
||||
{
|
||||
if (!then)
|
||||
/* 0 is a special value meaning "this never expires": */
|
||||
return false;
|
||||
return time_after(jiffies, then);
|
||||
}
|
||||
|
||||
static void time_out_leases(struct inode *inode)
|
||||
{
|
||||
struct file_lock **before;
|
||||
struct file_lock *fl;
|
||||
|
||||
before = &inode->i_flock;
|
||||
while ((fl = *before) && IS_LEASE(fl) && (fl->fl_type & F_INPROGRESS)) {
|
||||
if ((fl->fl_break_time == 0)
|
||||
|| time_before(jiffies, fl->fl_break_time)) {
|
||||
before = &fl->fl_next;
|
||||
continue;
|
||||
}
|
||||
lease_modify(before, fl->fl_type & ~F_INPROGRESS);
|
||||
while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) {
|
||||
if (past_time(fl->fl_downgrade_time))
|
||||
lease_modify(before, F_RDLCK);
|
||||
if (past_time(fl->fl_break_time))
|
||||
lease_modify(before, F_UNLCK);
|
||||
if (fl == *before) /* lease_modify may have freed fl */
|
||||
before = &fl->fl_next;
|
||||
}
|
||||
|
@ -1165,7 +1197,7 @@ static void time_out_leases(struct inode *inode)
|
|||
*/
|
||||
int __break_lease(struct inode *inode, unsigned int mode)
|
||||
{
|
||||
int error = 0, future;
|
||||
int error = 0;
|
||||
struct file_lock *new_fl, *flock;
|
||||
struct file_lock *fl;
|
||||
unsigned long break_time;
|
||||
|
@ -1182,24 +1214,13 @@ int __break_lease(struct inode *inode, unsigned int mode)
|
|||
if ((flock == NULL) || !IS_LEASE(flock))
|
||||
goto out;
|
||||
|
||||
if (!locks_conflict(flock, new_fl))
|
||||
goto out;
|
||||
|
||||
for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next)
|
||||
if (fl->fl_owner == current->files)
|
||||
i_have_this_lease = 1;
|
||||
|
||||
if (want_write) {
|
||||
/* If we want write access, we have to revoke any lease. */
|
||||
future = F_UNLCK | F_INPROGRESS;
|
||||
} else if (flock->fl_type & F_INPROGRESS) {
|
||||
/* If the lease is already being broken, we just leave it */
|
||||
future = flock->fl_type;
|
||||
} else if (flock->fl_type & F_WRLCK) {
|
||||
/* Downgrade the exclusive lease to a read-only lease. */
|
||||
future = F_RDLCK | F_INPROGRESS;
|
||||
} else {
|
||||
/* the existing lease was read-only, so we can read too. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (IS_ERR(new_fl) && !i_have_this_lease
|
||||
&& ((mode & O_NONBLOCK) == 0)) {
|
||||
error = PTR_ERR(new_fl);
|
||||
|
@ -1214,12 +1235,18 @@ int __break_lease(struct inode *inode, unsigned int mode)
|
|||
}
|
||||
|
||||
for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
|
||||
if (fl->fl_type != future) {
|
||||
fl->fl_type = future;
|
||||
if (want_write) {
|
||||
if (fl->fl_flags & FL_UNLOCK_PENDING)
|
||||
continue;
|
||||
fl->fl_flags |= FL_UNLOCK_PENDING;
|
||||
fl->fl_break_time = break_time;
|
||||
/* lease must have lmops break callback */
|
||||
fl->fl_lmops->lm_break(fl);
|
||||
} else {
|
||||
if (lease_breaking(flock))
|
||||
continue;
|
||||
fl->fl_flags |= FL_DOWNGRADE_PENDING;
|
||||
fl->fl_downgrade_time = break_time;
|
||||
}
|
||||
fl->fl_lmops->lm_break(fl);
|
||||
}
|
||||
|
||||
if (i_have_this_lease || (mode & O_NONBLOCK)) {
|
||||
|
@ -1243,10 +1270,13 @@ restart:
|
|||
if (error >= 0) {
|
||||
if (error == 0)
|
||||
time_out_leases(inode);
|
||||
/* Wait for the next lease that has not been broken yet */
|
||||
/*
|
||||
* Wait for the next conflicting lease that has not been
|
||||
* broken yet
|
||||
*/
|
||||
for (flock = inode->i_flock; flock && IS_LEASE(flock);
|
||||
flock = flock->fl_next) {
|
||||
if (flock->fl_type & F_INPROGRESS)
|
||||
if (locks_conflict(new_fl, flock))
|
||||
goto restart;
|
||||
}
|
||||
error = 0;
|
||||
|
@ -1314,7 +1344,7 @@ int fcntl_getlease(struct file *filp)
|
|||
for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl);
|
||||
fl = fl->fl_next) {
|
||||
if (fl->fl_file == filp) {
|
||||
type = fl->fl_type & ~F_INPROGRESS;
|
||||
type = target_leasetype(fl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1322,6 +1352,87 @@ int fcntl_getlease(struct file *filp)
|
|||
return type;
|
||||
}
|
||||
|
||||
int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
|
||||
{
|
||||
struct file_lock *fl, **before, **my_before = NULL, *lease;
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int error;
|
||||
|
||||
lease = *flp;
|
||||
|
||||
error = -EAGAIN;
|
||||
if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
|
||||
goto out;
|
||||
if ((arg == F_WRLCK)
|
||||
&& ((dentry->d_count > 1)
|
||||
|| (atomic_read(&inode->i_count) > 1)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* At this point, we know that if there is an exclusive
|
||||
* lease on this file, then we hold it on this filp
|
||||
* (otherwise our open of this file would have blocked).
|
||||
* And if we are trying to acquire an exclusive lease,
|
||||
* then the file is not open by anyone (including us)
|
||||
* except for this filp.
|
||||
*/
|
||||
error = -EAGAIN;
|
||||
for (before = &inode->i_flock;
|
||||
((fl = *before) != NULL) && IS_LEASE(fl);
|
||||
before = &fl->fl_next) {
|
||||
if (fl->fl_file == filp) {
|
||||
my_before = before;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* No exclusive leases if someone else has a lease on
|
||||
* this file:
|
||||
*/
|
||||
if (arg == F_WRLCK)
|
||||
goto out;
|
||||
/*
|
||||
* Modifying our existing lease is OK, but no getting a
|
||||
* new lease if someone else is opening for write:
|
||||
*/
|
||||
if (fl->fl_flags & FL_UNLOCK_PENDING)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (my_before != NULL) {
|
||||
error = lease->fl_lmops->lm_change(my_before, arg);
|
||||
if (!error)
|
||||
*flp = *my_before;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = -EINVAL;
|
||||
if (!leases_enable)
|
||||
goto out;
|
||||
|
||||
locks_insert_lock(before, lease);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
int generic_delete_lease(struct file *filp, struct file_lock **flp)
|
||||
{
|
||||
struct file_lock *fl, **before;
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
for (before = &inode->i_flock;
|
||||
((fl = *before) != NULL) && IS_LEASE(fl);
|
||||
before = &fl->fl_next) {
|
||||
if (fl->fl_file != filp)
|
||||
continue;
|
||||
return (*flp)->fl_lmops->lm_change(before, F_UNLCK);
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* generic_setlease - sets a lease on an open file
|
||||
* @filp: file pointer
|
||||
|
@ -1335,85 +1446,31 @@ int fcntl_getlease(struct file *filp)
|
|||
*/
|
||||
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
|
||||
{
|
||||
struct file_lock *fl, **before, **my_before = NULL, *lease;
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int error, rdlease_count = 0, wrlease_count = 0;
|
||||
int error;
|
||||
|
||||
lease = *flp;
|
||||
|
||||
error = -EACCES;
|
||||
if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE))
|
||||
goto out;
|
||||
error = -EINVAL;
|
||||
return -EACCES;
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
error = security_file_lock(filp, arg);
|
||||
if (error)
|
||||
goto out;
|
||||
return error;
|
||||
|
||||
time_out_leases(inode);
|
||||
|
||||
BUG_ON(!(*flp)->fl_lmops->lm_break);
|
||||
|
||||
if (arg != F_UNLCK) {
|
||||
error = -EAGAIN;
|
||||
if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
|
||||
goto out;
|
||||
if ((arg == F_WRLCK)
|
||||
&& ((dentry->d_count > 1)
|
||||
|| (atomic_read(&inode->i_count) > 1)))
|
||||
goto out;
|
||||
switch (arg) {
|
||||
case F_UNLCK:
|
||||
return generic_delete_lease(filp, flp);
|
||||
case F_RDLCK:
|
||||
case F_WRLCK:
|
||||
return generic_add_lease(filp, arg, flp);
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, we know that if there is an exclusive
|
||||
* lease on this file, then we hold it on this filp
|
||||
* (otherwise our open of this file would have blocked).
|
||||
* And if we are trying to acquire an exclusive lease,
|
||||
* then the file is not open by anyone (including us)
|
||||
* except for this filp.
|
||||
*/
|
||||
for (before = &inode->i_flock;
|
||||
((fl = *before) != NULL) && IS_LEASE(fl);
|
||||
before = &fl->fl_next) {
|
||||
if (fl->fl_file == filp)
|
||||
my_before = before;
|
||||
else if (fl->fl_type == (F_INPROGRESS | F_UNLCK))
|
||||
/*
|
||||
* Someone is in the process of opening this
|
||||
* file for writing so we may not take an
|
||||
* exclusive lease on it.
|
||||
*/
|
||||
wrlease_count++;
|
||||
else
|
||||
rdlease_count++;
|
||||
}
|
||||
|
||||
error = -EAGAIN;
|
||||
if ((arg == F_RDLCK && (wrlease_count > 0)) ||
|
||||
(arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0)))
|
||||
goto out;
|
||||
|
||||
if (my_before != NULL) {
|
||||
error = lease->fl_lmops->lm_change(my_before, arg);
|
||||
if (!error)
|
||||
*flp = *my_before;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (arg == F_UNLCK)
|
||||
goto out;
|
||||
|
||||
error = -EINVAL;
|
||||
if (!leases_enable)
|
||||
goto out;
|
||||
|
||||
locks_insert_lock(before, lease);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(generic_setlease);
|
||||
|
||||
|
@ -2126,7 +2183,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
|
|||
}
|
||||
} else if (IS_LEASE(fl)) {
|
||||
seq_printf(f, "LEASE ");
|
||||
if (fl->fl_type & F_INPROGRESS)
|
||||
if (lease_breaking(fl))
|
||||
seq_printf(f, "BREAKING ");
|
||||
else if (fl->fl_file)
|
||||
seq_printf(f, "ACTIVE ");
|
||||
|
@ -2142,7 +2199,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
|
|||
: (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
|
||||
} else {
|
||||
seq_printf(f, "%s ",
|
||||
(fl->fl_type & F_INPROGRESS)
|
||||
(lease_breaking(fl))
|
||||
? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
|
||||
: (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ nfs4_callback_up(struct svc_serv *serv)
|
|||
else
|
||||
goto out_err;
|
||||
|
||||
return svc_prepare_thread(serv, &serv->sv_pools[0]);
|
||||
return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
|
||||
|
||||
out_err:
|
||||
if (ret == 0)
|
||||
|
@ -199,7 +199,7 @@ nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
|
|||
INIT_LIST_HEAD(&serv->sv_cb_list);
|
||||
spin_lock_init(&serv->sv_cb_lock);
|
||||
init_waitqueue_head(&serv->sv_cb_waitq);
|
||||
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
|
||||
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
|
||||
if (IS_ERR(rqstp)) {
|
||||
svc_xprt_put(serv->sv_bc_xprt);
|
||||
serv->sv_bc_xprt = NULL;
|
||||
|
|
|
@ -336,11 +336,12 @@ static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1,
|
|||
const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1;
|
||||
const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2;
|
||||
|
||||
if (ipv6_addr_scope(&sin1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL &&
|
||||
sin1->sin6_scope_id != sin2->sin6_scope_id)
|
||||
if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr))
|
||||
return 0;
|
||||
else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL)
|
||||
return sin1->sin6_scope_id == sin2->sin6_scope_id;
|
||||
|
||||
return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr);
|
||||
return 1;
|
||||
}
|
||||
#else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */
|
||||
static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1,
|
||||
|
|
|
@ -13,30 +13,6 @@
|
|||
|
||||
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_MANAGER_RUNNING = 0,
|
||||
NFS4CLNT_CHECK_LEASE,
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/exportfs.h>
|
||||
|
||||
#include <linux/nfsd/syscall.h>
|
||||
#include <net/ipv6.h>
|
||||
|
||||
#include "nfsd.h"
|
||||
|
@ -318,7 +317,6 @@ static void svc_export_put(struct kref *ref)
|
|||
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
|
||||
path_put(&exp->ex_path);
|
||||
auth_domain_put(exp->ex_client);
|
||||
kfree(exp->ex_pathname);
|
||||
nfsd4_fslocs_free(&exp->ex_fslocs);
|
||||
kfree(exp);
|
||||
}
|
||||
|
@ -528,11 +526,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
|
|||
|
||||
exp.ex_client = dom;
|
||||
|
||||
err = -ENOMEM;
|
||||
exp.ex_pathname = kstrdup(buf, GFP_KERNEL);
|
||||
if (!exp.ex_pathname)
|
||||
goto out2;
|
||||
|
||||
/* expiry */
|
||||
err = -EINVAL;
|
||||
exp.h.expiry_time = get_expiry(&mesg);
|
||||
|
@ -613,8 +606,6 @@ out4:
|
|||
nfsd4_fslocs_free(&exp.ex_fslocs);
|
||||
kfree(exp.ex_uuid);
|
||||
out3:
|
||||
kfree(exp.ex_pathname);
|
||||
out2:
|
||||
path_put(&exp.ex_path);
|
||||
out1:
|
||||
auth_domain_put(dom);
|
||||
|
@ -678,7 +669,6 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem)
|
|||
new->ex_client = item->ex_client;
|
||||
new->ex_path.dentry = dget(item->ex_path.dentry);
|
||||
new->ex_path.mnt = mntget(item->ex_path.mnt);
|
||||
new->ex_pathname = NULL;
|
||||
new->ex_fslocs.locations = NULL;
|
||||
new->ex_fslocs.locations_count = 0;
|
||||
new->ex_fslocs.migrated = 0;
|
||||
|
@ -696,8 +686,6 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem)
|
|||
new->ex_fsid = item->ex_fsid;
|
||||
new->ex_uuid = item->ex_uuid;
|
||||
item->ex_uuid = NULL;
|
||||
new->ex_pathname = item->ex_pathname;
|
||||
item->ex_pathname = NULL;
|
||||
new->ex_fslocs.locations = item->ex_fslocs.locations;
|
||||
item->ex_fslocs.locations = NULL;
|
||||
new->ex_fslocs.locations_count = item->ex_fslocs.locations_count;
|
||||
|
@ -1010,7 +998,7 @@ rqst_exp_parent(struct svc_rqst *rqstp, struct path *path)
|
|||
return exp;
|
||||
}
|
||||
|
||||
static struct svc_export *find_fsidzero_export(struct svc_rqst *rqstp)
|
||||
struct svc_export *rqst_find_fsidzero_export(struct svc_rqst *rqstp)
|
||||
{
|
||||
u32 fsidv[2];
|
||||
|
||||
|
@ -1030,7 +1018,7 @@ exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
|||
struct svc_export *exp;
|
||||
__be32 rv;
|
||||
|
||||
exp = find_fsidzero_export(rqstp);
|
||||
exp = rqst_find_fsidzero_export(rqstp);
|
||||
if (IS_ERR(exp))
|
||||
return nfserrno(PTR_ERR(exp));
|
||||
rv = fh_compose(fhp, exp, exp->ex_path.dentry, NULL);
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PROC
|
||||
|
||||
static void nfsd4_mark_cb_fault(struct nfs4_client *, int reason);
|
||||
|
||||
#define NFSPROC4_CB_NULL 0
|
||||
#define NFSPROC4_CB_COMPOUND 1
|
||||
|
||||
|
@ -351,7 +353,7 @@ static void encode_cb_recall4args(struct xdr_stream *xdr,
|
|||
__be32 *p;
|
||||
|
||||
encode_nfs_cb_opnum4(xdr, OP_CB_RECALL);
|
||||
encode_stateid4(xdr, &dp->dl_stateid);
|
||||
encode_stateid4(xdr, &dp->dl_stid.sc_stateid);
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
*p++ = xdr_zero; /* truncate */
|
||||
|
@ -460,6 +462,8 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
|
|||
*/
|
||||
status = 0;
|
||||
out:
|
||||
if (status)
|
||||
nfsd4_mark_cb_fault(cb->cb_clp, status);
|
||||
return status;
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
|
@ -686,6 +690,12 @@ static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
|
|||
warn_no_callback_path(clp, reason);
|
||||
}
|
||||
|
||||
static void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason)
|
||||
{
|
||||
clp->cl_cb_state = NFSD4_CB_FAULT;
|
||||
warn_no_callback_path(clp, reason);
|
||||
}
|
||||
|
||||
static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
|
||||
|
@ -787,7 +797,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
|
|||
{
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct nfs4_client *clp = dp->dl_stid.sc_client;
|
||||
u32 minorversion = clp->cl_minorversion;
|
||||
|
||||
cb->cb_minorversion = minorversion;
|
||||
|
@ -809,7 +819,7 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
|
|||
{
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct nfs4_client *clp = dp->dl_stid.sc_client;
|
||||
|
||||
dprintk("%s: minorversion=%d\n", __func__,
|
||||
clp->cl_minorversion);
|
||||
|
@ -832,7 +842,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
|
|||
{
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct nfs4_client *clp = dp->dl_stid.sc_client;
|
||||
struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
|
||||
|
||||
nfsd4_cb_done(task, calldata);
|
||||
|
@ -1006,7 +1016,7 @@ void nfsd4_do_callback_rpc(struct work_struct *w)
|
|||
void nfsd4_cb_recall(struct nfs4_delegation *dp)
|
||||
{
|
||||
struct nfsd4_callback *cb = &dp->dl_recall;
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct nfs4_client *clp = dp->dl_stid.sc_client;
|
||||
|
||||
dp->dl_retries = 1;
|
||||
cb->cb_op = dp;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/file.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "idmap.h"
|
||||
#include "cache.h"
|
||||
#include "xdr4.h"
|
||||
#include "vfs.h"
|
||||
|
@ -156,6 +157,8 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
|
|||
!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE))
|
||||
return nfserr_inval;
|
||||
|
||||
accmode |= NFSD_MAY_READ_IF_EXEC;
|
||||
|
||||
if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
|
||||
accmode |= NFSD_MAY_READ;
|
||||
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
|
||||
|
@ -168,12 +171,29 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
|
|||
return status;
|
||||
}
|
||||
|
||||
static __be32 nfsd_check_obj_isreg(struct svc_fh *fh)
|
||||
{
|
||||
umode_t mode = fh->fh_dentry->d_inode->i_mode;
|
||||
|
||||
if (S_ISREG(mode))
|
||||
return nfs_ok;
|
||||
if (S_ISDIR(mode))
|
||||
return nfserr_isdir;
|
||||
/*
|
||||
* Using err_symlink as our catch-all case may look odd; but
|
||||
* there's no other obvious error for this case in 4.0, and we
|
||||
* happen to know that it will cause the linux v4 client to do
|
||||
* the right thing on attempts to open something other than a
|
||||
* regular file.
|
||||
*/
|
||||
return nfserr_symlink;
|
||||
}
|
||||
|
||||
static __be32
|
||||
do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
|
||||
{
|
||||
struct svc_fh resfh;
|
||||
__be32 status;
|
||||
int created = 0;
|
||||
|
||||
fh_init(&resfh, NFS4_FHSIZE);
|
||||
open->op_truncate = 0;
|
||||
|
@ -202,7 +222,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
|
|||
open->op_fname.len, &open->op_iattr,
|
||||
&resfh, open->op_createmode,
|
||||
(u32 *)open->op_verf.data,
|
||||
&open->op_truncate, &created);
|
||||
&open->op_truncate, &open->op_created);
|
||||
|
||||
/*
|
||||
* Following rfc 3530 14.2.16, use the returned bitmask
|
||||
|
@ -216,6 +236,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
|
|||
status = nfsd_lookup(rqstp, current_fh,
|
||||
open->op_fname.data, open->op_fname.len, &resfh);
|
||||
fh_unlock(current_fh);
|
||||
if (status)
|
||||
goto out;
|
||||
status = nfsd_check_obj_isreg(&resfh);
|
||||
}
|
||||
if (status)
|
||||
goto out;
|
||||
|
@ -227,9 +250,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
|
|||
fh_dup2(current_fh, &resfh);
|
||||
|
||||
/* set reply cache */
|
||||
fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh,
|
||||
fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh,
|
||||
&resfh.fh_handle);
|
||||
if (!created)
|
||||
if (!open->op_created)
|
||||
status = do_open_permission(rqstp, current_fh, open,
|
||||
NFSD_MAY_NOP);
|
||||
|
||||
|
@ -254,7 +277,7 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_
|
|||
memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info));
|
||||
|
||||
/* set replay cache */
|
||||
fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh,
|
||||
fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh,
|
||||
¤t_fh->fh_handle);
|
||||
|
||||
open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) &&
|
||||
|
@ -283,14 +306,18 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
__be32 status;
|
||||
struct nfsd4_compoundres *resp;
|
||||
|
||||
dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n",
|
||||
dprintk("NFSD: nfsd4_open filename %.*s op_openowner %p\n",
|
||||
(int)open->op_fname.len, open->op_fname.data,
|
||||
open->op_stateowner);
|
||||
open->op_openowner);
|
||||
|
||||
/* This check required by spec. */
|
||||
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
|
||||
return nfserr_inval;
|
||||
|
||||
/* We don't yet support WANT bits: */
|
||||
open->op_share_access &= NFS4_SHARE_ACCESS_MASK;
|
||||
|
||||
open->op_created = 0;
|
||||
/*
|
||||
* RFC5661 18.51.3
|
||||
* Before RECLAIM_COMPLETE done, server should deny new lock
|
||||
|
@ -309,7 +336,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
resp = rqstp->rq_resp;
|
||||
status = nfsd4_process_open1(&resp->cstate, open);
|
||||
if (status == nfserr_replay_me) {
|
||||
struct nfs4_replay *rp = &open->op_stateowner->so_replay;
|
||||
struct nfs4_replay *rp = &open->op_openowner->oo_owner.so_replay;
|
||||
fh_put(&cstate->current_fh);
|
||||
fh_copy_shallow(&cstate->current_fh.fh_handle,
|
||||
&rp->rp_openfh);
|
||||
|
@ -339,32 +366,23 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
switch (open->op_claim_type) {
|
||||
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
|
||||
case NFS4_OPEN_CLAIM_NULL:
|
||||
/*
|
||||
* (1) set CURRENT_FH to the file being opened,
|
||||
* creating it if necessary, (2) set open->op_cinfo,
|
||||
* (3) set open->op_truncate if the file is to be
|
||||
* truncated after opening, (4) do permission checking.
|
||||
*/
|
||||
status = do_open_lookup(rqstp, &cstate->current_fh,
|
||||
open);
|
||||
if (status)
|
||||
goto out;
|
||||
break;
|
||||
case NFS4_OPEN_CLAIM_PREVIOUS:
|
||||
open->op_stateowner->so_confirmed = 1;
|
||||
/*
|
||||
* The CURRENT_FH is already set to the file being
|
||||
* opened. (1) set open->op_cinfo, (2) set
|
||||
* open->op_truncate if the file is to be truncated
|
||||
* after opening, (3) do permission checking.
|
||||
*/
|
||||
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
|
||||
case NFS4_OPEN_CLAIM_FH:
|
||||
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
||||
status = do_open_fhandle(rqstp, &cstate->current_fh,
|
||||
open);
|
||||
if (status)
|
||||
goto out;
|
||||
break;
|
||||
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
||||
case NFS4_OPEN_CLAIM_DELEGATE_PREV:
|
||||
open->op_stateowner->so_confirmed = 1;
|
||||
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
|
||||
dprintk("NFSD: unsupported OPEN claim type %d\n",
|
||||
open->op_claim_type);
|
||||
status = nfserr_notsupp;
|
||||
|
@ -381,12 +399,13 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
* set, (2) sets open->op_stateid, (3) sets open->op_delegation.
|
||||
*/
|
||||
status = nfsd4_process_open2(rqstp, &cstate->current_fh, open);
|
||||
WARN_ON(status && open->op_created);
|
||||
out:
|
||||
if (open->op_stateowner) {
|
||||
nfs4_get_stateowner(open->op_stateowner);
|
||||
cstate->replay_owner = open->op_stateowner;
|
||||
}
|
||||
nfs4_unlock_state();
|
||||
nfsd4_cleanup_open_state(open, status);
|
||||
if (open->op_openowner)
|
||||
cstate->replay_owner = &open->op_openowner->oo_owner;
|
||||
else
|
||||
nfs4_unlock_state();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -467,17 +486,12 @@ static __be32
|
|||
nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
struct nfsd4_commit *commit)
|
||||
{
|
||||
__be32 status;
|
||||
|
||||
u32 *p = (u32 *)commit->co_verf.data;
|
||||
*p++ = nfssvc_boot.tv_sec;
|
||||
*p++ = nfssvc_boot.tv_usec;
|
||||
|
||||
status = nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset,
|
||||
return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset,
|
||||
commit->co_count);
|
||||
if (status == nfserr_symlink)
|
||||
status = nfserr_inval;
|
||||
return status;
|
||||
}
|
||||
|
||||
static __be32
|
||||
|
@ -492,8 +506,6 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
|
||||
status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR,
|
||||
NFSD_MAY_CREATE);
|
||||
if (status == nfserr_symlink)
|
||||
status = nfserr_notdir;
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
|
@ -691,7 +703,7 @@ nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
readdir->rd_bmval[1] &= nfsd_suppattrs1(cstate->minorversion);
|
||||
readdir->rd_bmval[2] &= nfsd_suppattrs2(cstate->minorversion);
|
||||
|
||||
if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) ||
|
||||
if ((cookie == 1) || (cookie == 2) ||
|
||||
(cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE)))
|
||||
return nfserr_bad_cookie;
|
||||
|
||||
|
@ -719,8 +731,6 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
return nfserr_grace;
|
||||
status = nfsd_unlink(rqstp, &cstate->current_fh, 0,
|
||||
remove->rm_name, remove->rm_namelen);
|
||||
if (status == nfserr_symlink)
|
||||
return nfserr_notdir;
|
||||
if (!status) {
|
||||
fh_unlock(&cstate->current_fh);
|
||||
set_change_info(&remove->rm_cinfo, &cstate->current_fh);
|
||||
|
@ -751,8 +761,6 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
(S_ISDIR(cstate->save_fh.fh_dentry->d_inode->i_mode) &&
|
||||
S_ISDIR(cstate->current_fh.fh_dentry->d_inode->i_mode)))
|
||||
status = nfserr_exist;
|
||||
else if (status == nfserr_symlink)
|
||||
status = nfserr_notdir;
|
||||
|
||||
if (!status) {
|
||||
set_change_info(&rename->rn_sinfo, &cstate->current_fh);
|
||||
|
@ -892,8 +900,6 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
|
||||
write->wr_bytes_written = cnt;
|
||||
|
||||
if (status == nfserr_symlink)
|
||||
status = nfserr_inval;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -930,7 +936,7 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
count = 4 + (verify->ve_attrlen >> 2);
|
||||
buf = kmalloc(count << 2, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return nfserr_resource;
|
||||
return nfserr_jukebox;
|
||||
|
||||
status = nfsd4_encode_fattr(&cstate->current_fh,
|
||||
cstate->current_fh.fh_export,
|
||||
|
@ -994,6 +1000,8 @@ static inline void nfsd4_increment_op_stats(u32 opnum)
|
|||
|
||||
typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
|
||||
void *);
|
||||
typedef u32(*nfsd4op_rsize)(struct svc_rqst *, struct nfsd4_op *op);
|
||||
|
||||
enum nfsd4_op_flags {
|
||||
ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */
|
||||
ALLOWED_ON_ABSENT_FS = 1 << 1, /* ops processed on absent fs */
|
||||
|
@ -1001,13 +1009,15 @@ enum nfsd4_op_flags {
|
|||
/* For rfc 5661 section 2.6.3.1.1: */
|
||||
OP_HANDLES_WRONGSEC = 1 << 3,
|
||||
OP_IS_PUTFH_LIKE = 1 << 4,
|
||||
};
|
||||
|
||||
struct nfsd4_operation {
|
||||
nfsd4op_func op_func;
|
||||
u32 op_flags;
|
||||
char *op_name;
|
||||
/*
|
||||
* These are the ops whose result size we estimate before
|
||||
* encoding, to avoid performing an op then not being able to
|
||||
* respond or cache a response. This includes writes and setattrs
|
||||
* as well as the operations usually called "nonidempotent":
|
||||
*/
|
||||
OP_MODIFIES_SOMETHING = 1 << 5,
|
||||
/*
|
||||
* Cache compounds containing these ops in the xid-based drc:
|
||||
* We use the DRC for compounds containing non-idempotent
|
||||
* operations, *except* those that are 4.1-specific (since
|
||||
* sessions provide their own EOS), and except for stateful
|
||||
|
@ -1015,7 +1025,15 @@ struct nfsd4_operation {
|
|||
* (since sequence numbers provide EOS for open, lock, etc in
|
||||
* the v4.0 case).
|
||||
*/
|
||||
bool op_cacheresult;
|
||||
OP_CACHEME = 1 << 6,
|
||||
};
|
||||
|
||||
struct nfsd4_operation {
|
||||
nfsd4op_func op_func;
|
||||
u32 op_flags;
|
||||
char *op_name;
|
||||
/* Try to get response size before operation */
|
||||
nfsd4op_rsize op_rsize_bop;
|
||||
};
|
||||
|
||||
static struct nfsd4_operation nfsd4_ops[];
|
||||
|
@ -1062,7 +1080,7 @@ static inline struct nfsd4_operation *OPDESC(struct nfsd4_op *op)
|
|||
|
||||
bool nfsd4_cache_this_op(struct nfsd4_op *op)
|
||||
{
|
||||
return OPDESC(op)->op_cacheresult;
|
||||
return OPDESC(op)->op_flags & OP_CACHEME;
|
||||
}
|
||||
|
||||
static bool need_wrongsec_check(struct svc_rqst *rqstp)
|
||||
|
@ -1110,6 +1128,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
|
|||
struct nfsd4_operation *opdesc;
|
||||
struct nfsd4_compound_state *cstate = &resp->cstate;
|
||||
int slack_bytes;
|
||||
u32 plen = 0;
|
||||
__be32 status;
|
||||
|
||||
resp->xbuf = &rqstp->rq_res;
|
||||
|
@ -1188,6 +1207,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
|
|||
goto encode_op;
|
||||
}
|
||||
|
||||
/* If op is non-idempotent */
|
||||
if (opdesc->op_flags & OP_MODIFIES_SOMETHING) {
|
||||
plen = opdesc->op_rsize_bop(rqstp, op);
|
||||
op->status = nfsd4_check_resp_size(resp, plen);
|
||||
}
|
||||
|
||||
if (op->status)
|
||||
goto encode_op;
|
||||
|
||||
if (opdesc->op_func)
|
||||
op->status = opdesc->op_func(rqstp, cstate, &op->u);
|
||||
else
|
||||
|
@ -1217,7 +1245,7 @@ encode_op:
|
|||
be32_to_cpu(status));
|
||||
|
||||
if (cstate->replay_owner) {
|
||||
nfs4_put_stateowner(cstate->replay_owner);
|
||||
nfs4_unlock_state();
|
||||
cstate->replay_owner = NULL;
|
||||
}
|
||||
/* XXX Ugh, we need to get rid of this kind of special case: */
|
||||
|
@ -1238,6 +1266,144 @@ out:
|
|||
return status;
|
||||
}
|
||||
|
||||
#define op_encode_hdr_size (2)
|
||||
#define op_encode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE))
|
||||
#define op_encode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE))
|
||||
#define op_encode_change_info_maxsz (5)
|
||||
#define nfs4_fattr_bitmap_maxsz (4)
|
||||
|
||||
#define op_encode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
|
||||
#define op_encode_lock_denied_maxsz (8 + op_encode_lockowner_maxsz)
|
||||
|
||||
#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
|
||||
|
||||
#define op_encode_ace_maxsz (3 + nfs4_owner_maxsz)
|
||||
#define op_encode_delegation_maxsz (1 + op_encode_stateid_maxsz + 1 + \
|
||||
op_encode_ace_maxsz)
|
||||
|
||||
#define op_encode_channel_attrs_maxsz (6 + 1 + 1)
|
||||
|
||||
static inline u32 nfsd4_only_status_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_status_stateid_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_stateid_maxsz)* sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_commit_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_change_info_maxsz
|
||||
+ nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_change_info_maxsz)
|
||||
* sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_lock_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_lock_denied_maxsz)
|
||||
* sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_open_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_stateid_maxsz
|
||||
+ op_encode_change_info_maxsz + 1
|
||||
+ nfs4_fattr_bitmap_maxsz
|
||||
+ op_encode_delegation_maxsz) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
u32 maxcount = 0, rlen = 0;
|
||||
|
||||
maxcount = svc_max_payload(rqstp);
|
||||
rlen = op->u.read.rd_length;
|
||||
|
||||
if (rlen > maxcount)
|
||||
rlen = maxcount;
|
||||
|
||||
return (op_encode_hdr_size + 2) * sizeof(__be32) + rlen;
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
u32 rlen = op->u.readdir.rd_maxcount;
|
||||
|
||||
if (rlen > PAGE_SIZE)
|
||||
rlen = PAGE_SIZE;
|
||||
|
||||
return (op_encode_hdr_size + op_encode_verifier_maxsz)
|
||||
* sizeof(__be32) + rlen;
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_change_info_maxsz)
|
||||
* sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_rename_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_change_info_maxsz
|
||||
+ op_encode_change_info_maxsz) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_setattr_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_setclientid_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + 2 + 1024) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_write_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_exchange_id_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + 2 + 1 + /* eir_clientid, eir_sequenceid */\
|
||||
1 + 1 + 0 + /* eir_flags, spr_how, SP4_NONE (for now) */\
|
||||
2 + /*eir_server_owner.so_minor_id */\
|
||||
/* eir_server_owner.so_major_id<> */\
|
||||
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\
|
||||
/* eir_server_scope<> */\
|
||||
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 +\
|
||||
1 + /* eir_server_impl_id array length */\
|
||||
0 /* ignored eir_server_impl_id contents */) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_bind_conn_to_session_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + \
|
||||
XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* bctsr_sessid */\
|
||||
2 /* bctsr_dir, use_conn_in_rdma_mode */) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static inline u32 nfsd4_create_session_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
|
||||
{
|
||||
return (op_encode_hdr_size + \
|
||||
XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + /* sessionid */\
|
||||
2 + /* csr_sequence, csr_flags */\
|
||||
op_encode_channel_attrs_maxsz + \
|
||||
op_encode_channel_attrs_maxsz) * sizeof(__be32);
|
||||
}
|
||||
|
||||
static struct nfsd4_operation nfsd4_ops[] = {
|
||||
[OP_ACCESS] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_access,
|
||||
|
@ -1245,20 +1411,27 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
},
|
||||
[OP_CLOSE] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_close,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_CLOSE",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
|
||||
},
|
||||
[OP_COMMIT] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_commit,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_COMMIT",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_commit_rsize,
|
||||
},
|
||||
[OP_CREATE] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_create,
|
||||
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_CREATE",
|
||||
.op_cacheresult = true,
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_create_rsize,
|
||||
},
|
||||
[OP_DELEGRETURN] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_delegreturn,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_DELEGRETURN",
|
||||
.op_rsize_bop = nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_GETATTR] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_getattr,
|
||||
|
@ -1271,12 +1444,16 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
},
|
||||
[OP_LINK] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_link,
|
||||
.op_flags = ALLOWED_ON_ABSENT_FS | OP_MODIFIES_SOMETHING
|
||||
| OP_CACHEME,
|
||||
.op_name = "OP_LINK",
|
||||
.op_cacheresult = true,
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_link_rsize,
|
||||
},
|
||||
[OP_LOCK] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_lock,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_LOCK",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_lock_rsize,
|
||||
},
|
||||
[OP_LOCKT] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_lockt,
|
||||
|
@ -1284,7 +1461,9 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
},
|
||||
[OP_LOCKU] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_locku,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_LOCKU",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
|
||||
},
|
||||
[OP_LOOKUP] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_lookup,
|
||||
|
@ -1302,42 +1481,54 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
},
|
||||
[OP_OPEN] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_open,
|
||||
.op_flags = OP_HANDLES_WRONGSEC,
|
||||
.op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_OPEN",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_open_rsize,
|
||||
},
|
||||
[OP_OPEN_CONFIRM] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_open_confirm,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_OPEN_CONFIRM",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
|
||||
},
|
||||
[OP_OPEN_DOWNGRADE] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_open_downgrade,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_OPEN_DOWNGRADE",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
|
||||
},
|
||||
[OP_PUTFH] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_putfh,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_IS_PUTFH_LIKE,
|
||||
| OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_PUTFH",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_PUTPUBFH] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_putrootfh,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_IS_PUTFH_LIKE,
|
||||
| OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_PUTPUBFH",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_PUTROOTFH] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_putrootfh,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_IS_PUTFH_LIKE,
|
||||
| OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_PUTROOTFH",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_READ] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_read,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_READ",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize,
|
||||
},
|
||||
[OP_READDIR] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_readdir,
|
||||
.op_flags = OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_READDIR",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_readdir_rsize,
|
||||
},
|
||||
[OP_READLINK] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_readlink,
|
||||
|
@ -1345,29 +1536,36 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
},
|
||||
[OP_REMOVE] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_remove,
|
||||
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_REMOVE",
|
||||
.op_cacheresult = true,
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_remove_rsize,
|
||||
},
|
||||
[OP_RENAME] = {
|
||||
.op_name = "OP_RENAME",
|
||||
.op_func = (nfsd4op_func)nfsd4_rename,
|
||||
.op_cacheresult = true,
|
||||
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_RENAME",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_rename_rsize,
|
||||
},
|
||||
[OP_RENEW] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_renew,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_RENEW",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
|
||||
},
|
||||
[OP_RESTOREFH] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_restorefh,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_IS_PUTFH_LIKE,
|
||||
| OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_RESTOREFH",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_SAVEFH] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_savefh,
|
||||
.op_flags = OP_HANDLES_WRONGSEC,
|
||||
.op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_SAVEFH",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_SECINFO] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_secinfo,
|
||||
|
@ -1377,19 +1575,22 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
[OP_SETATTR] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_setattr,
|
||||
.op_name = "OP_SETATTR",
|
||||
.op_cacheresult = true,
|
||||
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_setattr_rsize,
|
||||
},
|
||||
[OP_SETCLIENTID] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_setclientid,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_SETCLIENTID",
|
||||
.op_cacheresult = true,
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_setclientid_rsize,
|
||||
},
|
||||
[OP_SETCLIENTID_CONFIRM] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_setclientid_confirm,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_SETCLIENTID_CONFIRM",
|
||||
.op_cacheresult = true,
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_VERIFY] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_verify,
|
||||
|
@ -1397,35 +1598,46 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
},
|
||||
[OP_WRITE] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_write,
|
||||
.op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
|
||||
.op_name = "OP_WRITE",
|
||||
.op_cacheresult = true,
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_write_rsize,
|
||||
},
|
||||
[OP_RELEASE_LOCKOWNER] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_release_lockowner,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_RELEASE_LOCKOWNER",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
|
||||
/* NFSv4.1 operations */
|
||||
[OP_EXCHANGE_ID] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_exchange_id,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_EXCHANGE_ID",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_exchange_id_rsize,
|
||||
},
|
||||
[OP_BIND_CONN_TO_SESSION] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_bind_conn_to_session,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_BIND_CONN_TO_SESSION",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_bind_conn_to_session_rsize,
|
||||
},
|
||||
[OP_CREATE_SESSION] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_create_session,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_CREATE_SESSION",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_create_session_rsize,
|
||||
},
|
||||
[OP_DESTROY_SESSION] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_destroy_session,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_DESTROY_SESSION",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_SEQUENCE] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_sequence,
|
||||
|
@ -1433,14 +1645,17 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
.op_name = "OP_SEQUENCE",
|
||||
},
|
||||
[OP_DESTROY_CLIENTID] = {
|
||||
.op_func = NULL,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
||||
.op_func = (nfsd4op_func)nfsd4_destroy_clientid,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_DESTROY_CLIENTID",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_RECLAIM_COMPLETE] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_reclaim_complete,
|
||||
.op_flags = ALLOWED_WITHOUT_FH,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_RECLAIM_COMPLETE",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
[OP_SECINFO_NO_NAME] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_secinfo_no_name,
|
||||
|
@ -1454,8 +1669,9 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|||
},
|
||||
[OP_FREE_STATEID] = {
|
||||
.op_func = (nfsd4op_func)nfsd4_free_stateid,
|
||||
.op_flags = ALLOWED_WITHOUT_FH,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_FREE_STATEID",
|
||||
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
/* Globals */
|
||||
static struct file *rec_file;
|
||||
static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
|
||||
|
||||
static int
|
||||
nfs4_save_creds(const struct cred **original_creds)
|
||||
|
@ -88,7 +89,7 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
|
|||
struct xdr_netobj cksum;
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg;
|
||||
__be32 status = nfserr_resource;
|
||||
__be32 status = nfserr_jukebox;
|
||||
|
||||
dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
|
||||
clname->len, clname->data);
|
||||
|
@ -129,6 +130,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
|
|||
if (!rec_file || clp->cl_firststate)
|
||||
return 0;
|
||||
|
||||
clp->cl_firststate = 1;
|
||||
status = nfs4_save_creds(&original_cred);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
@ -143,10 +145,8 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
|
|||
goto out_unlock;
|
||||
}
|
||||
status = -EEXIST;
|
||||
if (dentry->d_inode) {
|
||||
dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n");
|
||||
if (dentry->d_inode)
|
||||
goto out_put;
|
||||
}
|
||||
status = mnt_want_write(rec_file->f_path.mnt);
|
||||
if (status)
|
||||
goto out_put;
|
||||
|
@ -156,12 +156,14 @@ out_put:
|
|||
dput(dentry);
|
||||
out_unlock:
|
||||
mutex_unlock(&dir->d_inode->i_mutex);
|
||||
if (status == 0) {
|
||||
clp->cl_firststate = 1;
|
||||
if (status == 0)
|
||||
vfs_fsync(rec_file, 0);
|
||||
}
|
||||
else
|
||||
printk(KERN_ERR "NFSD: failed to write recovery record"
|
||||
" (err %d); please check that %s exists"
|
||||
" and is writeable", status,
|
||||
user_recovery_dirname);
|
||||
nfs4_reset_creds(original_cred);
|
||||
dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -354,13 +356,13 @@ nfsd4_recdir_load(void) {
|
|||
*/
|
||||
|
||||
void
|
||||
nfsd4_init_recdir(char *rec_dirname)
|
||||
nfsd4_init_recdir()
|
||||
{
|
||||
const struct cred *original_cred;
|
||||
int status;
|
||||
|
||||
printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
|
||||
rec_dirname);
|
||||
user_recovery_dirname);
|
||||
|
||||
BUG_ON(rec_file);
|
||||
|
||||
|
@ -372,10 +374,10 @@ nfsd4_init_recdir(char *rec_dirname)
|
|||
return;
|
||||
}
|
||||
|
||||
rec_file = filp_open(rec_dirname, O_RDONLY | O_DIRECTORY, 0);
|
||||
rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0);
|
||||
if (IS_ERR(rec_file)) {
|
||||
printk("NFSD: unable to find recovery directory %s\n",
|
||||
rec_dirname);
|
||||
user_recovery_dirname);
|
||||
rec_file = NULL;
|
||||
}
|
||||
|
||||
|
@ -390,3 +392,30 @@ nfsd4_shutdown_recdir(void)
|
|||
fput(rec_file);
|
||||
rec_file = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the NFSv4 recovery directory to recdir.
|
||||
*/
|
||||
int
|
||||
nfs4_reset_recoverydir(char *recdir)
|
||||
{
|
||||
int status;
|
||||
struct path path;
|
||||
|
||||
status = kern_path(recdir, LOOKUP_FOLLOW, &path);
|
||||
if (status)
|
||||
return status;
|
||||
status = -ENOTDIR;
|
||||
if (S_ISDIR(path.dentry->d_inode->i_mode)) {
|
||||
strcpy(user_recovery_dirname, recdir);
|
||||
status = 0;
|
||||
}
|
||||
path_put(&path);
|
||||
return status;
|
||||
}
|
||||
|
||||
char *
|
||||
nfs4_recoverydir(void)
|
||||
{
|
||||
return user_recovery_dirname;
|
||||
}
|
||||
|
|
1818
fs/nfsd/nfs4state.c
1818
fs/nfsd/nfs4state.c
File diff suppressed because it is too large
Load Diff
|
@ -456,7 +456,6 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
|
|||
{
|
||||
DECODE_HEAD;
|
||||
|
||||
close->cl_stateowner = NULL;
|
||||
READ_BUF(4);
|
||||
READ32(close->cl_seqid);
|
||||
return nfsd4_decode_stateid(argp, &close->cl_stateid);
|
||||
|
@ -551,7 +550,6 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
|
|||
{
|
||||
DECODE_HEAD;
|
||||
|
||||
lock->lk_replay_owner = NULL;
|
||||
/*
|
||||
* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
|
||||
*/
|
||||
|
@ -611,7 +609,6 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
|
|||
{
|
||||
DECODE_HEAD;
|
||||
|
||||
locku->lu_stateowner = NULL;
|
||||
READ_BUF(8);
|
||||
READ32(locku->lu_type);
|
||||
if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
|
||||
|
@ -642,6 +639,83 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup
|
|||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *x)
|
||||
{
|
||||
__be32 *p;
|
||||
u32 w;
|
||||
|
||||
READ_BUF(4);
|
||||
READ32(w);
|
||||
*x = w;
|
||||
switch (w & NFS4_SHARE_ACCESS_MASK) {
|
||||
case NFS4_SHARE_ACCESS_READ:
|
||||
case NFS4_SHARE_ACCESS_WRITE:
|
||||
case NFS4_SHARE_ACCESS_BOTH:
|
||||
break;
|
||||
default:
|
||||
return nfserr_bad_xdr;
|
||||
}
|
||||
w &= !NFS4_SHARE_ACCESS_MASK;
|
||||
if (!w)
|
||||
return nfs_ok;
|
||||
if (!argp->minorversion)
|
||||
return nfserr_bad_xdr;
|
||||
switch (w & NFS4_SHARE_WANT_MASK) {
|
||||
case NFS4_SHARE_WANT_NO_PREFERENCE:
|
||||
case NFS4_SHARE_WANT_READ_DELEG:
|
||||
case NFS4_SHARE_WANT_WRITE_DELEG:
|
||||
case NFS4_SHARE_WANT_ANY_DELEG:
|
||||
case NFS4_SHARE_WANT_NO_DELEG:
|
||||
case NFS4_SHARE_WANT_CANCEL:
|
||||
break;
|
||||
default:
|
||||
return nfserr_bad_xdr;
|
||||
}
|
||||
w &= ~NFS4_SHARE_WANT_MASK;
|
||||
if (!w)
|
||||
return nfs_ok;
|
||||
switch (w) {
|
||||
case NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
|
||||
case NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED:
|
||||
case (NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL |
|
||||
NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED):
|
||||
return nfs_ok;
|
||||
}
|
||||
xdr_error:
|
||||
return nfserr_bad_xdr;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
READ_BUF(4);
|
||||
READ32(*x);
|
||||
/* Note: unlinke access bits, deny bits may be zero. */
|
||||
if (*x & ~NFS4_SHARE_DENY_BOTH)
|
||||
return nfserr_bad_xdr;
|
||||
return nfs_ok;
|
||||
xdr_error:
|
||||
return nfserr_bad_xdr;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
READ_BUF(4);
|
||||
READ32(o->len);
|
||||
|
||||
if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT)
|
||||
return nfserr_bad_xdr;
|
||||
|
||||
READ_BUF(o->len);
|
||||
SAVEMEM(o->data, o->len);
|
||||
return nfs_ok;
|
||||
xdr_error:
|
||||
return nfserr_bad_xdr;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
|
||||
{
|
||||
|
@ -649,19 +723,23 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
|
|||
|
||||
memset(open->op_bmval, 0, sizeof(open->op_bmval));
|
||||
open->op_iattr.ia_valid = 0;
|
||||
open->op_stateowner = NULL;
|
||||
open->op_openowner = NULL;
|
||||
|
||||
/* seqid, share_access, share_deny, clientid, ownerlen */
|
||||
READ_BUF(16 + sizeof(clientid_t));
|
||||
READ_BUF(4);
|
||||
READ32(open->op_seqid);
|
||||
READ32(open->op_share_access);
|
||||
READ32(open->op_share_deny);
|
||||
status = nfsd4_decode_share_access(argp, &open->op_share_access);
|
||||
if (status)
|
||||
goto xdr_error;
|
||||
status = nfsd4_decode_share_deny(argp, &open->op_share_deny);
|
||||
if (status)
|
||||
goto xdr_error;
|
||||
READ_BUF(sizeof(clientid_t));
|
||||
COPYMEM(&open->op_clientid, sizeof(clientid_t));
|
||||
READ32(open->op_owner.len);
|
||||
|
||||
/* owner, open_flag */
|
||||
READ_BUF(open->op_owner.len + 4);
|
||||
SAVEMEM(open->op_owner.data, open->op_owner.len);
|
||||
status = nfsd4_decode_opaque(argp, &open->op_owner);
|
||||
if (status)
|
||||
goto xdr_error;
|
||||
READ_BUF(4);
|
||||
READ32(open->op_create);
|
||||
switch (open->op_create) {
|
||||
case NFS4_OPEN_NOCREATE:
|
||||
|
@ -727,6 +805,19 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
|
|||
if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval)))
|
||||
return status;
|
||||
break;
|
||||
case NFS4_OPEN_CLAIM_FH:
|
||||
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
||||
if (argp->minorversion < 1)
|
||||
goto xdr_error;
|
||||
/* void */
|
||||
break;
|
||||
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
||||
if (argp->minorversion < 1)
|
||||
goto xdr_error;
|
||||
status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
|
||||
if (status)
|
||||
return status;
|
||||
break;
|
||||
default:
|
||||
goto xdr_error;
|
||||
}
|
||||
|
@ -739,7 +830,6 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con
|
|||
{
|
||||
DECODE_HEAD;
|
||||
|
||||
open_conf->oc_stateowner = NULL;
|
||||
status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
|
||||
if (status)
|
||||
return status;
|
||||
|
@ -754,15 +844,17 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d
|
|||
{
|
||||
DECODE_HEAD;
|
||||
|
||||
open_down->od_stateowner = NULL;
|
||||
status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
|
||||
if (status)
|
||||
return status;
|
||||
READ_BUF(12);
|
||||
READ_BUF(4);
|
||||
READ32(open_down->od_seqid);
|
||||
READ32(open_down->od_share_access);
|
||||
READ32(open_down->od_share_deny);
|
||||
|
||||
status = nfsd4_decode_share_access(argp, &open_down->od_share_access);
|
||||
if (status)
|
||||
return status;
|
||||
status = nfsd4_decode_share_deny(argp, &open_down->od_share_deny);
|
||||
if (status)
|
||||
return status;
|
||||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
|
@ -903,12 +995,13 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
|
|||
{
|
||||
DECODE_HEAD;
|
||||
|
||||
READ_BUF(12);
|
||||
READ_BUF(8);
|
||||
COPYMEM(setclientid->se_verf.data, 8);
|
||||
READ32(setclientid->se_namelen);
|
||||
|
||||
READ_BUF(setclientid->se_namelen + 8);
|
||||
SAVEMEM(setclientid->se_name, setclientid->se_namelen);
|
||||
status = nfsd4_decode_opaque(argp, &setclientid->se_name);
|
||||
if (status)
|
||||
return nfserr_bad_xdr;
|
||||
READ_BUF(8);
|
||||
READ32(setclientid->se_callback_prog);
|
||||
READ32(setclientid->se_callback_netid_len);
|
||||
|
||||
|
@ -1051,11 +1144,9 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
|
|||
READ_BUF(NFS4_VERIFIER_SIZE);
|
||||
COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE);
|
||||
|
||||
READ_BUF(4);
|
||||
READ32(exid->clname.len);
|
||||
|
||||
READ_BUF(exid->clname.len);
|
||||
SAVEMEM(exid->clname.data, exid->clname.len);
|
||||
status = nfsd4_decode_opaque(argp, &exid->clname);
|
||||
if (status)
|
||||
return nfserr_bad_xdr;
|
||||
|
||||
READ_BUF(4);
|
||||
READ32(exid->flags);
|
||||
|
@ -1326,6 +1417,16 @@ xdr_error:
|
|||
goto out;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_decode_destroy_clientid(struct nfsd4_compoundargs *argp, struct nfsd4_destroy_clientid *dc)
|
||||
{
|
||||
DECODE_HEAD;
|
||||
|
||||
READ_BUF(8);
|
||||
COPYMEM(&dc->clientid, 8);
|
||||
|
||||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
|
||||
{
|
||||
DECODE_HEAD;
|
||||
|
@ -1447,7 +1548,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
|
|||
[OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid,
|
||||
[OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_destroy_clientid,
|
||||
[OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete,
|
||||
};
|
||||
|
||||
|
@ -1630,15 +1731,20 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
|
|||
* we know whether the error to be returned is a sequence id mutating error.
|
||||
*/
|
||||
|
||||
#define ENCODE_SEQID_OP_TAIL(stateowner) do { \
|
||||
if (seqid_mutating_err(nfserr) && stateowner) { \
|
||||
stateowner->so_seqid++; \
|
||||
stateowner->so_replay.rp_status = nfserr; \
|
||||
stateowner->so_replay.rp_buflen = \
|
||||
(((char *)(resp)->p - (char *)save)); \
|
||||
memcpy(stateowner->so_replay.rp_buf, save, \
|
||||
stateowner->so_replay.rp_buflen); \
|
||||
} } while (0);
|
||||
static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, __be32 nfserr)
|
||||
{
|
||||
struct nfs4_stateowner *stateowner = resp->cstate.replay_owner;
|
||||
|
||||
if (seqid_mutating_err(ntohl(nfserr)) && stateowner) {
|
||||
stateowner->so_seqid++;
|
||||
stateowner->so_replay.rp_status = nfserr;
|
||||
stateowner->so_replay.rp_buflen =
|
||||
(char *)resp->p - (char *)save;
|
||||
memcpy(stateowner->so_replay.rp_buf, save,
|
||||
stateowner->so_replay.rp_buflen);
|
||||
nfsd4_purge_closed_stateid(stateowner);
|
||||
}
|
||||
}
|
||||
|
||||
/* Encode as an array of strings the string given with components
|
||||
* separated @sep.
|
||||
|
@ -1697,36 +1803,89 @@ static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
|
|||
}
|
||||
|
||||
/*
|
||||
* Return the path to an export point in the pseudo filesystem namespace
|
||||
* Returned string is safe to use as long as the caller holds a reference
|
||||
* to @exp.
|
||||
* Encode a path in RFC3530 'pathname4' format
|
||||
*/
|
||||
static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat)
|
||||
static __be32 nfsd4_encode_path(const struct path *root,
|
||||
const struct path *path, __be32 **pp, int *buflen)
|
||||
{
|
||||
struct svc_fh tmp_fh;
|
||||
char *path = NULL, *rootpath;
|
||||
size_t rootlen;
|
||||
struct path cur = {
|
||||
.mnt = path->mnt,
|
||||
.dentry = path->dentry,
|
||||
};
|
||||
__be32 *p = *pp;
|
||||
struct dentry **components = NULL;
|
||||
unsigned int ncomponents = 0;
|
||||
__be32 err = nfserr_jukebox;
|
||||
|
||||
fh_init(&tmp_fh, NFS4_FHSIZE);
|
||||
*stat = exp_pseudoroot(rqstp, &tmp_fh);
|
||||
if (*stat)
|
||||
return NULL;
|
||||
rootpath = tmp_fh.fh_export->ex_pathname;
|
||||
dprintk("nfsd4_encode_components(");
|
||||
|
||||
path = exp->ex_pathname;
|
||||
|
||||
rootlen = strlen(rootpath);
|
||||
if (strncmp(path, rootpath, rootlen)) {
|
||||
dprintk("nfsd: fs_locations failed;"
|
||||
"%s is not contained in %s\n", path, rootpath);
|
||||
*stat = nfserr_notsupp;
|
||||
path = NULL;
|
||||
goto out;
|
||||
path_get(&cur);
|
||||
/* First walk the path up to the nfsd root, and store the
|
||||
* dentries/path components in an array.
|
||||
*/
|
||||
for (;;) {
|
||||
if (cur.dentry == root->dentry && cur.mnt == root->mnt)
|
||||
break;
|
||||
if (cur.dentry == cur.mnt->mnt_root) {
|
||||
if (follow_up(&cur))
|
||||
continue;
|
||||
goto out_free;
|
||||
}
|
||||
if ((ncomponents & 15) == 0) {
|
||||
struct dentry **new;
|
||||
new = krealloc(components,
|
||||
sizeof(*new) * (ncomponents + 16),
|
||||
GFP_KERNEL);
|
||||
if (!new)
|
||||
goto out_free;
|
||||
components = new;
|
||||
}
|
||||
components[ncomponents++] = cur.dentry;
|
||||
cur.dentry = dget_parent(cur.dentry);
|
||||
}
|
||||
path += rootlen;
|
||||
out:
|
||||
fh_put(&tmp_fh);
|
||||
return path;
|
||||
|
||||
*buflen -= 4;
|
||||
if (*buflen < 0)
|
||||
goto out_free;
|
||||
WRITE32(ncomponents);
|
||||
|
||||
while (ncomponents) {
|
||||
struct dentry *dentry = components[ncomponents - 1];
|
||||
unsigned int len = dentry->d_name.len;
|
||||
|
||||
*buflen -= 4 + (XDR_QUADLEN(len) << 2);
|
||||
if (*buflen < 0)
|
||||
goto out_free;
|
||||
WRITE32(len);
|
||||
WRITEMEM(dentry->d_name.name, len);
|
||||
dprintk("/%s", dentry->d_name.name);
|
||||
dput(dentry);
|
||||
ncomponents--;
|
||||
}
|
||||
|
||||
*pp = p;
|
||||
err = 0;
|
||||
out_free:
|
||||
dprintk(")\n");
|
||||
while (ncomponents)
|
||||
dput(components[--ncomponents]);
|
||||
kfree(components);
|
||||
path_put(&cur);
|
||||
return err;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
|
||||
const struct path *path, __be32 **pp, int *buflen)
|
||||
{
|
||||
struct svc_export *exp_ps;
|
||||
__be32 res;
|
||||
|
||||
exp_ps = rqst_find_fsidzero_export(rqstp);
|
||||
if (IS_ERR(exp_ps))
|
||||
return nfserrno(PTR_ERR(exp_ps));
|
||||
res = nfsd4_encode_path(&exp_ps->ex_path, path, pp, buflen);
|
||||
exp_put(exp_ps);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1740,11 +1899,8 @@ static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
|
|||
int i;
|
||||
__be32 *p = *pp;
|
||||
struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
|
||||
char *root = nfsd4_path(rqstp, exp, &status);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
status = nfsd4_encode_components('/', root, &p, buflen);
|
||||
status = nfsd4_encode_fsloc_fsroot(rqstp, &exp->ex_path, &p, buflen);
|
||||
if (status)
|
||||
return status;
|
||||
if ((*buflen -= 4) < 0)
|
||||
|
@ -1760,12 +1916,19 @@ static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 nfs4_ftypes[16] = {
|
||||
NF4BAD, NF4FIFO, NF4CHR, NF4BAD,
|
||||
NF4DIR, NF4BAD, NF4BLK, NF4BAD,
|
||||
NF4REG, NF4BAD, NF4LNK, NF4BAD,
|
||||
NF4SOCK, NF4BAD, NF4LNK, NF4BAD,
|
||||
};
|
||||
static u32 nfs4_file_type(umode_t mode)
|
||||
{
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFIFO: return NF4FIFO;
|
||||
case S_IFCHR: return NF4CHR;
|
||||
case S_IFDIR: return NF4DIR;
|
||||
case S_IFBLK: return NF4BLK;
|
||||
case S_IFLNK: return NF4LNK;
|
||||
case S_IFREG: return NF4REG;
|
||||
case S_IFSOCK: return NF4SOCK;
|
||||
default: return NF4BAD;
|
||||
};
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
|
||||
|
@ -1954,7 +2117,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
|||
if (bmval0 & FATTR4_WORD0_TYPE) {
|
||||
if ((buflen -= 4) < 0)
|
||||
goto out_resource;
|
||||
dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12];
|
||||
dummy = nfs4_file_type(stat.mode);
|
||||
if (dummy == NF4BAD)
|
||||
goto out_serverfault;
|
||||
WRITE32(dummy);
|
||||
|
@ -2488,7 +2651,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
|
|||
if (!nfserr)
|
||||
nfsd4_encode_stateid(resp, &close->cl_stateid);
|
||||
|
||||
ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
|
||||
encode_seqid_op_tail(resp, save, nfserr);
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
|
@ -2564,17 +2727,18 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
|
|||
static void
|
||||
nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
|
||||
{
|
||||
struct xdr_netobj *conf = &ld->ld_owner;
|
||||
__be32 *p;
|
||||
|
||||
RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
|
||||
RESERVE_SPACE(32 + XDR_LEN(conf->len));
|
||||
WRITE64(ld->ld_start);
|
||||
WRITE64(ld->ld_length);
|
||||
WRITE32(ld->ld_type);
|
||||
if (ld->ld_sop) {
|
||||
if (conf->len) {
|
||||
WRITEMEM(&ld->ld_clientid, 8);
|
||||
WRITE32(ld->ld_sop->so_owner.len);
|
||||
WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
|
||||
kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
|
||||
WRITE32(conf->len);
|
||||
WRITEMEM(conf->data, conf->len);
|
||||
kfree(conf->data);
|
||||
} else { /* non - nfsv4 lock in conflict, no clientid nor owner */
|
||||
WRITE64((u64)0); /* clientid */
|
||||
WRITE32(0); /* length of owner name */
|
||||
|
@ -2592,7 +2756,7 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo
|
|||
else if (nfserr == nfserr_denied)
|
||||
nfsd4_encode_lock_denied(resp, &lock->lk_denied);
|
||||
|
||||
ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
|
||||
encode_seqid_op_tail(resp, save, nfserr);
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
|
@ -2612,7 +2776,7 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
|
|||
if (!nfserr)
|
||||
nfsd4_encode_stateid(resp, &locku->lu_stateid);
|
||||
|
||||
ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
|
||||
encode_seqid_op_tail(resp, save, nfserr);
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
|
@ -2693,7 +2857,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
|
|||
}
|
||||
/* XXX save filehandle here */
|
||||
out:
|
||||
ENCODE_SEQID_OP_TAIL(open->op_stateowner);
|
||||
encode_seqid_op_tail(resp, save, nfserr);
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
|
@ -2705,7 +2869,7 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct
|
|||
if (!nfserr)
|
||||
nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
|
||||
|
||||
ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
|
||||
encode_seqid_op_tail(resp, save, nfserr);
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
|
@ -2717,7 +2881,7 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struc
|
|||
if (!nfserr)
|
||||
nfsd4_encode_stateid(resp, &od->od_stateid);
|
||||
|
||||
ENCODE_SEQID_OP_TAIL(od->od_stateowner);
|
||||
encode_seqid_op_tail(resp, save, nfserr);
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
|
@ -2759,8 +2923,6 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
|
|||
read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
|
||||
&maxcount);
|
||||
|
||||
if (nfserr == nfserr_symlink)
|
||||
nfserr = nfserr_inval;
|
||||
if (nfserr)
|
||||
return nfserr;
|
||||
eof = (read->rd_offset + maxcount >=
|
||||
|
@ -2886,8 +3048,6 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
|
|||
readdir->common.err == nfserr_toosmall &&
|
||||
readdir->buffer == page)
|
||||
nfserr = nfserr_toosmall;
|
||||
if (nfserr == nfserr_symlink)
|
||||
nfserr = nfserr_notdir;
|
||||
if (nfserr)
|
||||
goto err_no_verf;
|
||||
|
||||
|
@ -3218,9 +3378,9 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
|
|||
WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
|
||||
WRITE32(seq->seqid);
|
||||
WRITE32(seq->slotid);
|
||||
WRITE32(seq->maxslots);
|
||||
/* For now: target_maxslots = maxslots */
|
||||
WRITE32(seq->maxslots);
|
||||
/* Note slotid's are numbered from zero: */
|
||||
WRITE32(seq->maxslots - 1); /* sr_highest_slotid */
|
||||
WRITE32(seq->maxslots - 1); /* sr_target_highest_slotid */
|
||||
WRITE32(seq->status_flags);
|
||||
|
||||
ADJUST_ARGS();
|
||||
|
@ -3233,6 +3393,7 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
|
|||
struct nfsd4_test_stateid *test_stateid)
|
||||
{
|
||||
struct nfsd4_compoundargs *argp;
|
||||
struct nfs4_client *cl = resp->cstate.session->se_client;
|
||||
stateid_t si;
|
||||
__be32 *p;
|
||||
int i;
|
||||
|
@ -3248,7 +3409,7 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
|
|||
nfs4_lock_state();
|
||||
for (i = 0; i < test_stateid->ts_num_ids; i++) {
|
||||
nfsd4_decode_stateid(argp, &si);
|
||||
valid = nfs4_validate_stateid(&si, test_stateid->ts_has_session);
|
||||
valid = nfs4_validate_stateid(cl, &si);
|
||||
RESERVE_SPACE(4);
|
||||
*p++ = htonl(valid);
|
||||
resp->p = p;
|
||||
|
@ -3334,34 +3495,29 @@ static nfsd4_enc nfsd4_enc_ops[] = {
|
|||
|
||||
/*
|
||||
* Calculate the total amount of memory that the compound response has taken
|
||||
* after encoding the current operation.
|
||||
* after encoding the current operation with pad.
|
||||
*
|
||||
* pad: add on 8 bytes for the next operation's op_code and status so that
|
||||
* there is room to cache a failure on the next operation.
|
||||
* pad: if operation is non-idempotent, pad was calculate by op_rsize_bop()
|
||||
* which was specified at nfsd4_operation, else pad is zero.
|
||||
*
|
||||
* Compare this length to the session se_fmaxresp_cached.
|
||||
* Compare this length to the session se_fmaxresp_sz and se_fmaxresp_cached.
|
||||
*
|
||||
* Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
|
||||
* will be at least a page and will therefore hold the xdr_buf head.
|
||||
*/
|
||||
static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
|
||||
int nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
|
||||
{
|
||||
int status = 0;
|
||||
struct xdr_buf *xb = &resp->rqstp->rq_res;
|
||||
struct nfsd4_compoundargs *args = resp->rqstp->rq_argp;
|
||||
struct nfsd4_session *session = NULL;
|
||||
struct nfsd4_slot *slot = resp->cstate.slot;
|
||||
u32 length, tlen = 0, pad = 8;
|
||||
u32 length, tlen = 0;
|
||||
|
||||
if (!nfsd4_has_session(&resp->cstate))
|
||||
return status;
|
||||
return 0;
|
||||
|
||||
session = resp->cstate.session;
|
||||
if (session == NULL || slot->sl_cachethis == 0)
|
||||
return status;
|
||||
|
||||
if (resp->opcnt >= args->opcnt)
|
||||
pad = 0; /* this is the last operation */
|
||||
if (session == NULL)
|
||||
return 0;
|
||||
|
||||
if (xb->page_len == 0) {
|
||||
length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
|
||||
|
@ -3374,10 +3530,14 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
|
|||
dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
|
||||
length, xb->page_len, tlen, pad);
|
||||
|
||||
if (length <= session->se_fchannel.maxresp_cached)
|
||||
return status;
|
||||
else
|
||||
if (length > session->se_fchannel.maxresp_sz)
|
||||
return nfserr_rep_too_big;
|
||||
|
||||
if (slot->sl_cachethis == 1 &&
|
||||
length > session->se_fchannel.maxresp_cached)
|
||||
return nfserr_rep_too_big_to_cache;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3397,8 +3557,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
|
|||
!nfsd4_enc_ops[op->opnum]);
|
||||
op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
|
||||
/* nfsd4_check_drc_limit guarantees enough room for error status */
|
||||
if (!op->status && nfsd4_check_drc_limit(resp))
|
||||
op->status = nfserr_rep_too_big_to_cache;
|
||||
if (!op->status)
|
||||
op->status = nfsd4_check_resp_size(resp, 0);
|
||||
status:
|
||||
/*
|
||||
* Note: We write the status directly, instead of using WRITE32(),
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <linux/ctype.h>
|
||||
|
||||
#include <linux/sunrpc/svcsock.h>
|
||||
#include <linux/nfsd/syscall.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
|
|
|
@ -11,13 +11,39 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/mount.h>
|
||||
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs2.h>
|
||||
#include <linux/nfs3.h>
|
||||
#include <linux/nfs4.h>
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
|
||||
#include <linux/nfsd/debug.h>
|
||||
#include <linux/nfsd/export.h>
|
||||
#include <linux/nfsd/stats.h>
|
||||
|
||||
/*
|
||||
* nfsd version
|
||||
*/
|
||||
#define NFSD_SUPPORTED_MINOR_VERSION 1
|
||||
/*
|
||||
* Maximum blocksizes supported by daemon under various circumstances.
|
||||
*/
|
||||
#define NFSSVC_MAXBLKSIZE RPCSVC_MAXPAYLOAD
|
||||
/* NFSv2 is limited by the protocol specification, see RFC 1094 */
|
||||
#define NFSSVC_MAXBLKSIZE_V2 (8*1024)
|
||||
|
||||
|
||||
/*
|
||||
* Largest number of bytes we need to allocate for an NFS
|
||||
* call or reply. Used to control buffer sizes. We use
|
||||
* the length of v3 WRITE, READDIR and READDIR replies
|
||||
* which are an RPC header, up to 26 XDR units of reply
|
||||
* data, and some page data.
|
||||
*
|
||||
* Note that accuracy here doesn't matter too much as the
|
||||
* size is rounded up to a page size when allocating space.
|
||||
*/
|
||||
#define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE)
|
||||
|
||||
struct readdir_cd {
|
||||
__be32 err; /* 0, nfserr, or nfserr_eof */
|
||||
|
@ -335,6 +361,13 @@ static inline u32 nfsd_suppattrs2(u32 minorversion)
|
|||
#define NFSD_SUPPATTR_EXCLCREAT_WORD2 \
|
||||
NFSD_WRITEABLE_ATTRS_WORD2
|
||||
|
||||
extern int nfsd4_is_junction(struct dentry *dentry);
|
||||
#else
|
||||
static inline int nfsd4_is_junction(struct dentry *dentry)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NFSD_V4 */
|
||||
|
||||
#endif /* LINUX_NFSD_NFSD_H */
|
||||
|
|
|
@ -59,28 +59,25 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
|
|||
* the write call).
|
||||
*/
|
||||
static inline __be32
|
||||
nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type)
|
||||
nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int requested)
|
||||
{
|
||||
/* Type can be negative when creating hardlinks - not to a dir */
|
||||
if (type > 0 && (mode & S_IFMT) != type) {
|
||||
if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK)
|
||||
return nfserr_symlink;
|
||||
else if (type == S_IFDIR)
|
||||
return nfserr_notdir;
|
||||
else if ((mode & S_IFMT) == S_IFDIR)
|
||||
return nfserr_isdir;
|
||||
else
|
||||
return nfserr_inval;
|
||||
}
|
||||
if (type < 0 && (mode & S_IFMT) == -type) {
|
||||
if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK)
|
||||
return nfserr_symlink;
|
||||
else if (type == -S_IFDIR)
|
||||
return nfserr_isdir;
|
||||
else
|
||||
return nfserr_notdir;
|
||||
}
|
||||
return 0;
|
||||
mode &= S_IFMT;
|
||||
|
||||
if (requested == 0) /* the caller doesn't care */
|
||||
return nfs_ok;
|
||||
if (mode == requested)
|
||||
return nfs_ok;
|
||||
/*
|
||||
* v4 has an error more specific than err_notdir which we should
|
||||
* return in preference to err_notdir:
|
||||
*/
|
||||
if (rqstp->rq_vers == 4 && mode == S_IFLNK)
|
||||
return nfserr_symlink;
|
||||
if (requested == S_IFDIR)
|
||||
return nfserr_notdir;
|
||||
if (mode == S_IFDIR)
|
||||
return nfserr_isdir;
|
||||
return nfserr_inval;
|
||||
}
|
||||
|
||||
static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
|
||||
|
|
174
fs/nfsd/state.h
174
fs/nfsd/state.h
|
@ -35,6 +35,7 @@
|
|||
#ifndef _NFSD4_STATE_H
|
||||
#define _NFSD4_STATE_H
|
||||
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sunrpc/svc_xprt.h>
|
||||
#include <linux/nfsd/nfsfh.h>
|
||||
#include "nfsfh.h"
|
||||
|
@ -45,24 +46,20 @@ typedef struct {
|
|||
} clientid_t;
|
||||
|
||||
typedef struct {
|
||||
u32 so_boot;
|
||||
u32 so_stateownerid;
|
||||
u32 so_fileid;
|
||||
clientid_t so_clid;
|
||||
u32 so_id;
|
||||
} stateid_opaque_t;
|
||||
|
||||
typedef struct {
|
||||
u32 si_generation;
|
||||
stateid_opaque_t si_opaque;
|
||||
} stateid_t;
|
||||
#define si_boot si_opaque.so_boot
|
||||
#define si_stateownerid si_opaque.so_stateownerid
|
||||
#define si_fileid si_opaque.so_fileid
|
||||
|
||||
#define STATEID_FMT "(%08x/%08x/%08x/%08x)"
|
||||
#define STATEID_VAL(s) \
|
||||
(s)->si_boot, \
|
||||
(s)->si_stateownerid, \
|
||||
(s)->si_fileid, \
|
||||
(s)->si_opaque.so_clid.cl_boot, \
|
||||
(s)->si_opaque.so_clid.cl_id, \
|
||||
(s)->si_opaque.so_id, \
|
||||
(s)->si_generation
|
||||
|
||||
struct nfsd4_callback {
|
||||
|
@ -76,17 +73,27 @@ struct nfsd4_callback {
|
|||
bool cb_done;
|
||||
};
|
||||
|
||||
struct nfs4_stid {
|
||||
#define NFS4_OPEN_STID 1
|
||||
#define NFS4_LOCK_STID 2
|
||||
#define NFS4_DELEG_STID 4
|
||||
/* For an open stateid kept around *only* to process close replays: */
|
||||
#define NFS4_CLOSED_STID 8
|
||||
unsigned char sc_type;
|
||||
stateid_t sc_stateid;
|
||||
struct nfs4_client *sc_client;
|
||||
};
|
||||
|
||||
struct nfs4_delegation {
|
||||
struct nfs4_stid dl_stid; /* must be first field */
|
||||
struct list_head dl_perfile;
|
||||
struct list_head dl_perclnt;
|
||||
struct list_head dl_recall_lru; /* delegation recalled */
|
||||
atomic_t dl_count; /* ref count */
|
||||
struct nfs4_client *dl_client;
|
||||
struct nfs4_file *dl_file;
|
||||
u32 dl_type;
|
||||
time_t dl_time;
|
||||
/* For recall: */
|
||||
stateid_t dl_stateid;
|
||||
struct knfsd_fh dl_fh;
|
||||
int dl_retries;
|
||||
struct nfsd4_callback dl_recall;
|
||||
|
@ -104,6 +111,11 @@ struct nfs4_cb_conn {
|
|||
struct svc_xprt *cb_xprt; /* minorversion 1 only */
|
||||
};
|
||||
|
||||
static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
|
||||
{
|
||||
return container_of(s, struct nfs4_delegation, dl_stid);
|
||||
}
|
||||
|
||||
/* Maximum number of slots per session. 160 is useful for long haul TCP */
|
||||
#define NFSD_MAX_SLOTS_PER_SESSION 160
|
||||
/* Maximum number of operations per session compound */
|
||||
|
@ -220,6 +232,7 @@ struct nfs4_client {
|
|||
struct list_head cl_idhash; /* hash by cl_clientid.id */
|
||||
struct list_head cl_strhash; /* hash by cl_name */
|
||||
struct list_head cl_openowners;
|
||||
struct idr cl_stateids; /* stateid lookup */
|
||||
struct list_head cl_delegations;
|
||||
struct list_head cl_lru; /* tail queue */
|
||||
struct xdr_netobj cl_name; /* id generated by client */
|
||||
|
@ -245,6 +258,7 @@ struct nfs4_client {
|
|||
#define NFSD4_CB_UP 0
|
||||
#define NFSD4_CB_UNKNOWN 1
|
||||
#define NFSD4_CB_DOWN 2
|
||||
#define NFSD4_CB_FAULT 3
|
||||
int cl_cb_state;
|
||||
struct nfsd4_callback cl_cb_null;
|
||||
struct nfsd4_session *cl_cb_session;
|
||||
|
@ -293,6 +307,9 @@ static inline void
|
|||
update_stateid(stateid_t *stateid)
|
||||
{
|
||||
stateid->si_generation++;
|
||||
/* Wraparound recommendation from 3530bis-13 9.1.3.2: */
|
||||
if (stateid->si_generation == 0)
|
||||
stateid->si_generation = 1;
|
||||
}
|
||||
|
||||
/* A reasonable value for REPLAY_ISIZE was estimated as follows:
|
||||
|
@ -312,49 +329,57 @@ struct nfs4_replay {
|
|||
__be32 rp_status;
|
||||
unsigned int rp_buflen;
|
||||
char *rp_buf;
|
||||
unsigned intrp_allocated;
|
||||
struct knfsd_fh rp_openfh;
|
||||
char rp_ibuf[NFSD4_REPLAY_ISIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* nfs4_stateowner can either be an open_owner, or a lock_owner
|
||||
*
|
||||
* so_idhash: stateid_hashtbl[] for open owner, lockstateid_hashtbl[]
|
||||
* for lock_owner
|
||||
* so_strhash: ownerstr_hashtbl[] for open_owner, lock_ownerstr_hashtbl[]
|
||||
* for lock_owner
|
||||
* so_perclient: nfs4_client->cl_perclient entry - used when nfs4_client
|
||||
* struct is reaped.
|
||||
* so_perfilestate: heads the list of nfs4_stateid (either open or lock)
|
||||
* and is used to ensure no dangling nfs4_stateid references when we
|
||||
* release a stateowner.
|
||||
* so_perlockowner: (open) nfs4_stateid->st_perlockowner entry - used when
|
||||
* close is called to reap associated byte-range locks
|
||||
* so_close_lru: (open) stateowner is placed on this list instead of being
|
||||
* reaped (when so_perfilestate is empty) to hold the last close replay.
|
||||
* reaped by laundramat thread after lease period.
|
||||
*/
|
||||
struct nfs4_stateowner {
|
||||
struct kref so_ref;
|
||||
struct list_head so_idhash; /* hash by so_id */
|
||||
struct list_head so_strhash; /* hash by op_name */
|
||||
struct list_head so_perclient;
|
||||
struct list_head so_stateids;
|
||||
struct list_head so_perstateid; /* for lockowners only */
|
||||
struct list_head so_close_lru; /* tail queue */
|
||||
time_t so_time; /* time of placement on so_close_lru */
|
||||
int so_is_open_owner; /* 1=openowner,0=lockowner */
|
||||
u32 so_id;
|
||||
struct nfs4_client * so_client;
|
||||
/* after increment in ENCODE_SEQID_OP_TAIL, represents the next
|
||||
* sequence id expected from the client: */
|
||||
u32 so_seqid;
|
||||
struct xdr_netobj so_owner; /* open owner name */
|
||||
int so_confirmed; /* successful OPEN_CONFIRM? */
|
||||
struct nfs4_replay so_replay;
|
||||
bool so_is_open_owner;
|
||||
};
|
||||
|
||||
struct nfs4_openowner {
|
||||
struct nfs4_stateowner oo_owner; /* must be first field */
|
||||
struct list_head oo_perclient;
|
||||
/*
|
||||
* We keep around openowners a little while after last close,
|
||||
* which saves clients from having to confirm, and allows us to
|
||||
* handle close replays if they come soon enough. The close_lru
|
||||
* is a list of such openowners, to be reaped by the laundromat
|
||||
* thread eventually if they remain unused:
|
||||
*/
|
||||
struct list_head oo_close_lru;
|
||||
struct nfs4_ol_stateid *oo_last_closed_stid;
|
||||
time_t oo_time; /* time of placement on so_close_lru */
|
||||
#define NFS4_OO_CONFIRMED 1
|
||||
#define NFS4_OO_PURGE_CLOSE 2
|
||||
#define NFS4_OO_NEW 4
|
||||
unsigned char oo_flags;
|
||||
};
|
||||
|
||||
struct nfs4_lockowner {
|
||||
struct nfs4_stateowner lo_owner; /* must be first element */
|
||||
struct list_head lo_perstateid; /* for lockowners only */
|
||||
struct list_head lo_list; /* for temporary uses */
|
||||
};
|
||||
|
||||
static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
|
||||
{
|
||||
return container_of(so, struct nfs4_openowner, oo_owner);
|
||||
}
|
||||
|
||||
static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so)
|
||||
{
|
||||
return container_of(so, struct nfs4_lockowner, lo_owner);
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs4_file: a file opened by some number of (open) nfs4_stateowners.
|
||||
* o fi_perfile list is used to search for conflicting
|
||||
|
@ -368,17 +393,17 @@ struct nfs4_file {
|
|||
/* One each for O_RDONLY, O_WRONLY, O_RDWR: */
|
||||
struct file * fi_fds[3];
|
||||
/*
|
||||
* Each open or lock stateid contributes 1 to either
|
||||
* fi_access[O_RDONLY], fi_access[O_WRONLY], or both, depending
|
||||
* on open or lock mode:
|
||||
* Each open or lock stateid contributes 0-4 to the counts
|
||||
* below depending on which bits are set in st_access_bitmap:
|
||||
* 1 to fi_access[O_RDONLY] if NFS4_SHARE_ACCES_READ is set
|
||||
* + 1 to fi_access[O_WRONLY] if NFS4_SHARE_ACCESS_WRITE is set
|
||||
* + 1 to both of the above if NFS4_SHARE_ACCESS_BOTH is set.
|
||||
*/
|
||||
atomic_t fi_access[2];
|
||||
struct file *fi_deleg_file;
|
||||
struct file_lock *fi_lease;
|
||||
atomic_t fi_delegees;
|
||||
struct inode *fi_inode;
|
||||
u32 fi_id; /* used with stateowner->so_id
|
||||
* for stateid_hashtbl hash */
|
||||
bool fi_had_conflict;
|
||||
};
|
||||
|
||||
|
@ -408,50 +433,27 @@ static inline struct file *find_any_file(struct nfs4_file *f)
|
|||
return f->fi_fds[O_RDONLY];
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs4_stateid can either be an open stateid or (eventually) a lock stateid
|
||||
*
|
||||
* (open)nfs4_stateid: one per (open)nfs4_stateowner, nfs4_file
|
||||
*
|
||||
* st_hash: stateid_hashtbl[] entry or lockstateid_hashtbl entry
|
||||
* st_perfile: file_hashtbl[] entry.
|
||||
* st_perfile_state: nfs4_stateowner->so_perfilestate
|
||||
* st_perlockowner: (open stateid) list of lock nfs4_stateowners
|
||||
* st_access_bmap: used only for open stateid
|
||||
* st_deny_bmap: used only for open stateid
|
||||
* st_openstp: open stateid lock stateid was derived from
|
||||
*
|
||||
* XXX: open stateids and lock stateids have diverged sufficiently that
|
||||
* we should consider defining separate structs for the two cases.
|
||||
*/
|
||||
|
||||
struct nfs4_stateid {
|
||||
struct list_head st_hash;
|
||||
/* "ol" stands for "Open or Lock". Better suggestions welcome. */
|
||||
struct nfs4_ol_stateid {
|
||||
struct nfs4_stid st_stid; /* must be first field */
|
||||
struct list_head st_perfile;
|
||||
struct list_head st_perstateowner;
|
||||
struct list_head st_lockowners;
|
||||
struct nfs4_stateowner * st_stateowner;
|
||||
struct nfs4_file * st_file;
|
||||
stateid_t st_stateid;
|
||||
unsigned long st_access_bmap;
|
||||
unsigned long st_deny_bmap;
|
||||
struct nfs4_stateid * st_openstp;
|
||||
struct nfs4_ol_stateid * st_openstp;
|
||||
};
|
||||
|
||||
static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s)
|
||||
{
|
||||
return container_of(s, struct nfs4_ol_stateid, st_stid);
|
||||
}
|
||||
|
||||
/* flags for preprocess_seqid_op() */
|
||||
#define HAS_SESSION 0x00000001
|
||||
#define CONFIRM 0x00000002
|
||||
#define OPEN_STATE 0x00000004
|
||||
#define LOCK_STATE 0x00000008
|
||||
#define RD_STATE 0x00000010
|
||||
#define WR_STATE 0x00000020
|
||||
#define CLOSE_STATE 0x00000040
|
||||
|
||||
#define seqid_mutating_err(err) \
|
||||
(((err) != nfserr_stale_clientid) && \
|
||||
((err) != nfserr_bad_seqid) && \
|
||||
((err) != nfserr_stale_stateid) && \
|
||||
((err) != nfserr_bad_stateid))
|
||||
|
||||
struct nfsd4_compound_state;
|
||||
|
||||
|
@ -461,7 +463,8 @@ extern void nfs4_lock_state(void);
|
|||
extern void nfs4_unlock_state(void);
|
||||
extern int nfs4_in_grace(void);
|
||||
extern __be32 nfs4_check_open_reclaim(clientid_t *clid);
|
||||
extern void nfs4_free_stateowner(struct kref *kref);
|
||||
extern void nfs4_free_openowner(struct nfs4_openowner *);
|
||||
extern void nfs4_free_lockowner(struct nfs4_lockowner *);
|
||||
extern int set_callback_cred(void);
|
||||
extern void nfsd4_probe_callback(struct nfs4_client *clp);
|
||||
extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
|
||||
|
@ -473,7 +476,7 @@ extern void nfsd4_destroy_callback_queue(void);
|
|||
extern void nfsd4_shutdown_callback(struct nfs4_client *);
|
||||
extern void nfs4_put_delegation(struct nfs4_delegation *dp);
|
||||
extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
|
||||
extern void nfsd4_init_recdir(char *recdir_name);
|
||||
extern void nfsd4_init_recdir(void);
|
||||
extern int nfsd4_recdir_load(void);
|
||||
extern void nfsd4_shutdown_recdir(void);
|
||||
extern int nfs4_client_to_reclaim(const char *name);
|
||||
|
@ -482,18 +485,7 @@ extern void nfsd4_recdir_purge_old(void);
|
|||
extern int nfsd4_create_clid_dir(struct nfs4_client *clp);
|
||||
extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
|
||||
extern void release_session_client(struct nfsd4_session *);
|
||||
extern __be32 nfs4_validate_stateid(stateid_t *, int);
|
||||
|
||||
static inline void
|
||||
nfs4_put_stateowner(struct nfs4_stateowner *so)
|
||||
{
|
||||
kref_put(&so->so_ref, nfs4_free_stateowner);
|
||||
}
|
||||
|
||||
static inline void
|
||||
nfs4_get_stateowner(struct nfs4_stateowner *so)
|
||||
{
|
||||
kref_get(&so->so_ref);
|
||||
}
|
||||
extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *);
|
||||
extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *);
|
||||
|
||||
#endif /* NFSD4_STATE_H */
|
||||
|
|
|
@ -168,6 +168,8 @@ int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
|
|||
{
|
||||
if (d_mountpoint(dentry))
|
||||
return 1;
|
||||
if (nfsd4_is_junction(dentry))
|
||||
return 1;
|
||||
if (!(exp->ex_flags & NFSEXP_V4ROOT))
|
||||
return 0;
|
||||
return dentry->d_inode != NULL;
|
||||
|
@ -502,7 +504,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|||
unsigned int flags = 0;
|
||||
|
||||
/* Get inode */
|
||||
error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
|
||||
error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -592,6 +594,22 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac
|
|||
return error;
|
||||
}
|
||||
|
||||
#define NFSD_XATTR_JUNCTION_PREFIX XATTR_TRUSTED_PREFIX "junction."
|
||||
#define NFSD_XATTR_JUNCTION_TYPE NFSD_XATTR_JUNCTION_PREFIX "type"
|
||||
int nfsd4_is_junction(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
if (inode == NULL)
|
||||
return 0;
|
||||
if (inode->i_mode & S_IXUGO)
|
||||
return 0;
|
||||
if (!(inode->i_mode & S_ISVTX))
|
||||
return 0;
|
||||
if (vfs_getxattr(dentry, NFSD_XATTR_JUNCTION_TYPE, NULL, 0) <= 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
#endif /* defined(CONFIG_NFSD_V4) */
|
||||
|
||||
#ifdef CONFIG_NFSD_V3
|
||||
|
@ -1352,7 +1370,7 @@ __be32
|
|||
do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
char *fname, int flen, struct iattr *iap,
|
||||
struct svc_fh *resfhp, int createmode, u32 *verifier,
|
||||
int *truncp, int *created)
|
||||
bool *truncp, bool *created)
|
||||
{
|
||||
struct dentry *dentry, *dchild = NULL;
|
||||
struct inode *dirp;
|
||||
|
@ -1632,10 +1650,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
|
|||
err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE);
|
||||
if (err)
|
||||
goto out;
|
||||
err = fh_verify(rqstp, tfhp, -S_IFDIR, NFSD_MAY_NOP);
|
||||
err = fh_verify(rqstp, tfhp, 0, NFSD_MAY_NOP);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = nfserr_isdir;
|
||||
if (S_ISDIR(tfhp->fh_dentry->d_inode->i_mode))
|
||||
goto out;
|
||||
err = nfserr_perm;
|
||||
if (!len)
|
||||
goto out;
|
||||
|
@ -2114,7 +2134,8 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
|
|||
|
||||
/* Allow read access to binaries even when mode 111 */
|
||||
if (err == -EACCES && S_ISREG(inode->i_mode) &&
|
||||
acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE))
|
||||
(acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE) ||
|
||||
acc == (NFSD_MAY_READ | NFSD_MAY_READ_IF_EXEC)))
|
||||
err = inode_permission(inode, MAY_EXEC);
|
||||
|
||||
return err? nfserrno(err) : 0;
|
||||
|
|
|
@ -10,21 +10,22 @@
|
|||
/*
|
||||
* Flags for nfsd_permission
|
||||
*/
|
||||
#define NFSD_MAY_NOP 0
|
||||
#define NFSD_MAY_EXEC 1 /* == MAY_EXEC */
|
||||
#define NFSD_MAY_WRITE 2 /* == MAY_WRITE */
|
||||
#define NFSD_MAY_READ 4 /* == MAY_READ */
|
||||
#define NFSD_MAY_SATTR 8
|
||||
#define NFSD_MAY_TRUNC 16
|
||||
#define NFSD_MAY_LOCK 32
|
||||
#define NFSD_MAY_MASK 63
|
||||
#define NFSD_MAY_NOP 0
|
||||
#define NFSD_MAY_EXEC 0x001 /* == MAY_EXEC */
|
||||
#define NFSD_MAY_WRITE 0x002 /* == MAY_WRITE */
|
||||
#define NFSD_MAY_READ 0x004 /* == MAY_READ */
|
||||
#define NFSD_MAY_SATTR 0x008
|
||||
#define NFSD_MAY_TRUNC 0x010
|
||||
#define NFSD_MAY_LOCK 0x020
|
||||
#define NFSD_MAY_MASK 0x03f
|
||||
|
||||
/* extra hints to permission and open routines: */
|
||||
#define NFSD_MAY_OWNER_OVERRIDE 64
|
||||
#define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/
|
||||
#define NFSD_MAY_BYPASS_GSS_ON_ROOT 256
|
||||
#define NFSD_MAY_NOT_BREAK_LEASE 512
|
||||
#define NFSD_MAY_BYPASS_GSS 1024
|
||||
#define NFSD_MAY_OWNER_OVERRIDE 0x040
|
||||
#define NFSD_MAY_LOCAL_ACCESS 0x080 /* for device special files */
|
||||
#define NFSD_MAY_BYPASS_GSS_ON_ROOT 0x100
|
||||
#define NFSD_MAY_NOT_BREAK_LEASE 0x200
|
||||
#define NFSD_MAY_BYPASS_GSS 0x400
|
||||
#define NFSD_MAY_READ_IF_EXEC 0x800
|
||||
|
||||
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
|
||||
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
|
||||
|
@ -61,7 +62,7 @@ __be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *);
|
|||
__be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *,
|
||||
char *name, int len, struct iattr *attrs,
|
||||
struct svc_fh *res, int createmode,
|
||||
u32 *verifier, int *truncp, int *created);
|
||||
u32 *verifier, bool *truncp, bool *created);
|
||||
__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
|
||||
loff_t, unsigned long);
|
||||
#endif /* CONFIG_NFSD_V3 */
|
||||
|
|
|
@ -81,7 +81,6 @@ struct nfsd4_access {
|
|||
struct nfsd4_close {
|
||||
u32 cl_seqid; /* request */
|
||||
stateid_t cl_stateid; /* request+response */
|
||||
struct nfs4_stateowner * cl_stateowner; /* response */
|
||||
};
|
||||
|
||||
struct nfsd4_commit {
|
||||
|
@ -131,7 +130,7 @@ struct nfsd4_link {
|
|||
|
||||
struct nfsd4_lock_denied {
|
||||
clientid_t ld_clientid;
|
||||
struct nfs4_stateowner *ld_sop;
|
||||
struct xdr_netobj ld_owner;
|
||||
u64 ld_start;
|
||||
u64 ld_length;
|
||||
u32 ld_type;
|
||||
|
@ -165,9 +164,6 @@ struct nfsd4_lock {
|
|||
} ok;
|
||||
struct nfsd4_lock_denied denied;
|
||||
} u;
|
||||
/* The lk_replay_owner is the open owner in the open_to_lock_owner
|
||||
* case and the lock owner otherwise: */
|
||||
struct nfs4_stateowner *lk_replay_owner;
|
||||
};
|
||||
#define lk_new_open_seqid v.new.open_seqid
|
||||
#define lk_new_open_stateid v.new.open_stateid
|
||||
|
@ -188,7 +184,6 @@ struct nfsd4_lockt {
|
|||
struct xdr_netobj lt_owner;
|
||||
u64 lt_offset;
|
||||
u64 lt_length;
|
||||
struct nfs4_stateowner * lt_stateowner;
|
||||
struct nfsd4_lock_denied lt_denied;
|
||||
};
|
||||
|
||||
|
@ -199,7 +194,6 @@ struct nfsd4_locku {
|
|||
stateid_t lu_stateid;
|
||||
u64 lu_offset;
|
||||
u64 lu_length;
|
||||
struct nfs4_stateowner *lu_stateowner;
|
||||
};
|
||||
|
||||
|
||||
|
@ -232,8 +226,11 @@ struct nfsd4_open {
|
|||
u32 op_recall; /* recall */
|
||||
struct nfsd4_change_info op_cinfo; /* response */
|
||||
u32 op_rflags; /* response */
|
||||
int op_truncate; /* used during processing */
|
||||
struct nfs4_stateowner *op_stateowner; /* used during processing */
|
||||
bool op_truncate; /* used during processing */
|
||||
bool op_created; /* used during processing */
|
||||
struct nfs4_openowner *op_openowner; /* used during processing */
|
||||
struct nfs4_file *op_file; /* used during processing */
|
||||
struct nfs4_ol_stateid *op_stp; /* used during processing */
|
||||
struct nfs4_acl *op_acl;
|
||||
};
|
||||
#define op_iattr iattr
|
||||
|
@ -243,7 +240,6 @@ struct nfsd4_open_confirm {
|
|||
stateid_t oc_req_stateid /* request */;
|
||||
u32 oc_seqid /* request */;
|
||||
stateid_t oc_resp_stateid /* response */;
|
||||
struct nfs4_stateowner * oc_stateowner; /* response */
|
||||
};
|
||||
|
||||
struct nfsd4_open_downgrade {
|
||||
|
@ -251,7 +247,6 @@ struct nfsd4_open_downgrade {
|
|||
u32 od_seqid;
|
||||
u32 od_share_access;
|
||||
u32 od_share_deny;
|
||||
struct nfs4_stateowner *od_stateowner;
|
||||
};
|
||||
|
||||
|
||||
|
@ -325,8 +320,7 @@ struct nfsd4_setattr {
|
|||
|
||||
struct nfsd4_setclientid {
|
||||
nfs4_verifier se_verf; /* request */
|
||||
u32 se_namelen; /* request */
|
||||
char * se_name; /* request */
|
||||
struct xdr_netobj se_name;
|
||||
u32 se_callback_prog; /* request */
|
||||
u32 se_callback_netid_len; /* request */
|
||||
char * se_callback_netid_val; /* request */
|
||||
|
@ -351,7 +345,6 @@ struct nfsd4_saved_compoundargs {
|
|||
|
||||
struct nfsd4_test_stateid {
|
||||
__be32 ts_num_ids;
|
||||
__be32 ts_has_session;
|
||||
struct nfsd4_compoundargs *ts_saved_args;
|
||||
struct nfsd4_saved_compoundargs ts_savedp;
|
||||
};
|
||||
|
@ -405,6 +398,10 @@ struct nfsd4_destroy_session {
|
|||
struct nfs4_sessionid sessionid;
|
||||
};
|
||||
|
||||
struct nfsd4_destroy_clientid {
|
||||
clientid_t clientid;
|
||||
};
|
||||
|
||||
struct nfsd4_reclaim_complete {
|
||||
u32 rca_one_fs;
|
||||
};
|
||||
|
@ -532,6 +529,7 @@ int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *,
|
|||
struct nfsd4_compoundargs *);
|
||||
int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,
|
||||
struct nfsd4_compoundres *);
|
||||
int nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
|
||||
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
|
||||
void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op);
|
||||
__be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
||||
|
@ -558,11 +556,13 @@ extern __be32 nfsd4_sequence(struct svc_rqst *,
|
|||
extern __be32 nfsd4_destroy_session(struct svc_rqst *,
|
||||
struct nfsd4_compound_state *,
|
||||
struct nfsd4_destroy_session *);
|
||||
extern __be32 nfsd4_destroy_clientid(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_clientid *);
|
||||
__be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *);
|
||||
extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *,
|
||||
struct nfsd4_open *open);
|
||||
extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp,
|
||||
struct svc_fh *current_fh, struct nfsd4_open *open);
|
||||
extern void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status);
|
||||
extern __be32 nfsd4_open_confirm(struct svc_rqst *rqstp,
|
||||
struct nfsd4_compound_state *, struct nfsd4_open_confirm *oc);
|
||||
extern __be32 nfsd4_close(struct svc_rqst *rqstp,
|
||||
|
|
|
@ -145,11 +145,6 @@ struct f_owner_ex {
|
|||
#define F_SHLCK 8 /* or 4 */
|
||||
#endif
|
||||
|
||||
/* for leases */
|
||||
#ifndef F_INPROGRESS
|
||||
#define F_INPROGRESS 16
|
||||
#endif
|
||||
|
||||
/* operations for bsd flock(), also used by the kernel implementation */
|
||||
#define LOCK_SH 1 /* shared lock */
|
||||
#define LOCK_EX 2 /* exclusive lock */
|
||||
|
|
|
@ -1063,6 +1063,8 @@ static inline int file_check_writeable(struct file *filp)
|
|||
#define FL_LEASE 32 /* lease held on this file */
|
||||
#define FL_CLOSE 64 /* unlock on close */
|
||||
#define FL_SLEEP 128 /* A blocking lock */
|
||||
#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
|
||||
#define FL_UNLOCK_PENDING 512 /* Lease is being broken */
|
||||
|
||||
/*
|
||||
* Special return value from posix_lock_file() and vfs_lock_file() for
|
||||
|
@ -1109,7 +1111,7 @@ struct file_lock {
|
|||
struct list_head fl_link; /* doubly linked list of all locks */
|
||||
struct list_head fl_block; /* circular list of blocked processes */
|
||||
fl_owner_t fl_owner;
|
||||
unsigned char fl_flags;
|
||||
unsigned int fl_flags;
|
||||
unsigned char fl_type;
|
||||
unsigned int fl_pid;
|
||||
struct pid *fl_nspid;
|
||||
|
@ -1119,7 +1121,9 @@ struct file_lock {
|
|||
loff_t fl_end;
|
||||
|
||||
struct fasync_struct * fl_fasync; /* for lease break notifications */
|
||||
unsigned long fl_break_time; /* for nonblocking lease breaks */
|
||||
/* for lease breaks: */
|
||||
unsigned long fl_break_time;
|
||||
unsigned long fl_downgrade_time;
|
||||
|
||||
const struct file_lock_operations *fl_ops; /* Callbacks for filesystems */
|
||||
const struct lock_manager_operations *fl_lmops; /* Callbacks for lockmanagers */
|
||||
|
|
|
@ -373,6 +373,22 @@ enum nfsstat4 {
|
|||
NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */
|
||||
};
|
||||
|
||||
static inline bool seqid_mutating_err(u32 err)
|
||||
{
|
||||
/* rfc 3530 section 8.1.5: */
|
||||
switch (err) {
|
||||
case NFS4ERR_STALE_CLIENTID:
|
||||
case NFS4ERR_STALE_STATEID:
|
||||
case NFS4ERR_BAD_STATEID:
|
||||
case NFS4ERR_BAD_SEQID:
|
||||
case NFS4ERR_BADXDR:
|
||||
case NFS4ERR_RESOURCE:
|
||||
case NFS4ERR_NOFILEHANDLE:
|
||||
return false;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: NF4BAD is not actually part of the protocol; it is just used
|
||||
* internally by nfsd.
|
||||
|
@ -394,7 +410,10 @@ enum open_claim_type4 {
|
|||
NFS4_OPEN_CLAIM_NULL = 0,
|
||||
NFS4_OPEN_CLAIM_PREVIOUS = 1,
|
||||
NFS4_OPEN_CLAIM_DELEGATE_CUR = 2,
|
||||
NFS4_OPEN_CLAIM_DELEGATE_PREV = 3
|
||||
NFS4_OPEN_CLAIM_DELEGATE_PREV = 3,
|
||||
NFS4_OPEN_CLAIM_FH = 4, /* 4.1 */
|
||||
NFS4_OPEN_CLAIM_DELEG_CUR_FH = 5, /* 4.1 */
|
||||
NFS4_OPEN_CLAIM_DELEG_PREV_FH = 6, /* 4.1 */
|
||||
};
|
||||
|
||||
enum opentype4 {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
header-y += const.h
|
||||
header-y += debug.h
|
||||
header-y += export.h
|
||||
header-y += nfsfh.h
|
||||
header-y += stats.h
|
||||
header-y += syscall.h
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* include/linux/nfsd/const.h
|
||||
*
|
||||
* Various constants related to NFS.
|
||||
*
|
||||
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_NFSD_CONST_H
|
||||
#define _LINUX_NFSD_CONST_H
|
||||
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs2.h>
|
||||
#include <linux/nfs3.h>
|
||||
#include <linux/nfs4.h>
|
||||
|
||||
/*
|
||||
* Maximum protocol version supported by knfsd
|
||||
*/
|
||||
#define NFSSVC_MAXVERS 3
|
||||
|
||||
/*
|
||||
* Maximum blocksizes supported by daemon under various circumstances.
|
||||
*/
|
||||
#define NFSSVC_MAXBLKSIZE RPCSVC_MAXPAYLOAD
|
||||
/* NFSv2 is limited by the protocol specification, see RFC 1094 */
|
||||
#define NFSSVC_MAXBLKSIZE_V2 (8*1024)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
|
||||
/*
|
||||
* Largest number of bytes we need to allocate for an NFS
|
||||
* call or reply. Used to control buffer sizes. We use
|
||||
* the length of v3 WRITE, READDIR and READDIR replies
|
||||
* which are an RPC header, up to 26 XDR units of reply
|
||||
* data, and some page data.
|
||||
*
|
||||
* Note that accuracy here doesn't matter too much as the
|
||||
* size is rounded up to a page size when allocating space.
|
||||
*/
|
||||
#define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE)
|
||||
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
# define NFSSVC_XDRSIZE NFS4_SVC_XDRSIZE
|
||||
#elif defined(CONFIG_NFSD_V3)
|
||||
# define NFSSVC_XDRSIZE NFS3_SVC_XDRSIZE
|
||||
#else
|
||||
# define NFSSVC_XDRSIZE NFS2_SVC_XDRSIZE
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _LINUX_NFSD_CONST_H */
|
|
@ -96,7 +96,6 @@ struct svc_export {
|
|||
struct auth_domain * ex_client;
|
||||
int ex_flags;
|
||||
struct path ex_path;
|
||||
char *ex_pathname;
|
||||
uid_t ex_anon_uid;
|
||||
gid_t ex_anon_gid;
|
||||
int ex_fsid;
|
||||
|
@ -137,6 +136,7 @@ struct svc_export * rqst_exp_get_by_name(struct svc_rqst *,
|
|||
struct path *);
|
||||
struct svc_export * rqst_exp_parent(struct svc_rqst *,
|
||||
struct path *);
|
||||
struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *);
|
||||
int exp_rootfh(struct auth_domain *,
|
||||
char *path, struct knfsd_fh *, int maxsize);
|
||||
__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *);
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
#ifndef _LINUX_NFSD_FH_H
|
||||
#define _LINUX_NFSD_FH_H
|
||||
|
||||
# include <linux/types.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs2.h>
|
||||
#include <linux/nfs3.h>
|
||||
#include <linux/nfs4.h>
|
||||
#ifdef __KERNEL__
|
||||
# include <linux/sunrpc/svc.h>
|
||||
#endif
|
||||
#include <linux/nfsd/const.h>
|
||||
|
||||
/*
|
||||
* This is the old "dentry style" Linux NFSv2 file handle.
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* include/linux/nfsd/syscall.h
|
||||
*
|
||||
* This file holds all declarations for the knfsd syscall interface.
|
||||
*
|
||||
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
|
||||
*/
|
||||
|
||||
#ifndef NFSD_SYSCALL_H
|
||||
#define NFSD_SYSCALL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/nfsd/export.h>
|
||||
|
||||
/*
|
||||
* Version of the syscall interface
|
||||
*/
|
||||
#define NFSCTL_VERSION 0x0201
|
||||
|
||||
/*
|
||||
* These are the commands understood by nfsctl().
|
||||
*/
|
||||
#define NFSCTL_SVC 0 /* This is a server process. */
|
||||
#define NFSCTL_ADDCLIENT 1 /* Add an NFS client. */
|
||||
#define NFSCTL_DELCLIENT 2 /* Remove an NFS client. */
|
||||
#define NFSCTL_EXPORT 3 /* export a file system. */
|
||||
#define NFSCTL_UNEXPORT 4 /* unexport a file system. */
|
||||
/*#define NFSCTL_UGIDUPDATE 5 / * update a client's uid/gid map. DISCARDED */
|
||||
/*#define NFSCTL_GETFH 6 / * get an fh by ino DISCARDED */
|
||||
#define NFSCTL_GETFD 7 /* get an fh by path (used by mountd) */
|
||||
#define NFSCTL_GETFS 8 /* get an fh by path with max FH len */
|
||||
|
||||
/* SVC */
|
||||
struct nfsctl_svc {
|
||||
unsigned short svc_port;
|
||||
int svc_nthreads;
|
||||
};
|
||||
|
||||
/* ADDCLIENT/DELCLIENT */
|
||||
struct nfsctl_client {
|
||||
char cl_ident[NFSCLNT_IDMAX+1];
|
||||
int cl_naddr;
|
||||
struct in_addr cl_addrlist[NFSCLNT_ADDRMAX];
|
||||
int cl_fhkeytype;
|
||||
int cl_fhkeylen;
|
||||
unsigned char cl_fhkey[NFSCLNT_KEYMAX];
|
||||
};
|
||||
|
||||
/* EXPORT/UNEXPORT */
|
||||
struct nfsctl_export {
|
||||
char ex_client[NFSCLNT_IDMAX+1];
|
||||
char ex_path[NFS_MAXPATHLEN+1];
|
||||
__kernel_old_dev_t ex_dev;
|
||||
__kernel_ino_t ex_ino;
|
||||
int ex_flags;
|
||||
__kernel_uid_t ex_anon_uid;
|
||||
__kernel_gid_t ex_anon_gid;
|
||||
};
|
||||
|
||||
/* GETFD */
|
||||
struct nfsctl_fdparm {
|
||||
struct sockaddr gd_addr;
|
||||
char gd_path[NFS_MAXPATHLEN+1];
|
||||
int gd_version;
|
||||
};
|
||||
|
||||
/* GETFS - GET Filehandle with Size */
|
||||
struct nfsctl_fsparm {
|
||||
struct sockaddr gd_addr;
|
||||
char gd_path[NFS_MAXPATHLEN+1];
|
||||
int gd_maxlen;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the argument union.
|
||||
*/
|
||||
struct nfsctl_arg {
|
||||
int ca_version; /* safeguard */
|
||||
union {
|
||||
struct nfsctl_svc u_svc;
|
||||
struct nfsctl_client u_client;
|
||||
struct nfsctl_export u_export;
|
||||
struct nfsctl_fdparm u_getfd;
|
||||
struct nfsctl_fsparm u_getfs;
|
||||
/*
|
||||
* The following dummy member is needed to preserve binary compatibility
|
||||
* on platforms where alignof(void*)>alignof(int). It's needed because
|
||||
* this union used to contain a member (u_umap) which contained a
|
||||
* pointer.
|
||||
*/
|
||||
void *u_ptr;
|
||||
} u;
|
||||
#define ca_svc u.u_svc
|
||||
#define ca_client u.u_client
|
||||
#define ca_export u.u_export
|
||||
#define ca_getfd u.u_getfd
|
||||
#define ca_getfs u.u_getfs
|
||||
};
|
||||
|
||||
union nfsctl_res {
|
||||
__u8 cr_getfh[NFS_FHSIZE];
|
||||
struct knfsd_fh cr_getfs;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* Kernel syscall implementation.
|
||||
*/
|
||||
extern int exp_addclient(struct nfsctl_client *ncp);
|
||||
extern int exp_delclient(struct nfsctl_client *ncp);
|
||||
extern int exp_export(struct nfsctl_export *nxp);
|
||||
extern int exp_unexport(struct nfsctl_export *nxp);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* NFSD_SYSCALL_H */
|
|
@ -218,7 +218,13 @@ static inline bool __rpc_cmp_addr6(const struct sockaddr *sap1,
|
|||
{
|
||||
const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sap1;
|
||||
const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sap2;
|
||||
return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr);
|
||||
|
||||
if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr))
|
||||
return false;
|
||||
else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL)
|
||||
return sin1->sin6_scope_id == sin2->sin6_scope_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool __rpc_copy_addr6(struct sockaddr *dst,
|
||||
|
|
|
@ -212,11 +212,6 @@ static inline void svc_putu32(struct kvec *iov, __be32 val)
|
|||
iov->iov_len += sizeof(__be32);
|
||||
}
|
||||
|
||||
union svc_addr_u {
|
||||
struct in_addr addr;
|
||||
struct in6_addr addr6;
|
||||
};
|
||||
|
||||
/*
|
||||
* The context of a single thread, including the request currently being
|
||||
* processed.
|
||||
|
@ -225,8 +220,12 @@ struct svc_rqst {
|
|||
struct list_head rq_list; /* idle list */
|
||||
struct list_head rq_all; /* all threads list */
|
||||
struct svc_xprt * rq_xprt; /* transport ptr */
|
||||
|
||||
struct sockaddr_storage rq_addr; /* peer address */
|
||||
size_t rq_addrlen;
|
||||
struct sockaddr_storage rq_daddr; /* dest addr of request
|
||||
* - reply from here */
|
||||
size_t rq_daddrlen;
|
||||
|
||||
struct svc_serv * rq_server; /* RPC service definition */
|
||||
struct svc_pool * rq_pool; /* thread pool */
|
||||
|
@ -255,9 +254,6 @@ struct svc_rqst {
|
|||
unsigned short
|
||||
rq_secure : 1; /* secure port */
|
||||
|
||||
union svc_addr_u rq_daddr; /* dest addr of request
|
||||
* - reply from here */
|
||||
|
||||
void * rq_argp; /* decoded arguments */
|
||||
void * rq_resp; /* xdr'd results */
|
||||
void * rq_auth_data; /* flavor-specific data */
|
||||
|
@ -300,6 +296,21 @@ static inline struct sockaddr *svc_addr(const struct svc_rqst *rqst)
|
|||
return (struct sockaddr *) &rqst->rq_addr;
|
||||
}
|
||||
|
||||
static inline struct sockaddr_in *svc_daddr_in(const struct svc_rqst *rqst)
|
||||
{
|
||||
return (struct sockaddr_in *) &rqst->rq_daddr;
|
||||
}
|
||||
|
||||
static inline struct sockaddr_in6 *svc_daddr_in6(const struct svc_rqst *rqst)
|
||||
{
|
||||
return (struct sockaddr_in6 *) &rqst->rq_daddr;
|
||||
}
|
||||
|
||||
static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst)
|
||||
{
|
||||
return (struct sockaddr *) &rqst->rq_daddr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check buffer bounds after decoding arguments
|
||||
*/
|
||||
|
@ -340,7 +351,8 @@ struct svc_deferred_req {
|
|||
struct svc_xprt *xprt;
|
||||
struct sockaddr_storage addr; /* where reply must go */
|
||||
size_t addrlen;
|
||||
union svc_addr_u daddr; /* where reply must come from */
|
||||
struct sockaddr_storage daddr; /* where reply must come from */
|
||||
size_t daddrlen;
|
||||
struct cache_deferred_req handle;
|
||||
size_t xprt_hlen;
|
||||
int argslen;
|
||||
|
@ -404,7 +416,7 @@ struct svc_procedure {
|
|||
struct svc_serv *svc_create(struct svc_program *, unsigned int,
|
||||
void (*shutdown)(struct svc_serv *));
|
||||
struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
|
||||
struct svc_pool *pool);
|
||||
struct svc_pool *pool, int node);
|
||||
void svc_exit_thread(struct svc_rqst *);
|
||||
struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int,
|
||||
void (*shutdown)(struct svc_serv *),
|
||||
|
|
|
@ -1084,3 +1084,6 @@ void unregister_rpc_pipefs(void)
|
|||
kmem_cache_destroy(rpc_inode_cachep);
|
||||
unregister_filesystem(&rpc_pipe_fs_type);
|
||||
}
|
||||
|
||||
/* Make 'mount -t rpc_pipefs ...' autoload this module. */
|
||||
MODULE_ALIAS("rpc_pipefs");
|
||||
|
|
|
@ -295,6 +295,18 @@ svc_pool_map_put(void)
|
|||
}
|
||||
|
||||
|
||||
static int svc_pool_map_get_node(unsigned int pidx)
|
||||
{
|
||||
const struct svc_pool_map *m = &svc_pool_map;
|
||||
|
||||
if (m->count) {
|
||||
if (m->mode == SVC_POOL_PERCPU)
|
||||
return cpu_to_node(m->pool_to[pidx]);
|
||||
if (m->mode == SVC_POOL_PERNODE)
|
||||
return m->pool_to[pidx];
|
||||
}
|
||||
return NUMA_NO_NODE;
|
||||
}
|
||||
/*
|
||||
* Set the given thread's cpus_allowed mask so that it
|
||||
* will only run on cpus in the given pool.
|
||||
|
@ -499,7 +511,7 @@ EXPORT_SYMBOL_GPL(svc_destroy);
|
|||
* We allocate pages and place them in rq_argpages.
|
||||
*/
|
||||
static int
|
||||
svc_init_buffer(struct svc_rqst *rqstp, unsigned int size)
|
||||
svc_init_buffer(struct svc_rqst *rqstp, unsigned int size, int node)
|
||||
{
|
||||
unsigned int pages, arghi;
|
||||
|
||||
|
@ -513,7 +525,7 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size)
|
|||
arghi = 0;
|
||||
BUG_ON(pages > RPCSVC_MAXPAGES);
|
||||
while (pages) {
|
||||
struct page *p = alloc_page(GFP_KERNEL);
|
||||
struct page *p = alloc_pages_node(node, GFP_KERNEL, 0);
|
||||
if (!p)
|
||||
break;
|
||||
rqstp->rq_pages[arghi++] = p;
|
||||
|
@ -536,11 +548,11 @@ svc_release_buffer(struct svc_rqst *rqstp)
|
|||
}
|
||||
|
||||
struct svc_rqst *
|
||||
svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool)
|
||||
svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
{
|
||||
struct svc_rqst *rqstp;
|
||||
|
||||
rqstp = kzalloc(sizeof(*rqstp), GFP_KERNEL);
|
||||
rqstp = kzalloc_node(sizeof(*rqstp), GFP_KERNEL, node);
|
||||
if (!rqstp)
|
||||
goto out_enomem;
|
||||
|
||||
|
@ -554,15 +566,15 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool)
|
|||
rqstp->rq_server = serv;
|
||||
rqstp->rq_pool = pool;
|
||||
|
||||
rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL);
|
||||
rqstp->rq_argp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node);
|
||||
if (!rqstp->rq_argp)
|
||||
goto out_thread;
|
||||
|
||||
rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL);
|
||||
rqstp->rq_resp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node);
|
||||
if (!rqstp->rq_resp)
|
||||
goto out_thread;
|
||||
|
||||
if (!svc_init_buffer(rqstp, serv->sv_max_mesg))
|
||||
if (!svc_init_buffer(rqstp, serv->sv_max_mesg, node))
|
||||
goto out_thread;
|
||||
|
||||
return rqstp;
|
||||
|
@ -647,6 +659,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
|||
struct svc_pool *chosen_pool;
|
||||
int error = 0;
|
||||
unsigned int state = serv->sv_nrthreads-1;
|
||||
int node;
|
||||
|
||||
if (pool == NULL) {
|
||||
/* The -1 assumes caller has done a svc_get() */
|
||||
|
@ -662,14 +675,16 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
|||
nrservs--;
|
||||
chosen_pool = choose_pool(serv, pool, &state);
|
||||
|
||||
rqstp = svc_prepare_thread(serv, chosen_pool);
|
||||
node = svc_pool_map_get_node(chosen_pool->sp_id);
|
||||
rqstp = svc_prepare_thread(serv, chosen_pool, node);
|
||||
if (IS_ERR(rqstp)) {
|
||||
error = PTR_ERR(rqstp);
|
||||
break;
|
||||
}
|
||||
|
||||
__module_get(serv->sv_module);
|
||||
task = kthread_create(serv->sv_function, rqstp, serv->sv_name);
|
||||
task = kthread_create_on_node(serv->sv_function, rqstp,
|
||||
node, serv->sv_name);
|
||||
if (IS_ERR(task)) {
|
||||
error = PTR_ERR(task);
|
||||
module_put(serv->sv_module);
|
||||
|
|
|
@ -254,8 +254,6 @@ EXPORT_SYMBOL_GPL(svc_create_xprt);
|
|||
*/
|
||||
void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt)
|
||||
{
|
||||
struct sockaddr *sin;
|
||||
|
||||
memcpy(&rqstp->rq_addr, &xprt->xpt_remote, xprt->xpt_remotelen);
|
||||
rqstp->rq_addrlen = xprt->xpt_remotelen;
|
||||
|
||||
|
@ -263,15 +261,8 @@ void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt)
|
|||
* Destination address in request is needed for binding the
|
||||
* source address in RPC replies/callbacks later.
|
||||
*/
|
||||
sin = (struct sockaddr *)&xprt->xpt_local;
|
||||
switch (sin->sa_family) {
|
||||
case AF_INET:
|
||||
rqstp->rq_daddr.addr = ((struct sockaddr_in *)sin)->sin_addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
rqstp->rq_daddr.addr6 = ((struct sockaddr_in6 *)sin)->sin6_addr;
|
||||
break;
|
||||
}
|
||||
memcpy(&rqstp->rq_daddr, &xprt->xpt_local, xprt->xpt_locallen);
|
||||
rqstp->rq_daddrlen = xprt->xpt_locallen;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_xprt_copy_addrs);
|
||||
|
||||
|
|
|
@ -143,19 +143,20 @@ static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh)
|
|||
cmh->cmsg_level = SOL_IP;
|
||||
cmh->cmsg_type = IP_PKTINFO;
|
||||
pki->ipi_ifindex = 0;
|
||||
pki->ipi_spec_dst.s_addr = rqstp->rq_daddr.addr.s_addr;
|
||||
pki->ipi_spec_dst.s_addr =
|
||||
svc_daddr_in(rqstp)->sin_addr.s_addr;
|
||||
cmh->cmsg_len = CMSG_LEN(sizeof(*pki));
|
||||
}
|
||||
break;
|
||||
|
||||
case AF_INET6: {
|
||||
struct in6_pktinfo *pki = CMSG_DATA(cmh);
|
||||
struct sockaddr_in6 *daddr = svc_daddr_in6(rqstp);
|
||||
|
||||
cmh->cmsg_level = SOL_IPV6;
|
||||
cmh->cmsg_type = IPV6_PKTINFO;
|
||||
pki->ipi6_ifindex = 0;
|
||||
ipv6_addr_copy(&pki->ipi6_addr,
|
||||
&rqstp->rq_daddr.addr6);
|
||||
pki->ipi6_ifindex = daddr->sin6_scope_id;
|
||||
ipv6_addr_copy(&pki->ipi6_addr, &daddr->sin6_addr);
|
||||
cmh->cmsg_len = CMSG_LEN(sizeof(*pki));
|
||||
}
|
||||
break;
|
||||
|
@ -498,9 +499,13 @@ static int svc_udp_get_dest_address4(struct svc_rqst *rqstp,
|
|||
struct cmsghdr *cmh)
|
||||
{
|
||||
struct in_pktinfo *pki = CMSG_DATA(cmh);
|
||||
struct sockaddr_in *daddr = svc_daddr_in(rqstp);
|
||||
|
||||
if (cmh->cmsg_type != IP_PKTINFO)
|
||||
return 0;
|
||||
rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr;
|
||||
|
||||
daddr->sin_family = AF_INET;
|
||||
daddr->sin_addr.s_addr = pki->ipi_spec_dst.s_addr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -511,9 +516,14 @@ static int svc_udp_get_dest_address6(struct svc_rqst *rqstp,
|
|||
struct cmsghdr *cmh)
|
||||
{
|
||||
struct in6_pktinfo *pki = CMSG_DATA(cmh);
|
||||
struct sockaddr_in6 *daddr = svc_daddr_in6(rqstp);
|
||||
|
||||
if (cmh->cmsg_type != IPV6_PKTINFO)
|
||||
return 0;
|
||||
ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr);
|
||||
|
||||
daddr->sin6_family = AF_INET6;
|
||||
ipv6_addr_copy(&daddr->sin6_addr, &pki->ipi6_addr);
|
||||
daddr->sin6_scope_id = pki->ipi6_ifindex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -614,6 +624,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
|
|||
skb_free_datagram_locked(svsk->sk_sk, skb);
|
||||
return 0;
|
||||
}
|
||||
rqstp->rq_daddrlen = svc_addr_len(svc_daddr(rqstp));
|
||||
|
||||
if (skb_is_nonlinear(skb)) {
|
||||
/* we have to copy */
|
||||
|
|
Loading…
Reference in New Issue