Commit 1442d167 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 7e0bb71e 345c2842
...@@ -51,8 +51,6 @@ ...@@ -51,8 +51,6 @@
#define F_EXLCK 16 /* or 3 */ #define F_EXLCK 16 /* or 3 */
#define F_SHLCK 32 /* or 4 */ #define F_SHLCK 32 /* or 4 */
#define F_INPROGRESS 64
#include <asm-generic/fcntl.h> #include <asm-generic/fcntl.h>
#endif #endif
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
#include <linux/dirent.h> #include <linux/dirent.h>
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/highuid.h> #include <linux/highuid.h>
#include <linux/nfsd/syscall.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/tsacct_kern.h> #include <linux/tsacct_kern.h>
......
...@@ -316,14 +316,8 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, ...@@ -316,14 +316,8 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
struct hlist_node *pos; struct hlist_node *pos;
struct nlm_host *host = NULL; struct nlm_host *host = NULL;
struct nsm_handle *nsm = NULL; struct nsm_handle *nsm = NULL;
struct sockaddr_in sin = { struct sockaddr *src_sap = svc_daddr(rqstp);
.sin_family = AF_INET, size_t src_len = rqstp->rq_daddrlen;
};
struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
};
struct sockaddr *src_sap;
size_t src_len = rqstp->rq_addrlen;
struct nlm_lookup_host_info ni = { struct nlm_lookup_host_info ni = {
.server = 1, .server = 1,
.sap = svc_addr(rqstp), .sap = svc_addr(rqstp),
...@@ -340,21 +334,6 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, ...@@ -340,21 +334,6 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
mutex_lock(&nlm_host_mutex); 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)) if (time_after_eq(jiffies, next_gc))
nlm_gc_hosts(); nlm_gc_hosts();
......
...@@ -282,7 +282,7 @@ int lockd_up(void) ...@@ -282,7 +282,7 @@ int lockd_up(void)
/* /*
* Create the kernel thread and wait for it to start. * 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)) { if (IS_ERR(nlmsvc_rqst)) {
error = PTR_ERR(nlmsvc_rqst); error = PTR_ERR(nlmsvc_rqst);
nlmsvc_rqst = NULL; nlmsvc_rqst = NULL;
......
...@@ -133,6 +133,20 @@ ...@@ -133,6 +133,20 @@
#define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK) #define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
#define IS_LEASE(fl) (fl->fl_flags & FL_LEASE) #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 leases_enable = 1;
int lease_break_time = 45; int lease_break_time = 45;
...@@ -1119,6 +1133,17 @@ int locks_mandatory_area(int read_write, struct inode *inode, ...@@ -1119,6 +1133,17 @@ int locks_mandatory_area(int read_write, struct inode *inode,
EXPORT_SYMBOL(locks_mandatory_area); 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 */ /* We already had a lease on this file; just change its type */
int lease_modify(struct file_lock **before, int arg) int lease_modify(struct file_lock **before, int arg)
{ {
...@@ -1127,6 +1152,7 @@ int lease_modify(struct file_lock **before, int arg) ...@@ -1127,6 +1152,7 @@ int lease_modify(struct file_lock **before, int arg)
if (error) if (error)
return error; return error;
lease_clear_pending(fl, arg);
locks_wake_up_blocks(fl); locks_wake_up_blocks(fl);
if (arg == F_UNLCK) if (arg == F_UNLCK)
locks_delete_lock(before); locks_delete_lock(before);
...@@ -1135,19 +1161,25 @@ int lease_modify(struct file_lock **before, int arg) ...@@ -1135,19 +1161,25 @@ int lease_modify(struct file_lock **before, int arg)
EXPORT_SYMBOL(lease_modify); 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) static void time_out_leases(struct inode *inode)
{ {
struct file_lock **before; struct file_lock **before;
struct file_lock *fl; struct file_lock *fl;
before = &inode->i_flock; before = &inode->i_flock;
while ((fl = *before) && IS_LEASE(fl) && (fl->fl_type & F_INPROGRESS)) { while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) {
if ((fl->fl_break_time == 0) if (past_time(fl->fl_downgrade_time))
|| time_before(jiffies, fl->fl_break_time)) { lease_modify(before, F_RDLCK);
before = &fl->fl_next; if (past_time(fl->fl_break_time))
continue; lease_modify(before, F_UNLCK);
}
lease_modify(before, fl->fl_type & ~F_INPROGRESS);
if (fl == *before) /* lease_modify may have freed fl */ if (fl == *before) /* lease_modify may have freed fl */
before = &fl->fl_next; before = &fl->fl_next;
} }
...@@ -1165,7 +1197,7 @@ static void time_out_leases(struct inode *inode) ...@@ -1165,7 +1197,7 @@ static void time_out_leases(struct inode *inode)
*/ */
int __break_lease(struct inode *inode, unsigned int mode) 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 *new_fl, *flock;
struct file_lock *fl; struct file_lock *fl;
unsigned long break_time; unsigned long break_time;
...@@ -1182,24 +1214,13 @@ int __break_lease(struct inode *inode, unsigned int mode) ...@@ -1182,24 +1214,13 @@ int __break_lease(struct inode *inode, unsigned int mode)
if ((flock == NULL) || !IS_LEASE(flock)) if ((flock == NULL) || !IS_LEASE(flock))
goto out; goto out;
if (!locks_conflict(flock, new_fl))
goto out;
for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next)
if (fl->fl_owner == current->files) if (fl->fl_owner == current->files)
i_have_this_lease = 1; 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 if (IS_ERR(new_fl) && !i_have_this_lease
&& ((mode & O_NONBLOCK) == 0)) { && ((mode & O_NONBLOCK) == 0)) {
error = PTR_ERR(new_fl); error = PTR_ERR(new_fl);
...@@ -1214,12 +1235,18 @@ int __break_lease(struct inode *inode, unsigned int mode) ...@@ -1214,12 +1235,18 @@ int __break_lease(struct inode *inode, unsigned int mode)
} }
for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
if (fl->fl_type != future) { if (want_write) {
fl->fl_type = future; if (fl->fl_flags & FL_UNLOCK_PENDING)
continue;
fl->fl_flags |= FL_UNLOCK_PENDING;
fl->fl_break_time = break_time; fl->fl_break_time = break_time;
/* lease must have lmops break callback */ } else {
fl->fl_lmops->lm_break(fl); 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)) { if (i_have_this_lease || (mode & O_NONBLOCK)) {
...@@ -1243,10 +1270,13 @@ int __break_lease(struct inode *inode, unsigned int mode) ...@@ -1243,10 +1270,13 @@ int __break_lease(struct inode *inode, unsigned int mode)
if (error >= 0) { if (error >= 0) {
if (error == 0) if (error == 0)
time_out_leases(inode); 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); for (flock = inode->i_flock; flock && IS_LEASE(flock);
flock = flock->fl_next) { flock = flock->fl_next) {
if (flock->fl_type & F_INPROGRESS) if (locks_conflict(new_fl, flock))
goto restart; goto restart;
} }
error = 0; error = 0;
...@@ -1314,7 +1344,7 @@ int fcntl_getlease(struct file *filp) ...@@ -1314,7 +1344,7 @@ int fcntl_getlease(struct file *filp)
for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl); for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl);
fl = fl->fl_next) { fl = fl->fl_next) {
if (fl->fl_file == filp) { if (fl->fl_file == filp) {
type = fl->fl_type & ~F_INPROGRESS; type = target_leasetype(fl);
break; break;
} }
} }
...@@ -1322,50 +1352,23 @@ int fcntl_getlease(struct file *filp) ...@@ -1322,50 +1352,23 @@ int fcntl_getlease(struct file *filp)
return type; return type;
} }
/** int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
* generic_setlease - sets a lease on an open file
* @filp: file pointer
* @arg: type of lease to obtain
* @flp: input - file_lock to use, output - file_lock inserted
*
* The (input) flp->fl_lmops->lm_break function is required
* by break_lease().
*
* Called with file_lock_lock held.
*/
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
{ {
struct file_lock *fl, **before, **my_before = NULL, *lease; struct file_lock *fl, **before, **my_before = NULL, *lease;
struct dentry *dentry = filp->f_path.dentry; struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int error, rdlease_count = 0, wrlease_count = 0; int error;
lease = *flp; lease = *flp;
error = -EACCES; error = -EAGAIN;
if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE)) if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
goto out;
error = -EINVAL;
if (!S_ISREG(inode->i_mode))
goto out; goto out;
error = security_file_lock(filp, arg); if ((arg == F_WRLCK)
if (error) && ((dentry->d_count > 1)
|| (atomic_read(&inode->i_count) > 1)))
goto out; goto out;
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;
}
/* /*
* At this point, we know that if there is an exclusive * At this point, we know that if there is an exclusive
* lease on this file, then we hold it on this filp * lease on this file, then we hold it on this filp
...@@ -1374,27 +1377,28 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) ...@@ -1374,27 +1377,28 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
* then the file is not open by anyone (including us) * then the file is not open by anyone (including us)
* except for this filp. * except for this filp.
*/ */
error = -EAGAIN;
for (before = &inode->i_flock; for (before = &inode->i_flock;
((fl = *before) != NULL) && IS_LEASE(fl); ((fl = *before) != NULL) && IS_LEASE(fl);
before = &fl->fl_next) { before = &fl->fl_next) {
if (fl->fl_file == filp) if (fl->fl_file == filp) {
my_before = before; my_before = before;
else if (fl->fl_type == (F_INPROGRESS | F_UNLCK)) continue;
/* }
* Someone is in the process of opening this /*
* file for writing so we may not take an * No exclusive leases if someone else has a lease on
* exclusive lease on it. * this file:
*/ */
wrlease_count++; if (arg == F_WRLCK)
else goto out;
rdlease_count++; /*
* 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;
} }
error = -EAGAIN;
if ((arg == F_RDLCK && (wrlease_count > 0)) ||
(arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0)))
goto out;
if (my_before != NULL) { if (my_before != NULL) {
error = lease->fl_lmops->lm_change(my_before, arg); error = lease->fl_lmops->lm_change(my_before, arg);
if (!error) if (!error)
...@@ -1402,9 +1406,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) ...@@ -1402,9 +1406,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
goto out; goto out;
} }
if (arg == F_UNLCK)
goto out;
error = -EINVAL; error = -EINVAL;
if (!leases_enable) if (!leases_enable)
goto out; goto out;
...@@ -1415,6 +1416,62 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) ...@@ -1415,6 +1416,62 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
out: out:
return error; 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
* @arg: type of lease to obtain
* @flp: input - file_lock to use, output - file_lock inserted
*
* The (input) flp->fl_lmops->lm_break function is required
* by break_lease().
*
* Called with file_lock_lock held.
*/
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
{
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
int error;
if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE))
return -EACCES;
if (!S_ISREG(inode->i_mode))
return -EINVAL;
error = security_file_lock(filp, arg);
if (error)
return error;
time_out_leases(inode);
BUG_ON(!(*flp)->fl_lmops->lm_break);
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();
}
}
EXPORT_SYMBOL(generic_setlease); EXPORT_SYMBOL(generic_setlease);
static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease) static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
...@@ -2126,7 +2183,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, ...@@ -2126,7 +2183,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
} }
} else if (IS_LEASE(fl)) { } else if (IS_LEASE(fl)) {
seq_printf(f, "LEASE "); seq_printf(f, "LEASE ");
if (fl->fl_type & F_INPROGRESS) if (lease_breaking(fl))
seq_printf(f, "BREAKING "); seq_printf(f, "BREAKING ");
else if (fl->fl_file) else if (fl->fl_file)
seq_printf(f, "ACTIVE "); seq_printf(f, "ACTIVE ");
...@@ -2142,7 +2199,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, ...@@ -2142,7 +2199,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
: (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
} else { } else {
seq_printf(f, "%s ", seq_printf(f, "%s ",
(fl->fl_type & F_INPROGRESS) (lease_breaking(fl))
? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ " ? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
: (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
} }
......
...@@ -125,7 +125,7 @@ nfs4_callback_up(struct svc_serv *serv) ...@@ -125,7 +125,7 @@ nfs4_callback_up(struct svc_serv *serv)
else else
goto out_err; 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: out_err:
if (ret == 0) if (ret == 0)
...@@ -199,7 +199,7 @@ nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) ...@@ -199,7 +199,7 @@ nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
INIT_LIST_HEAD(&serv->sv_cb_list); INIT_LIST_HEAD(&serv->sv_cb_list);
spin_lock_init(&serv->sv_cb_lock); spin_lock_init(&serv->sv_cb_lock);
init_waitqueue_head(&serv->sv_cb_waitq); 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)) { if (IS_ERR(rqstp)) {
svc_xprt_put(serv->sv_bc_xprt); svc_xprt_put(serv->sv_bc_xprt);
serv->sv_bc_xprt = NULL; serv->sv_bc_xprt = NULL;
......
...@@ -336,11 +336,12 @@ static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, ...@@ -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 *sin1 = (const struct sockaddr_in6 *)sa1;
const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2;
if (ipv6_addr_scope(&sin1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL && if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr))
sin1->sin6_scope_id != sin2->sin6_scope_id)
return 0; 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) */ #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */
static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1,
......
...@@ -13,30 +13,6 @@ ...@@ -13,30 +13,6 @@
struct idmap; 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 { enum nfs4_client_state {
NFS4CLNT_MANAGER_RUNNING = 0, NFS4CLNT_MANAGER_RUNNING = 0,
NFS4CLNT_CHECK_LEASE, NFS4CLNT_CHECK_LEASE,
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/nfsd/syscall.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include "nfsd.h" #include "nfsd.h"
...@@ -318,7 +317,6 @@ static void svc_export_put(struct kref *ref) ...@@ -318,7 +317,6 @@ static void svc_export_put(struct kref *ref)
struct svc_export *exp = container_of(ref, struct svc_export, h.ref); struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
path_put(&exp->ex_path); path_put(&exp->ex_path);
auth_domain_put(exp->ex_client); auth_domain_put(exp->ex_client);
kfree(exp->ex_pathname);
nfsd4_fslocs_free(&exp->ex_fslocs); nfsd4_fslocs_free(&exp->ex_fslocs);
kfree(exp); kfree(exp);
} }
...@@ -528,11 +526,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) ...@@ -528,11 +526,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
exp.ex_client = dom; exp.ex_client = dom;
err = -ENOMEM;
exp.ex_pathname = kstrdup(buf, GFP_KERNEL);
if (!exp.ex_pathname)
goto out2;
/* expiry */ /* expiry */
err = -EINVAL; err = -EINVAL;
exp.h.expiry_time = get_expiry(&mesg); exp.h.expiry_time = get_expiry(&mesg);
...@@ -613,8 +606,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) ...@@ -613,8 +606,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
nfsd4_fslocs_free(&exp.ex_fslocs); nfsd4_fslocs_free(&exp.ex_fslocs);
kfree(exp.ex_uuid); kfree(exp.ex_uuid);
out3: out3:
kfree(exp.ex_pathname);
out2:
path_put(&exp.ex_path); path_put(&exp.ex_path);
out1: out1:
auth_domain_put(dom); auth_domain_put(dom);
...@@ -678,7 +669,6 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) ...@@ -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_client = item->ex_client;
new->ex_path.dentry = dget(item->ex_path.dentry); new->ex_path.dentry = dget(item->ex_path.dentry);
new->ex_path.mnt = mntget(item->ex_path.mnt); new->ex_path.mnt = mntget(item->ex_path.mnt);
new->ex_pathname = NULL;
new->ex_fslocs.locations = NULL; new->ex_fslocs.locations = NULL;
new->ex_fslocs.locations_count = 0; new->ex_fslocs.locations_count = 0;
new->ex_fslocs.migrated = 0; new->ex_fslocs.migrated = 0;
...@@ -696,8 +686,6 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) ...@@ -696,8 +686,6 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem)
new->ex_fsid = item->ex_fsid; new->ex_fsid = item->ex_fsid;
new->ex_uuid = item->ex_uuid; new->ex_uuid = item->ex_uuid;
item->ex_uuid = NULL; item->ex_uuid = NULL;
new->ex_pathname = item->ex_pathname;
item->ex_pathname = NULL;
new->ex_fslocs.locations = item->ex_fslocs.locations; new->ex_fslocs.locations = item->ex_fslocs.locations;
item->ex_fslocs.locations = NULL; item->ex_fslocs.locations = NULL;
new->ex_fslocs.locations_count = item->ex_fslocs.locations_count; new->ex_fslocs.locations_count = item->ex_fslocs.locations_count;
...@@ -1010,7 +998,7 @@ rqst_exp_parent(struct svc_rqst *rqstp, struct path *path) ...@@ -1010,7 +998,7 @@ rqst_exp_parent(struct svc_rqst *rqstp, struct path *path)
return exp; 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]; u32 fsidv[2];
...@@ -1030,7 +1018,7 @@ exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp) ...@@ -1030,7 +1018,7 @@ exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp)
struct svc_export *exp; struct svc_export *exp;
__be32 rv; __be32 rv;
exp = find_fsidzero_export(rqstp); exp = rqst_find_fsidzero_export(rqstp);
if (IS_ERR(exp)) if (IS_ERR(exp))
return nfserrno(PTR_ERR(exp)); return nfserrno(PTR_ERR(exp));
rv = fh_compose(fhp, exp, exp->ex_path.dentry, NULL); rv = fh_compose(fhp, exp, exp->ex_path.dentry, NULL);
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#define NFSDDBG_FACILITY NFSDDBG_PROC #define NFSDDBG_FACILITY NFSDDBG_PROC
static void nfsd4_mark_cb_fault(struct nfs4_client *, int reason);
#define NFSPROC4_CB_NULL 0 #define NFSPROC4_CB_NULL 0
#define NFSPROC4_CB_COMPOUND 1 #define NFSPROC4_CB_COMPOUND 1
...@@ -351,7 +353,7 @@ static void encode_cb_recall4args(struct xdr_stream *xdr, ...@@ -351,7 +353,7 @@ static void encode_cb_recall4args(struct xdr_stream *xdr,
__be32 *p; __be32 *p;
encode_nfs_cb_opnum4(xdr, OP_CB_RECALL); 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_reserve_space(xdr, 4);
*p++ = xdr_zero; /* truncate */ *p++ = xdr_zero; /* truncate */
...@@ -460,6 +462,8 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr, ...@@ -460,6 +462,8 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
*/ */
status = 0; status = 0;
out: out:
if (status)
nfsd4_mark_cb_fault(cb->cb_clp, status);
return status; return status;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
...@@ -686,6 +690,12 @@ static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason) ...@@ -686,6 +690,12 @@ static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
warn_no_callback_path(clp, 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) 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); 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) ...@@ -787,7 +797,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
{ {
struct nfsd4_callback *cb = calldata; struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); 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; u32 minorversion = clp->cl_minorversion;
cb->cb_minorversion = minorversion; cb->cb_minorversion = minorversion;
...@@ -809,7 +819,7 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) ...@@ -809,7 +819,7 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
{ {
struct nfsd4_callback *cb = calldata; struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); 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__, dprintk("%s: minorversion=%d\n", __func__,
clp->cl_minorversion); clp->cl_minorversion);
...@@ -832,7 +842,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) ...@@ -832,7 +842,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
{ {
struct nfsd4_callback *cb = calldata; struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); 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; struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
nfsd4_cb_done(task, calldata); nfsd4_cb_done(task, calldata);
...@@ -1006,7 +1016,7 @@ void nfsd4_do_callback_rpc(struct work_struct *w) ...@@ -1006,7 +1016,7 @@ void nfsd4_do_callback_rpc(struct work_struct *w)
void nfsd4_cb_recall(struct nfs4_delegation *dp) void nfsd4_cb_recall(struct nfs4_delegation *dp)
{ {
struct nfsd4_callback *cb = &dp->dl_recall; 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; dp->dl_retries = 1;
cb->cb_op = dp; cb->cb_op = dp;
......
This diff is collapsed.
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
/* Globals */ /* Globals */
static struct file *rec_file; static struct file *rec_file;
static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
static int static int
nfs4_save_creds(const struct cred **original_creds) nfs4_save_creds(const struct cred **original_creds)
...@@ -88,7 +89,7 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) ...@@ -88,7 +89,7 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
struct xdr_netobj cksum; struct xdr_netobj cksum;
struct hash_desc desc; struct hash_desc desc;
struct scatterlist sg; struct scatterlist sg;
__be32 status = nfserr_resource; __be32 status = nfserr_jukebox;
dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
clname->len, clname->data); clname->len, clname->data);
...@@ -129,6 +130,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) ...@@ -129,6 +130,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
if (!rec_file || clp->cl_firststate) if (!rec_file || clp->cl_firststate)
return 0; return 0;
clp->cl_firststate = 1;
status = nfs4_save_creds(&original_cred); status = nfs4_save_creds(&original_cred);
if (status < 0) if (status < 0)
return status; return status;
...@@ -143,10 +145,8 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) ...@@ -143,10 +145,8 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
goto out_unlock; goto out_unlock;
} }
status = -EEXIST; status = -EEXIST;
if (dentry->d_inode) { if (dentry->d_inode)
dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n");
goto out_put; goto out_put;
}
status = mnt_want_write(rec_file->f_path.mnt); status = mnt_want_write(rec_file->f_path.mnt);
if (status) if (status)
goto out_put; goto out_put;
...@@ -156,12 +156,14 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) ...@@ -156,12 +156,14 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
dput(dentry); dput(dentry);
out_unlock: out_unlock:
mutex_unlock(&dir->d_inode->i_mutex); mutex_unlock(&dir->d_inode->i_mutex);
if (status == 0) { if (status == 0)
clp->cl_firststate = 1;
vfs_fsync(rec_file, 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); nfs4_reset_creds(original_cred);
dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status);
return status; return status;
} }
...@@ -354,13 +356,13 @@ nfsd4_recdir_load(void) { ...@@ -354,13 +356,13 @@ nfsd4_recdir_load(void) {
*/ */
void void
nfsd4_init_recdir(char *rec_dirname) nfsd4_init_recdir()
{ {
const struct cred *original_cred; const struct cred *original_cred;
int status; int status;
printk("NFSD: Using %s as the NFSv4 state recovery directory\n", printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
rec_dirname); user_recovery_dirname);
BUG_ON(rec_file); BUG_ON(rec_file);
...@@ -372,10 +374,10 @@ nfsd4_init_recdir(char *rec_dirname) ...@@ -372,10 +374,10 @@ nfsd4_init_recdir(char *rec_dirname)
return; 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)) { if (IS_ERR(rec_file)) {
printk("NFSD: unable to find recovery directory %s\n", printk("NFSD: unable to find recovery directory %s\n",
rec_dirname); user_recovery_dirname);
rec_file = NULL; rec_file = NULL;
} }
...@@ -390,3 +392,30 @@ nfsd4_shutdown_recdir(void) ...@@ -390,3 +392,30 @@ nfsd4_shutdown_recdir(void)
fput(rec_file); fput(rec_file);
rec_file = NULL; 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;
}
This diff is collapsed.
This diff is collapsed.
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/nfsd/syscall.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/gss_api.h> #include <linux/sunrpc/gss_api.h>
......
...@@ -11,13 +11,39 @@ ...@@ -11,13 +11,39 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/mount.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/debug.h>
#include <linux/nfsd/export.h> #include <linux/nfsd/export.h>
#include <linux/nfsd/stats.h> #include <linux/nfsd/stats.h>
/* /*
* nfsd version * nfsd version
*/ */
#define NFSD_SUPPORTED_MINOR_VERSION 1 #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 { struct readdir_cd {
__be32 err; /* 0, nfserr, or nfserr_eof */ __be32 err; /* 0, nfserr, or nfserr_eof */
...@@ -335,6 +361,13 @@ static inline u32 nfsd_suppattrs2(u32 minorversion) ...@@ -335,6 +361,13 @@ static inline u32 nfsd_suppattrs2(u32 minorversion)
#define NFSD_SUPPATTR_EXCLCREAT_WORD2 \ #define NFSD_SUPPATTR_EXCLCREAT_WORD2 \
NFSD_WRITEABLE_ATTRS_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 /* CONFIG_NFSD_V4 */
#endif /* LINUX_NFSD_NFSD_H */ #endif /* LINUX_NFSD_NFSD_H */
...@@ -59,28 +59,25 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry) ...@@ -59,28 +59,25 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
* the write call). * the write call).
*/ */
static inline __be32 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 */ mode &= S_IFMT;
if (type > 0 && (mode & S_IFMT) != type) {
if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) if (requested == 0) /* the caller doesn't care */
return nfserr_symlink; return nfs_ok;
else if (type == S_IFDIR) if (mode == requested)
return nfserr_notdir; return nfs_ok;
else if ((mode & S_IFMT) == S_IFDIR) /*
return nfserr_isdir; * v4 has an error more specific than err_notdir which we should
else * return in preference to err_notdir:
return nfserr_inval; */
} if (rqstp->rq_vers == 4 && mode == S_IFLNK)
if (type < 0 && (mode & S_IFMT) == -type) { return nfserr_symlink;
if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) if (requested == S_IFDIR)
return nfserr_symlink; return nfserr_notdir;
else if (type == -S_IFDIR) if (mode == S_IFDIR)
return nfserr_isdir; return nfserr_isdir;
else return nfserr_inval;
return nfserr_notdir;
}
return 0;
} }
static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
......
This diff is collapsed.
...@@ -168,6 +168,8 @@ int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp) ...@@ -168,6 +168,8 @@ int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
{ {
if (d_mountpoint(dentry)) if (d_mountpoint(dentry))
return 1; return 1;
if (nfsd4_is_junction(dentry))
return 1;
if (!(exp->ex_flags & NFSEXP_V4ROOT)) if (!(exp->ex_flags & NFSEXP_V4ROOT))
return 0; return 0;
return dentry->d_inode != NULL; return dentry->d_inode != NULL;
...@@ -502,7 +504,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -502,7 +504,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int flags = 0; unsigned int flags = 0;
/* Get inode */ /* Get inode */
error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR); error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
if (error) if (error)
return error; return error;
...@@ -592,6 +594,22 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac ...@@ -592,6 +594,22 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac
return error; 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) */ #endif /* defined(CONFIG_NFSD_V4) */
#ifdef CONFIG_NFSD_V3 #ifdef CONFIG_NFSD_V3
...@@ -1352,7 +1370,7 @@ __be32 ...@@ -1352,7 +1370,7 @@ __be32
do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *fname, int flen, struct iattr *iap, char *fname, int flen, struct iattr *iap,
struct svc_fh *resfhp, int createmode, u32 *verifier, struct svc_fh *resfhp, int createmode, u32 *verifier,
int *truncp, int *created) bool *truncp, bool *created)
{ {
struct dentry *dentry, *dchild = NULL; struct dentry *dentry, *dchild = NULL;
struct inode *dirp; struct inode *dirp;
...@@ -1632,10 +1650,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, ...@@ -1632,10 +1650,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE); err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE);
if (err) if (err)
goto out; goto out;
err = fh_verify(rqstp, tfhp, -S_IFDIR, NFSD_MAY_NOP); err = fh_verify(rqstp, tfhp, 0, NFSD_MAY_NOP);
if (err) if (err)
goto out; goto out;
err = nfserr_isdir;
if (S_ISDIR(tfhp->fh_dentry->d_inode->i_mode))
goto out;
err = nfserr_perm; err = nfserr_perm;
if (!len) if (!len)
goto out; goto out;
...@@ -2114,7 +2134,8 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, ...@@ -2114,7 +2134,8 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
/* Allow read access to binaries even when mode 111 */ /* Allow read access to binaries even when mode 111 */
if (err == -EACCES && S_ISREG(inode->i_mode) && 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); err = inode_permission(inode, MAY_EXEC);
return err? nfserrno(err) : 0; return err? nfserrno(err) : 0;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
header-y += const.h
header-y += debug.h header-y += debug.h
header-y += export.h header-y += export.h
header-y += nfsfh.h header-y += nfsfh.h
header-y += stats.h header-y += stats.h
header-y += syscall.h
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment