Revert "Merge 5.10.220 into android12-5.10-lts"

This reverts commit 87a7f35a24, reversing
changes made to 640645c85b.

5.10.220 is a bunch of vfs and nfs changes that are not needed in
Android systems, so revert the whole lot all at once, except for the
version number bump.

Change-Id: If28dc2231f27d326d3730716f23545dd0a2cdc75
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman 2024-07-16 16:32:09 +00:00
parent 87a7f35a24
commit 88eb084d18
183 changed files with 8839 additions and 13928 deletions

View File

@ -62,7 +62,7 @@ the fdtable structure -
be held. be held.
4. To look up the file structure given an fd, a reader 4. To look up the file structure given an fd, a reader
must use either lookup_fd_rcu() or files_lookup_fd_rcu() APIs. These must use either fcheck() or fcheck_files() APIs. These
take care of barrier requirements due to lock-free lookup. take care of barrier requirements due to lock-free lookup.
An example:: An example::
@ -70,7 +70,7 @@ the fdtable structure -
struct file *file; struct file *file;
rcu_read_lock(); rcu_read_lock();
file = lookup_fd_rcu(fd); file = fcheck(fd);
if (file) { if (file) {
... ...
} }
@ -84,7 +84,7 @@ the fdtable structure -
on ->f_count:: on ->f_count::
rcu_read_lock(); rcu_read_lock();
file = files_lookup_fd_rcu(files, fd); file = fcheck_files(files, fd);
if (file) { if (file) {
if (atomic_long_inc_not_zero(&file->f_count)) if (atomic_long_inc_not_zero(&file->f_count))
*fput_needed = 1; *fput_needed = 1;
@ -104,7 +104,7 @@ the fdtable structure -
lock-free, they must be installed using rcu_assign_pointer() lock-free, they must be installed using rcu_assign_pointer()
API. If they are looked up lock-free, rcu_dereference() API. If they are looked up lock-free, rcu_dereference()
must be used. However it is advisable to use files_fdtable() must be used. However it is advisable to use files_fdtable()
and lookup_fd_rcu()/files_lookup_fd_rcu() which take care of these issues. and fcheck()/fcheck_files() which take care of these issues.
7. While updating, the fdtable pointer must be looked up while 7. While updating, the fdtable pointer must be looked up while
holding files->file_lock. If ->file_lock is dropped, then holding files->file_lock. If ->file_lock is dropped, then

View File

@ -433,21 +433,17 @@ prototypes::
void (*lm_break)(struct file_lock *); /* break_lease callback */ void (*lm_break)(struct file_lock *); /* break_lease callback */
int (*lm_change)(struct file_lock **, int); int (*lm_change)(struct file_lock **, int);
bool (*lm_breaker_owns_lease)(struct file_lock *); bool (*lm_breaker_owns_lease)(struct file_lock *);
bool (*lm_lock_expirable)(struct file_lock *);
void (*lm_expire_lock)(void);
locking rules: locking rules:
====================== ============= ================= ========= ====================== ============= ================= =========
ops flc_lock blocked_lock_lock may block ops inode->i_lock blocked_lock_lock may block
====================== ============= ================= ========= ====================== ============= ================= =========
lm_notify: no yes no lm_notify: yes yes no
lm_grant: no no no lm_grant: no no no
lm_break: yes no no lm_break: yes no no
lm_change yes no no lm_change yes no no
lm_breaker_owns_lease: yes no no lm_breaker_owns_lease: no no no
lm_lock_expirable yes no no
lm_expire_lock no no yes
====================== ============= ================= ========= ====================== ============= ================= =========
buffer_head buffer_head

View File

@ -154,11 +154,6 @@ struct which has the following members:
to find potential names, and matches inode numbers to find the correct to find potential names, and matches inode numbers to find the correct
match. match.
flags
Some filesystems may need to be handled differently than others. The
export_operations struct also includes a flags field that allows the
filesystem to communicate such information to nfsd. See the Export
Operations Flags section below for more explanation.
A filehandle fragment consists of an array of 1 or more 4byte words, A filehandle fragment consists of an array of 1 or more 4byte words,
together with a one byte "type". together with a one byte "type".
@ -168,76 +163,3 @@ generated by encode_fh, in which case it will have been padded with
nuls. Rather, the encode_fh routine should choose a "type" which nuls. Rather, the encode_fh routine should choose a "type" which
indicates the decode_fh how much of the filehandle is valid, and how indicates the decode_fh how much of the filehandle is valid, and how
it should be interpreted. it should be interpreted.
Export Operations Flags
-----------------------
In addition to the operation vector pointers, struct export_operations also
contains a "flags" field that allows the filesystem to communicate to nfsd
that it may want to do things differently when dealing with it. The
following flags are defined:
EXPORT_OP_NOWCC - disable NFSv3 WCC attributes on this filesystem
RFC 1813 recommends that servers always send weak cache consistency
(WCC) data to the client after each operation. The server should
atomically collect attributes about the inode, do an operation on it,
and then collect the attributes afterward. This allows the client to
skip issuing GETATTRs in some situations but means that the server
is calling vfs_getattr for almost all RPCs. On some filesystems
(particularly those that are clustered or networked) this is expensive
and atomicity is difficult to guarantee. This flag indicates to nfsd
that it should skip providing WCC attributes to the client in NFSv3
replies when doing operations on this filesystem. Consider enabling
this on filesystems that have an expensive ->getattr inode operation,
or when atomicity between pre and post operation attribute collection
is impossible to guarantee.
EXPORT_OP_NOSUBTREECHK - disallow subtree checking on this fs
Many NFS operations deal with filehandles, which the server must then
vet to ensure that they live inside of an exported tree. When the
export consists of an entire filesystem, this is trivial. nfsd can just
ensure that the filehandle live on the filesystem. When only part of a
filesystem is exported however, then nfsd must walk the ancestors of the
inode to ensure that it's within an exported subtree. This is an
expensive operation and not all filesystems can support it properly.
This flag exempts the filesystem from subtree checking and causes
exportfs to get back an error if it tries to enable subtree checking
on it.
EXPORT_OP_CLOSE_BEFORE_UNLINK - always close cached files before unlinking
On some exportable filesystems (such as NFS) unlinking a file that
is still open can cause a fair bit of extra work. For instance,
the NFS client will do a "sillyrename" to ensure that the file
sticks around while it's still open. When reexporting, that open
file is held by nfsd so we usually end up doing a sillyrename, and
then immediately deleting the sillyrenamed file just afterward when
the link count actually goes to zero. Sometimes this delete can race
with other operations (for instance an rmdir of the parent directory).
This flag causes nfsd to close any open files for this inode _before_
calling into the vfs to do an unlink or a rename that would replace
an existing file.
EXPORT_OP_REMOTE_FS - Backing storage for this filesystem is remote
PF_LOCAL_THROTTLE exists for loopback NFSD, where a thread needs to
write to one bdi (the final bdi) in order to free up writes queued
to another bdi (the client bdi). Such threads get a private balance
of dirty pages so that dirty pages for the client bdi do not imact
the daemon writing to the final bdi. For filesystems whose durable
storage is not local (such as exported NFS filesystems), this
constraint has negative consequences. EXPORT_OP_REMOTE_FS enables
an export to disable writeback throttling.
EXPORT_OP_NOATOMIC_ATTR - Filesystem does not update attributes atomically
EXPORT_OP_NOATOMIC_ATTR indicates that the exported filesystem
cannot provide the semantics required by the "atomic" boolean in
NFSv4's change_info4. This boolean indicates to a client whether the
returned before and after change attributes were obtained atomically
with the respect to the requested metadata operation (UNLINK,
OPEN/CREATE, MKDIR, etc).
EXPORT_OP_FLUSH_ON_CLOSE - Filesystem flushes file data on close(2)
On most filesystems, inodes can remain under writeback after the
file is closed. NFSD relies on client activity or local flusher
threads to handle writeback. Certain filesystems, such as NFS, flush
all of an inode's dirty data on last close. Exports that behave this
way should set EXPORT_OP_FLUSH_ON_CLOSE so that NFSD knows to skip
waiting for writeback when closing such files.

View File

@ -74,7 +74,7 @@ static struct spu_context *coredump_next_context(int *fd)
*fd = n - 1; *fd = n - 1;
rcu_read_lock(); rcu_read_lock();
file = lookup_fd_rcu(*fd); file = fcheck(*fd);
ctx = SPUFS_I(file_inode(file))->i_ctx; ctx = SPUFS_I(file_inode(file))->i_ctx;
get_spu_context(ctx); get_spu_context(ctx);
rcu_read_unlock(); rcu_read_unlock();

View File

@ -74,7 +74,7 @@ static int cryptomgr_probe(void *data)
complete_all(&param->larval->completion); complete_all(&param->larval->completion);
crypto_alg_put(&param->larval->alg); crypto_alg_put(&param->larval->alg);
kfree(param); kfree(param);
module_put_and_kthread_exit(0); module_put_and_exit(0);
} }
static int cryptomgr_schedule_probe(struct crypto_larval *larval) static int cryptomgr_schedule_probe(struct crypto_larval *larval)
@ -209,7 +209,7 @@ static int cryptomgr_test(void *data)
crypto_alg_tested(param->driver, err); crypto_alg_tested(param->driver, err);
kfree(param); kfree(param);
module_put_and_kthread_exit(0); module_put_and_exit(0);
} }
static int cryptomgr_schedule_test(struct crypto_alg *alg) static int cryptomgr_schedule_test(struct crypto_alg *alg)

View File

@ -321,7 +321,7 @@ config LOCKD
config LOCKD_V4 config LOCKD_V4
bool bool
depends on NFSD || NFS_V3 depends on NFSD_V3 || NFS_V3
depends on FILE_LOCKING depends on FILE_LOCKING
default y default y
@ -334,10 +334,6 @@ config NFS_COMMON
depends on NFSD || NFS_FS || LOCKD depends on NFSD || NFS_FS || LOCKD
default y default y
config NFS_V4_2_SSC_HELPER
bool
default y if NFS_V4_2
source "net/sunrpc/Kconfig" source "net/sunrpc/Kconfig"
source "fs/ceph/Kconfig" source "fs/ceph/Kconfig"
source "fs/cifs/Kconfig" source "fs/cifs/Kconfig"

View File

@ -4,10 +4,9 @@
* Copyright 2008 Ian Kent <raven@themaw.net> * Copyright 2008 Ian Kent <raven@themaw.net>
*/ */
#include <linux/module.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/fdtable.h> #include <linux/syscalls.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/nospec.h> #include <linux/nospec.h>
@ -290,7 +289,7 @@ static int autofs_dev_ioctl_closemount(struct file *fp,
struct autofs_sb_info *sbi, struct autofs_sb_info *sbi,
struct autofs_dev_ioctl *param) struct autofs_dev_ioctl *param)
{ {
return close_fd(param->ioctlfd); return ksys_close(param->ioctlfd);
} }
/* /*

View File

@ -412,14 +412,9 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
if (ret < 0) { if (ret < 0) {
cachefiles_io_error(cache, "Rename security error %d", ret); cachefiles_io_error(cache, "Rename security error %d", ret);
} else { } else {
struct renamedata rd = {
.old_dir = d_inode(dir),
.old_dentry = rep,
.new_dir = d_inode(cache->graveyard),
.new_dentry = grave,
};
trace_cachefiles_rename(object, rep, grave, why); trace_cachefiles_rename(object, rep, grave, why);
ret = vfs_rename(&rd); ret = vfs_rename(d_inode(dir), rep,
d_inode(cache->graveyard), grave, NULL, 0);
if (ret != 0 && ret != -ENOMEM) if (ret != 0 && ret != -ENOMEM)
cachefiles_io_error(cache, cachefiles_io_error(cache,
"Rename failed with error %d", ret); "Rename failed with error %d", ret);

View File

@ -1242,7 +1242,7 @@ cifs_demultiplex_thread(void *p)
} }
memalloc_noreclaim_restore(noreclaim_flag); memalloc_noreclaim_restore(noreclaim_flag);
module_put_and_kthread_exit(0); module_put_and_exit(0);
} }
/* extract the host portion of the UNC string */ /* extract the host portion of the UNC string */

View File

@ -587,6 +587,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
int ispipe; int ispipe;
size_t *argv = NULL; size_t *argv = NULL;
int argc = 0; int argc = 0;
struct files_struct *displaced;
/* require nonrelative corefile path and be extra careful */ /* require nonrelative corefile path and be extra careful */
bool need_suid_safe = false; bool need_suid_safe = false;
bool core_dumped = false; bool core_dumped = false;
@ -792,9 +793,11 @@ void do_coredump(const kernel_siginfo_t *siginfo)
} }
/* get us an unshared descriptor table; almost always a no-op */ /* get us an unshared descriptor table; almost always a no-op */
retval = unshare_files(); retval = unshare_files(&displaced);
if (retval) if (retval)
goto close_fail; goto close_fail;
if (displaced)
put_files_struct(displaced);
if (!dump_interrupted()) { if (!dump_interrupted()) {
/* /*
* umh disabled with CONFIG_STATIC_USERMODEHELPER_PATH="" would * umh disabled with CONFIG_STATIC_USERMODEHELPER_PATH="" would

View File

@ -598,7 +598,6 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct dentry *lower_new_dir_dentry; struct dentry *lower_new_dir_dentry;
struct dentry *trap; struct dentry *trap;
struct inode *target_inode; struct inode *target_inode;
struct renamedata rd = {};
if (flags) if (flags)
return -EINVAL; return -EINVAL;
@ -628,12 +627,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
rc = -ENOTEMPTY; rc = -ENOTEMPTY;
goto out_lock; goto out_lock;
} }
rc = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
rd.old_dir = d_inode(lower_old_dir_dentry); d_inode(lower_new_dir_dentry), lower_new_dentry,
rd.old_dentry = lower_old_dentry; NULL, 0);
rd.new_dir = d_inode(lower_new_dir_dentry);
rd.new_dentry = lower_new_dentry;
rc = vfs_rename(&rd);
if (rc) if (rc)
goto out_lock; goto out_lock;
if (target_inode) if (target_inode)

View File

@ -1266,11 +1266,6 @@ int begin_new_exec(struct linux_binprm * bprm)
if (retval) if (retval)
goto out; goto out;
/* Ensure the files table is not shared. */
retval = unshare_files();
if (retval)
goto out;
/* /*
* Must be called _before_ exec_mmap() as bprm->mm is * Must be called _before_ exec_mmap() as bprm->mm is
* not visibile until then. This also enables the update * not visibile until then. This also enables the update
@ -1796,6 +1791,7 @@ static int bprm_execve(struct linux_binprm *bprm,
int fd, struct filename *filename, int flags) int fd, struct filename *filename, int flags)
{ {
struct file *file; struct file *file;
struct files_struct *displaced;
int retval; int retval;
/* /*
@ -1803,10 +1799,14 @@ static int bprm_execve(struct linux_binprm *bprm,
*/ */
io_uring_task_cancel(); io_uring_task_cancel();
retval = prepare_bprm_creds(bprm); retval = unshare_files(&displaced);
if (retval) if (retval)
return retval; return retval;
retval = prepare_bprm_creds(bprm);
if (retval)
goto out_files;
check_unsafe_exec(bprm); check_unsafe_exec(bprm);
current->in_execve = 1; current->in_execve = 1;
@ -1820,14 +1820,11 @@ static int bprm_execve(struct linux_binprm *bprm,
bprm->file = file; bprm->file = file;
/* /*
* Record that a name derived from an O_CLOEXEC fd will be * Record that a name derived from an O_CLOEXEC fd will be
* inaccessible after exec. This allows the code in exec to * inaccessible after exec. Relies on having exclusive access to
* choose to fail when the executable is not mmaped into the * current->files (due to unshare_files above).
* interpreter and an open file descriptor is not passed to
* the interpreter. This makes for a better user experience
* than having the interpreter start and then immediately fail
* when it finds the executable is inaccessible.
*/ */
if (bprm->fdpath && get_close_on_exec(fd)) if (bprm->fdpath &&
close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE; bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
/* Set the unchanging part of bprm->cred */ /* Set the unchanging part of bprm->cred */
@ -1845,6 +1842,8 @@ static int bprm_execve(struct linux_binprm *bprm,
rseq_execve(current); rseq_execve(current);
acct_update_integrals(current); acct_update_integrals(current);
task_numa_free(current, false); task_numa_free(current, false);
if (displaced)
put_files_struct(displaced);
return retval; return retval;
out: out:
@ -1861,6 +1860,10 @@ static int bprm_execve(struct linux_binprm *bprm,
current->fs->in_exec = 0; current->fs->in_exec = 0;
current->in_execve = 0; current->in_execve = 0;
out_files:
if (displaced)
reset_files_struct(displaced);
return retval; return retval;
} }

View File

@ -18,7 +18,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/cred.h> #include <linux/cred.h>
#define dprintk(fmt, args...) pr_debug(fmt, ##args) #define dprintk(fmt, args...) do{}while(0)
static int get_name(const struct path *path, char *name, struct dentry *child); static int get_name(const struct path *path, char *name, struct dentry *child);
@ -132,8 +132,8 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
inode_unlock(dentry->d_inode); inode_unlock(dentry->d_inode);
if (IS_ERR(parent)) { if (IS_ERR(parent)) {
dprintk("get_parent of %lu failed, err %ld\n", dprintk("%s: get_parent of %ld failed, err %d\n",
dentry->d_inode->i_ino, PTR_ERR(parent)); __func__, dentry->d_inode->i_ino, PTR_ERR(parent));
return parent; return parent;
} }
@ -147,7 +147,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
dprintk("%s: found name: %s\n", __func__, nbuf); dprintk("%s: found name: %s\n", __func__, nbuf);
tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf)); tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf));
if (IS_ERR(tmp)) { if (IS_ERR(tmp)) {
dprintk("lookup failed: %ld\n", PTR_ERR(tmp)); dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp));
err = PTR_ERR(tmp); err = PTR_ERR(tmp);
goto out_err; goto out_err;
} }
@ -417,11 +417,9 @@ int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len,
} }
EXPORT_SYMBOL_GPL(exportfs_encode_fh); EXPORT_SYMBOL_GPL(exportfs_encode_fh);
struct dentry * struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, int fh_len, int fileid_type,
int fileid_type, int (*acceptable)(void *, struct dentry *), void *context)
int (*acceptable)(void *, struct dentry *),
void *context)
{ {
const struct export_operations *nop = mnt->mnt_sb->s_export_op; const struct export_operations *nop = mnt->mnt_sb->s_export_op;
struct dentry *result, *alias; struct dentry *result, *alias;
@ -434,8 +432,10 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
if (!nop || !nop->fh_to_dentry) if (!nop || !nop->fh_to_dentry)
return ERR_PTR(-ESTALE); return ERR_PTR(-ESTALE);
result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
if (PTR_ERR(result) == -ENOMEM)
return ERR_CAST(result);
if (IS_ERR_OR_NULL(result)) if (IS_ERR_OR_NULL(result))
return result; return ERR_PTR(-ESTALE);
/* /*
* If no acceptance criteria was specified by caller, a disconnected * If no acceptance criteria was specified by caller, a disconnected
@ -561,26 +561,10 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
err_result: err_result:
dput(result); dput(result);
if (err != -ENOMEM)
err = -ESTALE;
return ERR_PTR(err); return ERR_PTR(err);
} }
EXPORT_SYMBOL_GPL(exportfs_decode_fh_raw);
struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
int fh_len, int fileid_type,
int (*acceptable)(void *, struct dentry *),
void *context)
{
struct dentry *ret;
ret = exportfs_decode_fh_raw(mnt, fid, fh_len, fileid_type,
acceptable, context);
if (IS_ERR_OR_NULL(ret)) {
if (ret == ERR_PTR(-ENOMEM))
return ret;
return ERR_PTR(-ESTALE);
}
return ret;
}
EXPORT_SYMBOL_GPL(exportfs_decode_fh); EXPORT_SYMBOL_GPL(exportfs_decode_fh);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

177
fs/file.c
View File

@ -175,7 +175,7 @@ static int expand_fdtable(struct files_struct *files, unsigned int nr)
spin_unlock(&files->file_lock); spin_unlock(&files->file_lock);
new_fdt = alloc_fdtable(nr); new_fdt = alloc_fdtable(nr);
/* make sure all fd_install() have seen resize_in_progress /* make sure all __fd_install() have seen resize_in_progress
* or have finished their rcu_read_lock_sched() section. * or have finished their rcu_read_lock_sched() section.
*/ */
if (atomic_read(&files->count) > 1) if (atomic_read(&files->count) > 1)
@ -198,7 +198,7 @@ static int expand_fdtable(struct files_struct *files, unsigned int nr)
rcu_assign_pointer(files->fdt, new_fdt); rcu_assign_pointer(files->fdt, new_fdt);
if (cur_fdt != &files->fdtab) if (cur_fdt != &files->fdtab)
call_rcu(&cur_fdt->rcu, free_fdtable_rcu); call_rcu(&cur_fdt->rcu, free_fdtable_rcu);
/* coupled with smp_rmb() in fd_install() */ /* coupled with smp_rmb() in __fd_install() */
smp_wmb(); smp_wmb();
return 1; return 1;
} }
@ -466,6 +466,18 @@ void put_files_struct(struct files_struct *files)
} }
} }
void reset_files_struct(struct files_struct *files)
{
struct task_struct *tsk = current;
struct files_struct *old;
old = tsk->files;
task_lock(tsk);
tsk->files = files;
task_unlock(tsk);
put_files_struct(old);
}
void exit_files(struct task_struct *tsk) void exit_files(struct task_struct *tsk)
{ {
struct files_struct * files = tsk->files; struct files_struct * files = tsk->files;
@ -509,9 +521,9 @@ static unsigned int find_next_fd(struct fdtable *fdt, unsigned int start)
/* /*
* allocate a file descriptor, mark it busy. * allocate a file descriptor, mark it busy.
*/ */
static int alloc_fd(unsigned start, unsigned end, unsigned flags) int __alloc_fd(struct files_struct *files,
unsigned start, unsigned end, unsigned flags)
{ {
struct files_struct *files = current->files;
unsigned int fd; unsigned int fd;
int error; int error;
struct fdtable *fdt; struct fdtable *fdt;
@ -567,9 +579,14 @@ static int alloc_fd(unsigned start, unsigned end, unsigned flags)
return error; return error;
} }
static int alloc_fd(unsigned start, unsigned flags)
{
return __alloc_fd(current->files, start, rlimit(RLIMIT_NOFILE), flags);
}
int __get_unused_fd_flags(unsigned flags, unsigned long nofile) int __get_unused_fd_flags(unsigned flags, unsigned long nofile)
{ {
return alloc_fd(0, nofile, flags); return __alloc_fd(current->files, 0, nofile, flags);
} }
int get_unused_fd_flags(unsigned flags) int get_unused_fd_flags(unsigned flags)
@ -608,13 +625,17 @@ EXPORT_SYMBOL(put_unused_fd);
* It should never happen - if we allow dup2() do it, _really_ bad things * It should never happen - if we allow dup2() do it, _really_ bad things
* will follow. * will follow.
* *
* This consumes the "file" refcount, so callers should treat it * NOTE: __fd_install() variant is really, really low-level; don't
* as if they had called fput(file). * use it unless you are forced to by truly lousy API shoved down
* your throat. 'files' *MUST* be either current->files or obtained
* by get_files_struct(current) done by whoever had given it to you,
* or really bad things will happen. Normally you want to use
* fd_install() instead.
*/ */
void fd_install(unsigned int fd, struct file *file) void __fd_install(struct files_struct *files, unsigned int fd,
struct file *file)
{ {
struct files_struct *files = current->files;
struct fdtable *fdt; struct fdtable *fdt;
rcu_read_lock_sched(); rcu_read_lock_sched();
@ -636,6 +657,15 @@ void fd_install(unsigned int fd, struct file *file)
rcu_read_unlock_sched(); rcu_read_unlock_sched();
} }
/*
* This consumes the "file" refcount, so callers should treat it
* as if they had called fput(file).
*/
void fd_install(unsigned int fd, struct file *file)
{
__fd_install(current->files, fd, file);
}
EXPORT_SYMBOL(fd_install); EXPORT_SYMBOL(fd_install);
static struct file *pick_file(struct files_struct *files, unsigned fd) static struct file *pick_file(struct files_struct *files, unsigned fd)
@ -659,9 +689,11 @@ static struct file *pick_file(struct files_struct *files, unsigned fd)
return file; return file;
} }
int close_fd(unsigned fd) /*
* The same warnings as for __alloc_fd()/__fd_install() apply here...
*/
int __close_fd(struct files_struct *files, unsigned fd)
{ {
struct files_struct *files = current->files;
struct file *file; struct file *file;
file = pick_file(files, fd); file = pick_file(files, fd);
@ -670,7 +702,7 @@ int close_fd(unsigned fd)
return filp_close(file, files); return filp_close(file, files);
} }
EXPORT_SYMBOL(close_fd); /* for ksys_close() */ EXPORT_SYMBOL(__close_fd); /* for ksys_close() */
/** /**
* __close_range() - Close all file descriptors in a given range. * __close_range() - Close all file descriptors in a given range.
@ -829,28 +861,68 @@ void do_close_on_exec(struct files_struct *files)
spin_unlock(&files->file_lock); spin_unlock(&files->file_lock);
} }
static inline struct file *__fget_files_rcu(struct files_struct *files,
unsigned int fd, fmode_t mask, unsigned int refs)
{
for (;;) {
struct file *file;
struct fdtable *fdt = rcu_dereference_raw(files->fdt);
struct file __rcu **fdentry;
if (unlikely(fd >= fdt->max_fds))
return NULL;
fdentry = fdt->fd + array_index_nospec(fd, fdt->max_fds);
file = rcu_dereference_raw(*fdentry);
if (unlikely(!file))
return NULL;
if (unlikely(file->f_mode & mask))
return NULL;
/*
* Ok, we have a file pointer. However, because we do
* this all locklessly under RCU, we may be racing with
* that file being closed.
*
* Such a race can take two forms:
*
* (a) the file ref already went down to zero,
* and get_file_rcu_many() fails. Just try
* again:
*/
if (unlikely(!get_file_rcu_many(file, refs)))
continue;
/*
* (b) the file table entry has changed under us.
* Note that we don't need to re-check the 'fdt->fd'
* pointer having changed, because it always goes
* hand-in-hand with 'fdt'.
*
* If so, we need to put our refs and try again.
*/
if (unlikely(rcu_dereference_raw(files->fdt) != fdt) ||
unlikely(rcu_dereference_raw(*fdentry) != file)) {
fput_many(file, refs);
continue;
}
/*
* Ok, we have a ref to the file, and checked that it
* still exists.
*/
return file;
}
}
static struct file *__fget_files(struct files_struct *files, unsigned int fd, static struct file *__fget_files(struct files_struct *files, unsigned int fd,
fmode_t mask, unsigned int refs) fmode_t mask, unsigned int refs)
{ {
struct file *file; struct file *file;
rcu_read_lock(); rcu_read_lock();
loop: file = __fget_files_rcu(files, fd, mask, refs);
file = files_lookup_fd_rcu(files, fd);
if (file) {
/* File object ref couldn't be taken.
* dup2() atomicity guarantee is the reason
* we loop to catch the new file (or NULL pointer)
*/
if (file->f_mode & mask)
file = NULL;
else if (!get_file_rcu_many(file, refs))
goto loop;
else if (files_lookup_fd_raw(files, fd) != file) {
fput_many(file, refs);
goto loop;
}
}
rcu_read_unlock(); rcu_read_unlock();
return file; return file;
@ -891,42 +963,6 @@ struct file *fget_task(struct task_struct *task, unsigned int fd)
return file; return file;
} }
struct file *task_lookup_fd_rcu(struct task_struct *task, unsigned int fd)
{
/* Must be called with rcu_read_lock held */
struct files_struct *files;
struct file *file = NULL;
task_lock(task);
files = task->files;
if (files)
file = files_lookup_fd_rcu(files, fd);
task_unlock(task);
return file;
}
struct file *task_lookup_next_fd_rcu(struct task_struct *task, unsigned int *ret_fd)
{
/* Must be called with rcu_read_lock held */
struct files_struct *files;
unsigned int fd = *ret_fd;
struct file *file = NULL;
task_lock(task);
files = task->files;
if (files) {
for (; fd < files_fdtable(files)->max_fds; fd++) {
file = files_lookup_fd_rcu(files, fd);
if (file)
break;
}
}
task_unlock(task);
*ret_fd = fd;
return file;
}
/* /*
* Lightweight file lookup - no refcnt increment if fd table isn't shared. * Lightweight file lookup - no refcnt increment if fd table isn't shared.
* *
@ -949,7 +985,7 @@ static unsigned long __fget_light(unsigned int fd, fmode_t mask)
struct file *file; struct file *file;
if (atomic_read(&files->count) == 1) { if (atomic_read(&files->count) == 1) {
file = files_lookup_fd_raw(files, fd); file = __fcheck_files(files, fd);
if (!file || unlikely(file->f_mode & mask)) if (!file || unlikely(file->f_mode & mask))
return 0; return 0;
return (unsigned long)file; return (unsigned long)file;
@ -1085,7 +1121,7 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags)
struct files_struct *files = current->files; struct files_struct *files = current->files;
if (!file) if (!file)
return close_fd(fd); return __close_fd(files, fd);
if (fd >= rlimit(RLIMIT_NOFILE)) if (fd >= rlimit(RLIMIT_NOFILE))
return -EBADF; return -EBADF;
@ -1174,7 +1210,7 @@ static int ksys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
spin_lock(&files->file_lock); spin_lock(&files->file_lock);
err = expand_files(files, newfd); err = expand_files(files, newfd);
file = files_lookup_fd_locked(files, oldfd); file = fcheck(oldfd);
if (unlikely(!file)) if (unlikely(!file))
goto Ebadf; goto Ebadf;
if (unlikely(err < 0)) { if (unlikely(err < 0)) {
@ -1203,7 +1239,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
int retval = oldfd; int retval = oldfd;
rcu_read_lock(); rcu_read_lock();
if (!files_lookup_fd_rcu(files, oldfd)) if (!fcheck_files(files, oldfd))
retval = -EBADF; retval = -EBADF;
rcu_read_unlock(); rcu_read_unlock();
return retval; return retval;
@ -1228,11 +1264,10 @@ SYSCALL_DEFINE1(dup, unsigned int, fildes)
int f_dupfd(unsigned int from, struct file *file, unsigned flags) int f_dupfd(unsigned int from, struct file *file, unsigned flags)
{ {
unsigned long nofile = rlimit(RLIMIT_NOFILE);
int err; int err;
if (from >= nofile) if (from >= rlimit(RLIMIT_NOFILE))
return -EINVAL; return -EINVAL;
err = alloc_fd(from, nofile, flags); err = alloc_fd(from, flags);
if (err >= 0) { if (err >= 0) {
get_file(file); get_file(file);
fd_install(err, file); fd_install(err, file);

View File

@ -49,7 +49,7 @@ int __init init_chdir(const char *filename)
error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
if (error) if (error)
return error; return error;
error = path_permission(&path, MAY_EXEC | MAY_CHDIR); error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (!error) if (!error)
set_fs_pwd(current->fs, &path); set_fs_pwd(current->fs, &path);
path_put(&path); path_put(&path);
@ -64,7 +64,7 @@ int __init init_chroot(const char *filename)
error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
if (error) if (error)
return error; return error;
error = path_permission(&path, MAY_EXEC | MAY_CHDIR); error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error) if (error)
goto dput_and_out; goto dput_and_out;
error = -EPERM; error = -EPERM;
@ -118,7 +118,7 @@ int __init init_eaccess(const char *filename)
error = kern_path(filename, LOOKUP_FOLLOW, &path); error = kern_path(filename, LOOKUP_FOLLOW, &path);
if (error) if (error)
return error; return error;
error = path_permission(&path, MAY_ACCESS); error = inode_permission(d_inode(path.dentry), MAY_ACCESS);
path_put(&path); path_put(&path);
return error; return error;
} }

View File

@ -261,6 +261,7 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
u32 exclusive; u32 exclusive;
int error; int error;
__be32 *p; __be32 *p;
s32 end;
memset(lock, 0, sizeof(*lock)); memset(lock, 0, sizeof(*lock));
locks_init_lock(fl); locks_init_lock(fl);
@ -284,7 +285,13 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK; fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
p = xdr_decode_hyper(p, &l_offset); p = xdr_decode_hyper(p, &l_offset);
xdr_decode_hyper(p, &l_len); xdr_decode_hyper(p, &l_len);
nlm4svc_set_file_lock_range(fl, l_offset, l_len); end = l_offset + l_len - 1;
fl->fl_start = (loff_t)l_offset;
if (l_len == 0 || end < 0)
fl->fl_end = OFFSET_MAX;
else
fl->fl_end = (loff_t)end;
error = 0; error = 0;
out: out:
return error; return error;

View File

@ -794,6 +794,9 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
goto retry_cancel; goto retry_cancel;
} }
dprintk("lockd: cancel status %u (task %u)\n",
status, task->tk_pid);
switch (status) { switch (status) {
case NLM_LCK_GRANTED: case NLM_LCK_GRANTED:
case NLM_LCK_DENIED_GRACE_PERIOD: case NLM_LCK_DENIED_GRACE_PERIOD:

View File

@ -163,8 +163,8 @@ static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
host->h_nsmhandle = nsm; host->h_nsmhandle = nsm;
host->h_addrbuf = nsm->sm_addrbuf; host->h_addrbuf = nsm->sm_addrbuf;
host->net = ni->net; host->net = ni->net;
host->h_cred = get_cred(ni->cred); host->h_cred = get_cred(ni->cred),
strscpy(host->nodename, utsname()->nodename, sizeof(host->nodename)); strlcpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
out: out:
return host; return host;

View File

@ -54,9 +54,13 @@ EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex); static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users; static unsigned int nlmsvc_users;
static struct svc_serv *nlmsvc_serv; static struct task_struct *nlmsvc_task;
static struct svc_rqst *nlmsvc_rqst;
unsigned long nlmsvc_timeout; unsigned long nlmsvc_timeout;
static atomic_t nlm_ntf_refcnt = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(nlm_ntf_wq);
unsigned int lockd_net_id; unsigned int lockd_net_id;
/* /*
@ -180,10 +184,6 @@ lockd(void *vrqstp)
nlm_shutdown_hosts(); nlm_shutdown_hosts();
cancel_delayed_work_sync(&ln->grace_period_end); cancel_delayed_work_sync(&ln->grace_period_end);
locks_end_grace(&ln->lockd_manager); locks_end_grace(&ln->lockd_manager);
dprintk("lockd_down: service stopped\n");
svc_exit_thread(rqstp);
return 0; return 0;
} }
@ -196,8 +196,8 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name,
xprt = svc_find_xprt(serv, name, net, family, 0); xprt = svc_find_xprt(serv, name, net, family, 0);
if (xprt == NULL) if (xprt == NULL)
return svc_xprt_create(serv, name, net, family, port, return svc_create_xprt(serv, name, net, family, port,
SVC_SOCK_DEFAULTS, cred); SVC_SOCK_DEFAULTS, cred);
svc_xprt_put(xprt); svc_xprt_put(xprt);
return 0; return 0;
} }
@ -247,8 +247,7 @@ static int make_socks(struct svc_serv *serv, struct net *net,
if (warned++ == 0) if (warned++ == 0)
printk(KERN_WARNING printk(KERN_WARNING
"lockd_up: makesock failed, error=%d\n", err); "lockd_up: makesock failed, error=%d\n", err);
svc_xprt_destroy_all(serv, net); svc_shutdown_net(serv, net);
svc_rpcb_cleanup(serv, net);
return err; return err;
} }
@ -286,12 +285,13 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net)
nlm_shutdown_hosts_net(net); nlm_shutdown_hosts_net(net);
cancel_delayed_work_sync(&ln->grace_period_end); cancel_delayed_work_sync(&ln->grace_period_end);
locks_end_grace(&ln->lockd_manager); locks_end_grace(&ln->lockd_manager);
svc_xprt_destroy_all(serv, net); svc_shutdown_net(serv, net);
svc_rpcb_cleanup(serv, net); dprintk("%s: per-net data destroyed; net=%x\n",
__func__, net->ns.inum);
} }
} else { } else {
pr_err("%s: no users! net=%x\n", pr_err("%s: no users! task=%p, net=%x\n",
__func__, net->ns.inum); __func__, nlmsvc_task, net->ns.inum);
BUG(); BUG();
} }
} }
@ -302,16 +302,20 @@ static int lockd_inetaddr_event(struct notifier_block *this,
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
struct sockaddr_in sin; struct sockaddr_in sin;
if (event != NETDEV_DOWN) if ((event != NETDEV_DOWN) ||
!atomic_inc_not_zero(&nlm_ntf_refcnt))
goto out; goto out;
if (nlmsvc_serv) { if (nlmsvc_rqst) {
dprintk("lockd_inetaddr_event: removed %pI4\n", dprintk("lockd_inetaddr_event: removed %pI4\n",
&ifa->ifa_local); &ifa->ifa_local);
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ifa->ifa_local; sin.sin_addr.s_addr = ifa->ifa_local;
svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin); svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
(struct sockaddr *)&sin);
} }
atomic_dec(&nlm_ntf_refcnt);
wake_up(&nlm_ntf_wq);
out: out:
return NOTIFY_DONE; return NOTIFY_DONE;
@ -328,17 +332,21 @@ static int lockd_inet6addr_event(struct notifier_block *this,
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
if (event != NETDEV_DOWN) if ((event != NETDEV_DOWN) ||
!atomic_inc_not_zero(&nlm_ntf_refcnt))
goto out; goto out;
if (nlmsvc_serv) { if (nlmsvc_rqst) {
dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr); dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr);
sin6.sin6_family = AF_INET6; sin6.sin6_family = AF_INET6;
sin6.sin6_addr = ifa->addr; sin6.sin6_addr = ifa->addr;
if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL) if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
sin6.sin6_scope_id = ifa->idev->dev->ifindex; sin6.sin6_scope_id = ifa->idev->dev->ifindex;
svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin6); svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
(struct sockaddr *)&sin6);
} }
atomic_dec(&nlm_ntf_refcnt);
wake_up(&nlm_ntf_wq);
out: out:
return NOTIFY_DONE; return NOTIFY_DONE;
@ -349,14 +357,86 @@ static struct notifier_block lockd_inet6addr_notifier = {
}; };
#endif #endif
static int lockd_get(void) static void lockd_unregister_notifiers(void)
{
unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
#endif
wait_event(nlm_ntf_wq, atomic_read(&nlm_ntf_refcnt) == 0);
}
static void lockd_svc_exit_thread(void)
{
atomic_dec(&nlm_ntf_refcnt);
lockd_unregister_notifiers();
svc_exit_thread(nlmsvc_rqst);
}
static int lockd_start_svc(struct svc_serv *serv)
{ {
struct svc_serv *serv;
int error; int error;
if (nlmsvc_serv) { if (nlmsvc_rqst)
nlmsvc_users++;
return 0; return 0;
/*
* Create the kernel thread and wait for it to start.
*/
nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
if (IS_ERR(nlmsvc_rqst)) {
error = PTR_ERR(nlmsvc_rqst);
printk(KERN_WARNING
"lockd_up: svc_rqst allocation failed, error=%d\n",
error);
lockd_unregister_notifiers();
goto out_rqst;
}
atomic_inc(&nlm_ntf_refcnt);
svc_sock_update_bufs(serv);
serv->sv_maxconn = nlm_max_connections;
nlmsvc_task = kthread_create(lockd, nlmsvc_rqst, "%s", serv->sv_name);
if (IS_ERR(nlmsvc_task)) {
error = PTR_ERR(nlmsvc_task);
printk(KERN_WARNING
"lockd_up: kthread_run failed, error=%d\n", error);
goto out_task;
}
nlmsvc_rqst->rq_task = nlmsvc_task;
wake_up_process(nlmsvc_task);
dprintk("lockd_up: service started\n");
return 0;
out_task:
lockd_svc_exit_thread();
nlmsvc_task = NULL;
out_rqst:
nlmsvc_rqst = NULL;
return error;
}
static const struct svc_serv_ops lockd_sv_ops = {
.svo_shutdown = svc_rpcb_cleanup,
.svo_enqueue_xprt = svc_xprt_do_enqueue,
};
static struct svc_serv *lockd_create_svc(void)
{
struct svc_serv *serv;
/*
* Check whether we're already up and running.
*/
if (nlmsvc_rqst) {
/*
* Note: increase service usage, because later in case of error
* svc_destroy() will be called.
*/
svc_get(nlmsvc_rqst->rq_server);
return nlmsvc_rqst->rq_server;
} }
/* /*
@ -371,44 +451,17 @@ static int lockd_get(void)
nlm_timeout = LOCKD_DFLT_TIMEO; nlm_timeout = LOCKD_DFLT_TIMEO;
nlmsvc_timeout = nlm_timeout * HZ; nlmsvc_timeout = nlm_timeout * HZ;
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, lockd); serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, &lockd_sv_ops);
if (!serv) { if (!serv) {
printk(KERN_WARNING "lockd_up: create service failed\n"); printk(KERN_WARNING "lockd_up: create service failed\n");
return -ENOMEM; return ERR_PTR(-ENOMEM);
} }
serv->sv_maxconn = nlm_max_connections;
error = svc_set_num_threads(serv, NULL, 1);
/* The thread now holds the only reference */
svc_put(serv);
if (error < 0)
return error;
nlmsvc_serv = serv;
register_inetaddr_notifier(&lockd_inetaddr_notifier); register_inetaddr_notifier(&lockd_inetaddr_notifier);
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
register_inet6addr_notifier(&lockd_inet6addr_notifier); register_inet6addr_notifier(&lockd_inet6addr_notifier);
#endif #endif
dprintk("lockd_up: service created\n"); dprintk("lockd_up: service created\n");
nlmsvc_users++; return serv;
return 0;
}
static void lockd_put(void)
{
if (WARN(nlmsvc_users <= 0, "lockd_down: no users!\n"))
return;
if (--nlmsvc_users)
return;
unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
#endif
svc_set_num_threads(nlmsvc_serv, NULL, 0);
nlmsvc_serv = NULL;
dprintk("lockd_down: service destroyed\n");
} }
/* /*
@ -416,21 +469,36 @@ static void lockd_put(void)
*/ */
int lockd_up(struct net *net, const struct cred *cred) int lockd_up(struct net *net, const struct cred *cred)
{ {
struct svc_serv *serv;
int error; int error;
mutex_lock(&nlmsvc_mutex); mutex_lock(&nlmsvc_mutex);
error = lockd_get(); serv = lockd_create_svc();
if (error) if (IS_ERR(serv)) {
goto err; error = PTR_ERR(serv);
goto err_create;
error = lockd_up_net(nlmsvc_serv, net, cred);
if (error < 0) {
lockd_put();
goto err;
} }
err: error = lockd_up_net(serv, net, cred);
if (error < 0) {
lockd_unregister_notifiers();
goto err_put;
}
error = lockd_start_svc(serv);
if (error < 0) {
lockd_down_net(serv, net);
goto err_put;
}
nlmsvc_users++;
/*
* Note: svc_serv structures have an initial use count of 1,
* so we exit through here on both success and failure.
*/
err_put:
svc_destroy(serv);
err_create:
mutex_unlock(&nlmsvc_mutex); mutex_unlock(&nlmsvc_mutex);
return error; return error;
} }
@ -443,8 +511,27 @@ void
lockd_down(struct net *net) lockd_down(struct net *net)
{ {
mutex_lock(&nlmsvc_mutex); mutex_lock(&nlmsvc_mutex);
lockd_down_net(nlmsvc_serv, net); lockd_down_net(nlmsvc_rqst->rq_server, net);
lockd_put(); if (nlmsvc_users) {
if (--nlmsvc_users)
goto out;
} else {
printk(KERN_ERR "lockd_down: no users! task=%p\n",
nlmsvc_task);
BUG();
}
if (!nlmsvc_task) {
printk(KERN_ERR "lockd_down: no lockd running.\n");
BUG();
}
kthread_stop(nlmsvc_task);
dprintk("lockd_down: service stopped\n");
lockd_svc_exit_thread();
dprintk("lockd_down: service destroyed\n");
nlmsvc_task = NULL;
nlmsvc_rqst = NULL;
out:
mutex_unlock(&nlmsvc_mutex); mutex_unlock(&nlmsvc_mutex);
} }
EXPORT_SYMBOL_GPL(lockd_down); EXPORT_SYMBOL_GPL(lockd_down);
@ -497,7 +584,7 @@ static struct ctl_table nlm_sysctls[] = {
.data = &nsm_use_hostnames, .data = &nsm_use_hostnames,
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dobool, .proc_handler = proc_dointvec,
}, },
{ {
.procname = "nsm_local_state", .procname = "nsm_local_state",
@ -562,7 +649,6 @@ static int lockd_authenticate(struct svc_rqst *rqstp)
switch (rqstp->rq_authop->flavour) { switch (rqstp->rq_authop->flavour) {
case RPC_AUTH_NULL: case RPC_AUTH_NULL:
case RPC_AUTH_UNIX: case RPC_AUTH_UNIX:
rqstp->rq_auth_stat = rpc_auth_ok;
if (rqstp->rq_proc == 0) if (rqstp->rq_proc == 0)
return SVC_OK; return SVC_OK;
if (is_callback(rqstp->rq_proc)) { if (is_callback(rqstp->rq_proc)) {
@ -573,7 +659,6 @@ static int lockd_authenticate(struct svc_rqst *rqstp)
} }
return svc_set_client(rqstp); return svc_set_client(rqstp);
} }
rqstp->rq_auth_stat = rpc_autherr_badcred;
return SVC_DENIED; return SVC_DENIED;
} }
@ -681,44 +766,6 @@ static void __exit exit_nlm(void)
module_init(init_nlm); module_init(init_nlm);
module_exit(exit_nlm); module_exit(exit_nlm);
/**
* nlmsvc_dispatch - Process an NLM Request
* @rqstp: incoming request
* @statp: pointer to location of accept_stat field in RPC Reply buffer
*
* Return values:
* %0: Processing complete; do not send a Reply
* %1: Processing complete; send Reply in rqstp->rq_res
*/
static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp)
{
const struct svc_procedure *procp = rqstp->rq_procinfo;
svcxdr_init_decode(rqstp);
if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream))
goto out_decode_err;
*statp = procp->pc_func(rqstp);
if (*statp == rpc_drop_reply)
return 0;
if (*statp != rpc_success)
return 1;
svcxdr_init_encode(rqstp);
if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream))
goto out_encode_err;
return 1;
out_decode_err:
*statp = rpc_garbage_args;
return 1;
out_encode_err:
*statp = rpc_system_err;
return 1;
}
/* /*
* Define NLM program and procedures * Define NLM program and procedures
*/ */
@ -728,7 +775,6 @@ static const struct svc_version nlmsvc_version1 = {
.vs_nproc = 17, .vs_nproc = 17,
.vs_proc = nlmsvc_procedures, .vs_proc = nlmsvc_procedures,
.vs_count = nlmsvc_version1_count, .vs_count = nlmsvc_version1_count,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = NLMSVC_XDRSIZE, .vs_xdrsize = NLMSVC_XDRSIZE,
}; };
static unsigned int nlmsvc_version3_count[24]; static unsigned int nlmsvc_version3_count[24];
@ -737,7 +783,6 @@ static const struct svc_version nlmsvc_version3 = {
.vs_nproc = 24, .vs_nproc = 24,
.vs_proc = nlmsvc_procedures, .vs_proc = nlmsvc_procedures,
.vs_count = nlmsvc_version3_count, .vs_count = nlmsvc_version3_count,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = NLMSVC_XDRSIZE, .vs_xdrsize = NLMSVC_XDRSIZE,
}; };
#ifdef CONFIG_LOCKD_V4 #ifdef CONFIG_LOCKD_V4
@ -747,7 +792,6 @@ static const struct svc_version nlmsvc_version4 = {
.vs_nproc = 24, .vs_nproc = 24,
.vs_proc = nlmsvc_procedures4, .vs_proc = nlmsvc_procedures4,
.vs_count = nlmsvc_version4_count, .vs_count = nlmsvc_version4_count,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = NLMSVC_XDRSIZE, .vs_xdrsize = NLMSVC_XDRSIZE,
}; };
#endif #endif

View File

@ -32,10 +32,6 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
if (!nlmsvc_ops) if (!nlmsvc_ops)
return nlm_lck_denied_nolocks; return nlm_lck_denied_nolocks;
if (lock->lock_start > OFFSET_MAX ||
(lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start))))
return nlm4_fbig;
/* Obtain host handle */ /* Obtain host handle */
if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len))
|| (argp->monitor && nsm_monitor(host) < 0)) || (argp->monitor && nsm_monitor(host) < 0))
@ -44,21 +40,13 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Obtain file pointer. Not used by FREE_ALL call. */ /* Obtain file pointer. Not used by FREE_ALL call. */
if (filp != NULL) { if (filp != NULL) {
int mode = lock_to_openmode(&lock->fl); if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
error = nlm_lookup_file(rqstp, &file, lock);
if (error)
goto no_locks; goto no_locks;
*filp = file; *filp = file;
/* Set up the missing parts of the file_lock structure */ /* Set up the missing parts of the file_lock structure */
lock->fl.fl_flags = FL_POSIX; lock->fl.fl_file = file->f_file;
lock->fl.fl_file = file->f_file[mode];
lock->fl.fl_pid = current->tgid; lock->fl.fl_pid = current->tgid;
lock->fl.fl_start = (loff_t)lock->lock_start;
lock->fl.fl_end = lock->lock_len ?
(loff_t)(lock->lock_start + lock->lock_len - 1) :
OFFSET_MAX;
lock->fl.fl_lmops = &nlmsvc_lock_operations; lock->fl.fl_lmops = &nlmsvc_lock_operations;
nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
if (!lock->fl.fl_owner) { if (!lock->fl.fl_owner) {
@ -96,7 +84,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
struct nlm_host *host; struct nlm_host *host;
struct nlm_file *file; struct nlm_file *file;
struct nlm_lockowner *test_owner;
__be32 rc = rpc_success; __be32 rc = rpc_success;
dprintk("lockd: TEST4 called\n"); dprintk("lockd: TEST4 called\n");
@ -106,7 +93,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
test_owner = argp->lock.fl.fl_owner;
/* Now check for conflicting locks */ /* Now check for conflicting locks */
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie); resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie);
if (resp->status == nlm_drop_reply) if (resp->status == nlm_drop_reply)
@ -114,7 +100,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
else else
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
nlmsvc_put_lockowner(test_owner); nlmsvc_release_lockowner(&argp->lock);
nlmsvc_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rc; return rc;
@ -280,6 +266,8 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp)
*/ */
static void nlm4svc_callback_exit(struct rpc_task *task, void *data) static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
{ {
dprintk("lockd: %5u callback returned %d\n", task->tk_pid,
-task->tk_status);
} }
static void nlm4svc_callback_release(void *data) static void nlm4svc_callback_release(void *data)
@ -522,239 +510,191 @@ const struct svc_procedure nlmsvc_procedures4[24] = {
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "NULL",
}, },
[NLMPROC_TEST] = { [NLMPROC_TEST] = {
.pc_func = nlm4svc_proc_test, .pc_func = nlm4svc_proc_test,
.pc_decode = nlm4svc_decode_testargs, .pc_decode = nlm4svc_decode_testargs,
.pc_encode = nlm4svc_encode_testres, .pc_encode = nlm4svc_encode_testres,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St+2+No+Rg, .pc_xdrressize = Ck+St+2+No+Rg,
.pc_name = "TEST",
}, },
[NLMPROC_LOCK] = { [NLMPROC_LOCK] = {
.pc_func = nlm4svc_proc_lock, .pc_func = nlm4svc_proc_lock,
.pc_decode = nlm4svc_decode_lockargs, .pc_decode = nlm4svc_decode_lockargs,
.pc_encode = nlm4svc_encode_res, .pc_encode = nlm4svc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "LOCK",
}, },
[NLMPROC_CANCEL] = { [NLMPROC_CANCEL] = {
.pc_func = nlm4svc_proc_cancel, .pc_func = nlm4svc_proc_cancel,
.pc_decode = nlm4svc_decode_cancargs, .pc_decode = nlm4svc_decode_cancargs,
.pc_encode = nlm4svc_encode_res, .pc_encode = nlm4svc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "CANCEL",
}, },
[NLMPROC_UNLOCK] = { [NLMPROC_UNLOCK] = {
.pc_func = nlm4svc_proc_unlock, .pc_func = nlm4svc_proc_unlock,
.pc_decode = nlm4svc_decode_unlockargs, .pc_decode = nlm4svc_decode_unlockargs,
.pc_encode = nlm4svc_encode_res, .pc_encode = nlm4svc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "UNLOCK",
}, },
[NLMPROC_GRANTED] = { [NLMPROC_GRANTED] = {
.pc_func = nlm4svc_proc_granted, .pc_func = nlm4svc_proc_granted,
.pc_decode = nlm4svc_decode_testargs, .pc_decode = nlm4svc_decode_testargs,
.pc_encode = nlm4svc_encode_res, .pc_encode = nlm4svc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "GRANTED",
}, },
[NLMPROC_TEST_MSG] = { [NLMPROC_TEST_MSG] = {
.pc_func = nlm4svc_proc_test_msg, .pc_func = nlm4svc_proc_test_msg,
.pc_decode = nlm4svc_decode_testargs, .pc_decode = nlm4svc_decode_testargs,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "TEST_MSG",
}, },
[NLMPROC_LOCK_MSG] = { [NLMPROC_LOCK_MSG] = {
.pc_func = nlm4svc_proc_lock_msg, .pc_func = nlm4svc_proc_lock_msg,
.pc_decode = nlm4svc_decode_lockargs, .pc_decode = nlm4svc_decode_lockargs,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "LOCK_MSG",
}, },
[NLMPROC_CANCEL_MSG] = { [NLMPROC_CANCEL_MSG] = {
.pc_func = nlm4svc_proc_cancel_msg, .pc_func = nlm4svc_proc_cancel_msg,
.pc_decode = nlm4svc_decode_cancargs, .pc_decode = nlm4svc_decode_cancargs,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "CANCEL_MSG",
}, },
[NLMPROC_UNLOCK_MSG] = { [NLMPROC_UNLOCK_MSG] = {
.pc_func = nlm4svc_proc_unlock_msg, .pc_func = nlm4svc_proc_unlock_msg,
.pc_decode = nlm4svc_decode_unlockargs, .pc_decode = nlm4svc_decode_unlockargs,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "UNLOCK_MSG",
}, },
[NLMPROC_GRANTED_MSG] = { [NLMPROC_GRANTED_MSG] = {
.pc_func = nlm4svc_proc_granted_msg, .pc_func = nlm4svc_proc_granted_msg,
.pc_decode = nlm4svc_decode_testargs, .pc_decode = nlm4svc_decode_testargs,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "GRANTED_MSG",
}, },
[NLMPROC_TEST_RES] = { [NLMPROC_TEST_RES] = {
.pc_func = nlm4svc_proc_null, .pc_func = nlm4svc_proc_null,
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "TEST_RES",
}, },
[NLMPROC_LOCK_RES] = { [NLMPROC_LOCK_RES] = {
.pc_func = nlm4svc_proc_null, .pc_func = nlm4svc_proc_null,
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "LOCK_RES",
}, },
[NLMPROC_CANCEL_RES] = { [NLMPROC_CANCEL_RES] = {
.pc_func = nlm4svc_proc_null, .pc_func = nlm4svc_proc_null,
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "CANCEL_RES",
}, },
[NLMPROC_UNLOCK_RES] = { [NLMPROC_UNLOCK_RES] = {
.pc_func = nlm4svc_proc_null, .pc_func = nlm4svc_proc_null,
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "UNLOCK_RES",
}, },
[NLMPROC_GRANTED_RES] = { [NLMPROC_GRANTED_RES] = {
.pc_func = nlm4svc_proc_granted_res, .pc_func = nlm4svc_proc_granted_res,
.pc_decode = nlm4svc_decode_res, .pc_decode = nlm4svc_decode_res,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "GRANTED_RES",
}, },
[NLMPROC_NSM_NOTIFY] = { [NLMPROC_NSM_NOTIFY] = {
.pc_func = nlm4svc_proc_sm_notify, .pc_func = nlm4svc_proc_sm_notify,
.pc_decode = nlm4svc_decode_reboot, .pc_decode = nlm4svc_decode_reboot,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_reboot), .pc_argsize = sizeof(struct nlm_reboot),
.pc_argzero = sizeof(struct nlm_reboot),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "SM_NOTIFY",
}, },
[17] = { [17] = {
.pc_func = nlm4svc_proc_unused, .pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = 0, .pc_xdrressize = 0,
.pc_name = "UNUSED",
}, },
[18] = { [18] = {
.pc_func = nlm4svc_proc_unused, .pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = 0, .pc_xdrressize = 0,
.pc_name = "UNUSED",
}, },
[19] = { [19] = {
.pc_func = nlm4svc_proc_unused, .pc_func = nlm4svc_proc_unused,
.pc_decode = nlm4svc_decode_void, .pc_decode = nlm4svc_decode_void,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = 0, .pc_xdrressize = 0,
.pc_name = "UNUSED",
}, },
[NLMPROC_SHARE] = { [NLMPROC_SHARE] = {
.pc_func = nlm4svc_proc_share, .pc_func = nlm4svc_proc_share,
.pc_decode = nlm4svc_decode_shareargs, .pc_decode = nlm4svc_decode_shareargs,
.pc_encode = nlm4svc_encode_shareres, .pc_encode = nlm4svc_encode_shareres,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St+1, .pc_xdrressize = Ck+St+1,
.pc_name = "SHARE",
}, },
[NLMPROC_UNSHARE] = { [NLMPROC_UNSHARE] = {
.pc_func = nlm4svc_proc_unshare, .pc_func = nlm4svc_proc_unshare,
.pc_decode = nlm4svc_decode_shareargs, .pc_decode = nlm4svc_decode_shareargs,
.pc_encode = nlm4svc_encode_shareres, .pc_encode = nlm4svc_encode_shareres,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St+1, .pc_xdrressize = Ck+St+1,
.pc_name = "UNSHARE",
}, },
[NLMPROC_NM_LOCK] = { [NLMPROC_NM_LOCK] = {
.pc_func = nlm4svc_proc_nm_lock, .pc_func = nlm4svc_proc_nm_lock,
.pc_decode = nlm4svc_decode_lockargs, .pc_decode = nlm4svc_decode_lockargs,
.pc_encode = nlm4svc_encode_res, .pc_encode = nlm4svc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "NM_LOCK",
}, },
[NLMPROC_FREE_ALL] = { [NLMPROC_FREE_ALL] = {
.pc_func = nlm4svc_proc_free_all, .pc_func = nlm4svc_proc_free_all,
.pc_decode = nlm4svc_decode_notify, .pc_decode = nlm4svc_decode_notify,
.pc_encode = nlm4svc_encode_void, .pc_encode = nlm4svc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "FREE_ALL",
}, },
}; };

View File

@ -31,7 +31,6 @@
#include <linux/lockd/nlm.h> #include <linux/lockd/nlm.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/exportfs.h>
#define NLMDBG_FACILITY NLMDBG_SVCLOCK #define NLMDBG_FACILITY NLMDBG_SVCLOCK
@ -340,7 +339,7 @@ nlmsvc_get_lockowner(struct nlm_lockowner *lockowner)
return lockowner; return lockowner;
} }
void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner) static void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner)
{ {
if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock)) if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
return; return;
@ -470,27 +469,18 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_host *host, struct nlm_lock *lock, int wait, struct nlm_host *host, struct nlm_lock *lock, int wait,
struct nlm_cookie *cookie, int reclaim) struct nlm_cookie *cookie, int reclaim)
{ {
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
struct inode *inode = nlmsvc_file_inode(file);
#endif
struct nlm_block *block = NULL; struct nlm_block *block = NULL;
int error; int error;
int mode;
int async_block = 0;
__be32 ret; __be32 ret;
dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n", dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
inode->i_sb->s_id, inode->i_ino, locks_inode(file->f_file)->i_sb->s_id,
locks_inode(file->f_file)->i_ino,
lock->fl.fl_type, lock->fl.fl_pid, lock->fl.fl_type, lock->fl.fl_pid,
(long long)lock->fl.fl_start, (long long)lock->fl.fl_start,
(long long)lock->fl.fl_end, (long long)lock->fl.fl_end,
wait); wait);
if (nlmsvc_file_file(file)->f_op->lock) {
async_block = wait;
wait = 0;
}
/* Lock file against concurrent access */ /* Lock file against concurrent access */
mutex_lock(&file->f_mutex); mutex_lock(&file->f_mutex);
/* Get existing block (in case client is busy-waiting) /* Get existing block (in case client is busy-waiting)
@ -534,8 +524,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
if (!wait) if (!wait)
lock->fl.fl_flags &= ~FL_SLEEP; lock->fl.fl_flags &= ~FL_SLEEP;
mode = lock_to_openmode(&lock->fl); error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
lock->fl.fl_flags &= ~FL_SLEEP; lock->fl.fl_flags &= ~FL_SLEEP;
dprintk("lockd: vfs_lock_file returned %d\n", error); dprintk("lockd: vfs_lock_file returned %d\n", error);
@ -551,7 +540,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
*/ */
if (wait) if (wait)
break; break;
ret = async_block ? nlm_lck_blocked : nlm_lck_denied; ret = nlm_lck_denied;
goto out; goto out;
case FILE_LOCK_DEFERRED: case FILE_LOCK_DEFERRED:
if (wait) if (wait)
@ -588,12 +577,12 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_lock *conflock, struct nlm_cookie *cookie) struct nlm_lock *conflock, struct nlm_cookie *cookie)
{ {
int error; int error;
int mode;
__be32 ret; __be32 ret;
struct nlm_lockowner *test_owner;
dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
nlmsvc_file_inode(file)->i_sb->s_id, locks_inode(file->f_file)->i_sb->s_id,
nlmsvc_file_inode(file)->i_ino, locks_inode(file->f_file)->i_ino,
lock->fl.fl_type, lock->fl.fl_type,
(long long)lock->fl.fl_start, (long long)lock->fl.fl_start,
(long long)lock->fl.fl_end); (long long)lock->fl.fl_end);
@ -603,8 +592,10 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out; goto out;
} }
mode = lock_to_openmode(&lock->fl); /* If there's a conflicting lock, remember to clean up the test lock */
error = vfs_test_lock(file->f_file[mode], &lock->fl); test_owner = (struct nlm_lockowner *)lock->fl.fl_owner;
error = vfs_test_lock(file->f_file, &lock->fl);
if (error) { if (error) {
/* We can't currently deal with deferred test requests */ /* We can't currently deal with deferred test requests */
if (error == FILE_LOCK_DEFERRED) if (error == FILE_LOCK_DEFERRED)
@ -631,6 +622,10 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
conflock->fl.fl_end = lock->fl.fl_end; conflock->fl.fl_end = lock->fl.fl_end;
locks_release_private(&lock->fl); locks_release_private(&lock->fl);
/* Clean up the test lock */
lock->fl.fl_owner = NULL;
nlmsvc_put_lockowner(test_owner);
ret = nlm_lck_denied; ret = nlm_lck_denied;
out: out:
return ret; return ret;
@ -646,11 +641,11 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
__be32 __be32
nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock) nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
{ {
int error = 0; int error;
dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n", dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
nlmsvc_file_inode(file)->i_sb->s_id, locks_inode(file->f_file)->i_sb->s_id,
nlmsvc_file_inode(file)->i_ino, locks_inode(file->f_file)->i_ino,
lock->fl.fl_pid, lock->fl.fl_pid,
(long long)lock->fl.fl_start, (long long)lock->fl.fl_start,
(long long)lock->fl.fl_end); (long long)lock->fl.fl_end);
@ -659,14 +654,7 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
nlmsvc_cancel_blocked(net, file, lock); nlmsvc_cancel_blocked(net, file, lock);
lock->fl.fl_type = F_UNLCK; lock->fl.fl_type = F_UNLCK;
lock->fl.fl_file = file->f_file[O_RDONLY]; error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
if (lock->fl.fl_file)
error = vfs_lock_file(lock->fl.fl_file, F_SETLK,
&lock->fl, NULL);
lock->fl.fl_file = file->f_file[O_WRONLY];
if (lock->fl.fl_file)
error |= vfs_lock_file(lock->fl.fl_file, F_SETLK,
&lock->fl, NULL);
return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
} }
@ -683,11 +671,10 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
{ {
struct nlm_block *block; struct nlm_block *block;
int status = 0; int status = 0;
int mode;
dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n", dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
nlmsvc_file_inode(file)->i_sb->s_id, locks_inode(file->f_file)->i_sb->s_id,
nlmsvc_file_inode(file)->i_ino, locks_inode(file->f_file)->i_ino,
lock->fl.fl_pid, lock->fl.fl_pid,
(long long)lock->fl.fl_start, (long long)lock->fl.fl_start,
(long long)lock->fl.fl_end); (long long)lock->fl.fl_end);
@ -699,10 +686,8 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
block = nlmsvc_lookup_block(file, lock); block = nlmsvc_lookup_block(file, lock);
mutex_unlock(&file->f_mutex); mutex_unlock(&file->f_mutex);
if (block != NULL) { if (block != NULL) {
struct file_lock *fl = &block->b_call->a_args.lock.fl; vfs_cancel_lock(block->b_file->f_file,
&block->b_call->a_args.lock.fl);
mode = lock_to_openmode(fl);
vfs_cancel_lock(block->b_file->f_file[mode], fl);
status = nlmsvc_unlink_block(block); status = nlmsvc_unlink_block(block);
nlmsvc_release_block(block); nlmsvc_release_block(block);
} }
@ -818,7 +803,6 @@ nlmsvc_grant_blocked(struct nlm_block *block)
{ {
struct nlm_file *file = block->b_file; struct nlm_file *file = block->b_file;
struct nlm_lock *lock = &block->b_call->a_args.lock; struct nlm_lock *lock = &block->b_call->a_args.lock;
int mode;
int error; int error;
loff_t fl_start, fl_end; loff_t fl_start, fl_end;
@ -844,8 +828,7 @@ nlmsvc_grant_blocked(struct nlm_block *block)
lock->fl.fl_flags |= FL_SLEEP; lock->fl.fl_flags |= FL_SLEEP;
fl_start = lock->fl.fl_start; fl_start = lock->fl.fl_start;
fl_end = lock->fl.fl_end; fl_end = lock->fl.fl_end;
mode = lock_to_openmode(&lock->fl); error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
lock->fl.fl_flags &= ~FL_SLEEP; lock->fl.fl_flags &= ~FL_SLEEP;
lock->fl.fl_start = fl_start; lock->fl.fl_start = fl_start;
lock->fl.fl_end = fl_end; lock->fl.fl_end = fl_end;

View File

@ -55,7 +55,6 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
struct nlm_host *host = NULL; struct nlm_host *host = NULL;
struct nlm_file *file = NULL; struct nlm_file *file = NULL;
struct nlm_lock *lock = &argp->lock; struct nlm_lock *lock = &argp->lock;
int mode;
__be32 error = 0; __be32 error = 0;
/* nfsd callbacks must have been installed for this procedure */ /* nfsd callbacks must have been installed for this procedure */
@ -70,15 +69,13 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Obtain file pointer. Not used by FREE_ALL call. */ /* Obtain file pointer. Not used by FREE_ALL call. */
if (filp != NULL) { if (filp != NULL) {
error = cast_status(nlm_lookup_file(rqstp, &file, lock)); error = cast_status(nlm_lookup_file(rqstp, &file, &lock->fh));
if (error != 0) if (error != 0)
goto no_locks; goto no_locks;
*filp = file; *filp = file;
/* Set up the missing parts of the file_lock structure */ /* Set up the missing parts of the file_lock structure */
mode = lock_to_openmode(&lock->fl); lock->fl.fl_file = file->f_file;
lock->fl.fl_flags = FL_POSIX;
lock->fl.fl_file = file->f_file[mode];
lock->fl.fl_pid = current->tgid; lock->fl.fl_pid = current->tgid;
lock->fl.fl_lmops = &nlmsvc_lock_operations; lock->fl.fl_lmops = &nlmsvc_lock_operations;
nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
@ -117,7 +114,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
struct nlm_host *host; struct nlm_host *host;
struct nlm_file *file; struct nlm_file *file;
struct nlm_lockowner *test_owner;
__be32 rc = rpc_success; __be32 rc = rpc_success;
dprintk("lockd: TEST called\n"); dprintk("lockd: TEST called\n");
@ -127,8 +123,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
test_owner = argp->lock.fl.fl_owner;
/* Now check for conflicting locks */ /* Now check for conflicting locks */
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie)); resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie));
if (resp->status == nlm_drop_reply) if (resp->status == nlm_drop_reply)
@ -137,7 +131,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
dprintk("lockd: TEST status %d vers %d\n", dprintk("lockd: TEST status %d vers %d\n",
ntohl(resp->status), rqstp->rq_vers); ntohl(resp->status), rqstp->rq_vers);
nlmsvc_put_lockowner(test_owner); nlmsvc_release_lockowner(&argp->lock);
nlmsvc_release_host(host); nlmsvc_release_host(host);
nlm_release_file(file); nlm_release_file(file);
return rc; return rc;
@ -305,6 +299,8 @@ nlmsvc_proc_granted(struct svc_rqst *rqstp)
*/ */
static void nlmsvc_callback_exit(struct rpc_task *task, void *data) static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
{ {
dprintk("lockd: %5u callback returned %d\n", task->tk_pid,
-task->tk_status);
} }
void nlmsvc_release_call(struct nlm_rqst *call) void nlmsvc_release_call(struct nlm_rqst *call)
@ -556,239 +552,191 @@ const struct svc_procedure nlmsvc_procedures[24] = {
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "NULL",
}, },
[NLMPROC_TEST] = { [NLMPROC_TEST] = {
.pc_func = nlmsvc_proc_test, .pc_func = nlmsvc_proc_test,
.pc_decode = nlmsvc_decode_testargs, .pc_decode = nlmsvc_decode_testargs,
.pc_encode = nlmsvc_encode_testres, .pc_encode = nlmsvc_encode_testres,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St+2+No+Rg, .pc_xdrressize = Ck+St+2+No+Rg,
.pc_name = "TEST",
}, },
[NLMPROC_LOCK] = { [NLMPROC_LOCK] = {
.pc_func = nlmsvc_proc_lock, .pc_func = nlmsvc_proc_lock,
.pc_decode = nlmsvc_decode_lockargs, .pc_decode = nlmsvc_decode_lockargs,
.pc_encode = nlmsvc_encode_res, .pc_encode = nlmsvc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "LOCK",
}, },
[NLMPROC_CANCEL] = { [NLMPROC_CANCEL] = {
.pc_func = nlmsvc_proc_cancel, .pc_func = nlmsvc_proc_cancel,
.pc_decode = nlmsvc_decode_cancargs, .pc_decode = nlmsvc_decode_cancargs,
.pc_encode = nlmsvc_encode_res, .pc_encode = nlmsvc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "CANCEL",
}, },
[NLMPROC_UNLOCK] = { [NLMPROC_UNLOCK] = {
.pc_func = nlmsvc_proc_unlock, .pc_func = nlmsvc_proc_unlock,
.pc_decode = nlmsvc_decode_unlockargs, .pc_decode = nlmsvc_decode_unlockargs,
.pc_encode = nlmsvc_encode_res, .pc_encode = nlmsvc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "UNLOCK",
}, },
[NLMPROC_GRANTED] = { [NLMPROC_GRANTED] = {
.pc_func = nlmsvc_proc_granted, .pc_func = nlmsvc_proc_granted,
.pc_decode = nlmsvc_decode_testargs, .pc_decode = nlmsvc_decode_testargs,
.pc_encode = nlmsvc_encode_res, .pc_encode = nlmsvc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "GRANTED",
}, },
[NLMPROC_TEST_MSG] = { [NLMPROC_TEST_MSG] = {
.pc_func = nlmsvc_proc_test_msg, .pc_func = nlmsvc_proc_test_msg,
.pc_decode = nlmsvc_decode_testargs, .pc_decode = nlmsvc_decode_testargs,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "TEST_MSG",
}, },
[NLMPROC_LOCK_MSG] = { [NLMPROC_LOCK_MSG] = {
.pc_func = nlmsvc_proc_lock_msg, .pc_func = nlmsvc_proc_lock_msg,
.pc_decode = nlmsvc_decode_lockargs, .pc_decode = nlmsvc_decode_lockargs,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "LOCK_MSG",
}, },
[NLMPROC_CANCEL_MSG] = { [NLMPROC_CANCEL_MSG] = {
.pc_func = nlmsvc_proc_cancel_msg, .pc_func = nlmsvc_proc_cancel_msg,
.pc_decode = nlmsvc_decode_cancargs, .pc_decode = nlmsvc_decode_cancargs,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "CANCEL_MSG",
}, },
[NLMPROC_UNLOCK_MSG] = { [NLMPROC_UNLOCK_MSG] = {
.pc_func = nlmsvc_proc_unlock_msg, .pc_func = nlmsvc_proc_unlock_msg,
.pc_decode = nlmsvc_decode_unlockargs, .pc_decode = nlmsvc_decode_unlockargs,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "UNLOCK_MSG",
}, },
[NLMPROC_GRANTED_MSG] = { [NLMPROC_GRANTED_MSG] = {
.pc_func = nlmsvc_proc_granted_msg, .pc_func = nlmsvc_proc_granted_msg,
.pc_decode = nlmsvc_decode_testargs, .pc_decode = nlmsvc_decode_testargs,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "GRANTED_MSG",
}, },
[NLMPROC_TEST_RES] = { [NLMPROC_TEST_RES] = {
.pc_func = nlmsvc_proc_null, .pc_func = nlmsvc_proc_null,
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "TEST_RES",
}, },
[NLMPROC_LOCK_RES] = { [NLMPROC_LOCK_RES] = {
.pc_func = nlmsvc_proc_null, .pc_func = nlmsvc_proc_null,
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "LOCK_RES",
}, },
[NLMPROC_CANCEL_RES] = { [NLMPROC_CANCEL_RES] = {
.pc_func = nlmsvc_proc_null, .pc_func = nlmsvc_proc_null,
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "CANCEL_RES",
}, },
[NLMPROC_UNLOCK_RES] = { [NLMPROC_UNLOCK_RES] = {
.pc_func = nlmsvc_proc_null, .pc_func = nlmsvc_proc_null,
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "UNLOCK_RES",
}, },
[NLMPROC_GRANTED_RES] = { [NLMPROC_GRANTED_RES] = {
.pc_func = nlmsvc_proc_granted_res, .pc_func = nlmsvc_proc_granted_res,
.pc_decode = nlmsvc_decode_res, .pc_decode = nlmsvc_decode_res,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_res), .pc_argsize = sizeof(struct nlm_res),
.pc_argzero = sizeof(struct nlm_res),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "GRANTED_RES",
}, },
[NLMPROC_NSM_NOTIFY] = { [NLMPROC_NSM_NOTIFY] = {
.pc_func = nlmsvc_proc_sm_notify, .pc_func = nlmsvc_proc_sm_notify,
.pc_decode = nlmsvc_decode_reboot, .pc_decode = nlmsvc_decode_reboot,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_reboot), .pc_argsize = sizeof(struct nlm_reboot),
.pc_argzero = sizeof(struct nlm_reboot),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "SM_NOTIFY",
}, },
[17] = { [17] = {
.pc_func = nlmsvc_proc_unused, .pc_func = nlmsvc_proc_unused,
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "UNUSED",
}, },
[18] = { [18] = {
.pc_func = nlmsvc_proc_unused, .pc_func = nlmsvc_proc_unused,
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "UNUSED",
}, },
[19] = { [19] = {
.pc_func = nlmsvc_proc_unused, .pc_func = nlmsvc_proc_unused,
.pc_decode = nlmsvc_decode_void, .pc_decode = nlmsvc_decode_void,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_void), .pc_argsize = sizeof(struct nlm_void),
.pc_argzero = sizeof(struct nlm_void),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = St, .pc_xdrressize = St,
.pc_name = "UNUSED",
}, },
[NLMPROC_SHARE] = { [NLMPROC_SHARE] = {
.pc_func = nlmsvc_proc_share, .pc_func = nlmsvc_proc_share,
.pc_decode = nlmsvc_decode_shareargs, .pc_decode = nlmsvc_decode_shareargs,
.pc_encode = nlmsvc_encode_shareres, .pc_encode = nlmsvc_encode_shareres,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St+1, .pc_xdrressize = Ck+St+1,
.pc_name = "SHARE",
}, },
[NLMPROC_UNSHARE] = { [NLMPROC_UNSHARE] = {
.pc_func = nlmsvc_proc_unshare, .pc_func = nlmsvc_proc_unshare,
.pc_decode = nlmsvc_decode_shareargs, .pc_decode = nlmsvc_decode_shareargs,
.pc_encode = nlmsvc_encode_shareres, .pc_encode = nlmsvc_encode_shareres,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St+1, .pc_xdrressize = Ck+St+1,
.pc_name = "UNSHARE",
}, },
[NLMPROC_NM_LOCK] = { [NLMPROC_NM_LOCK] = {
.pc_func = nlmsvc_proc_nm_lock, .pc_func = nlmsvc_proc_nm_lock,
.pc_decode = nlmsvc_decode_lockargs, .pc_decode = nlmsvc_decode_lockargs,
.pc_encode = nlmsvc_encode_res, .pc_encode = nlmsvc_encode_res,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_res), .pc_ressize = sizeof(struct nlm_res),
.pc_xdrressize = Ck+St, .pc_xdrressize = Ck+St,
.pc_name = "NM_LOCK",
}, },
[NLMPROC_FREE_ALL] = { [NLMPROC_FREE_ALL] = {
.pc_func = nlmsvc_proc_free_all, .pc_func = nlmsvc_proc_free_all,
.pc_decode = nlmsvc_decode_notify, .pc_decode = nlmsvc_decode_notify,
.pc_encode = nlmsvc_encode_void, .pc_encode = nlmsvc_encode_void,
.pc_argsize = sizeof(struct nlm_args), .pc_argsize = sizeof(struct nlm_args),
.pc_argzero = sizeof(struct nlm_args),
.pc_ressize = sizeof(struct nlm_void), .pc_ressize = sizeof(struct nlm_void),
.pc_xdrressize = 0, .pc_xdrressize = 0,
.pc_name = "FREE_ALL",
}, },
}; };

View File

@ -45,7 +45,7 @@ static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
static inline void nlm_debug_print_file(char *msg, struct nlm_file *file) static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
{ {
struct inode *inode = nlmsvc_file_inode(file); struct inode *inode = locks_inode(file->f_file);
dprintk("lockd: %s %s/%ld\n", dprintk("lockd: %s %s/%ld\n",
msg, inode->i_sb->s_id, inode->i_ino); msg, inode->i_sb->s_id, inode->i_ino);
@ -71,75 +71,56 @@ static inline unsigned int file_hash(struct nfs_fh *f)
return tmp & (FILE_NRHASH - 1); return tmp & (FILE_NRHASH - 1);
} }
int lock_to_openmode(struct file_lock *lock)
{
return (lock->fl_type == F_WRLCK) ? O_WRONLY : O_RDONLY;
}
/*
* Open the file. Note that if we're reexporting, for example,
* this could block the lockd thread for a while.
*
* We have to make sure we have the right credential to open
* the file.
*/
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
struct nlm_file *file, int mode)
{
struct file **fp = &file->f_file[mode];
__be32 nfserr;
if (*fp)
return 0;
nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
if (nfserr)
dprintk("lockd: open failed (error %d)\n", nfserr);
return nfserr;
}
/* /*
* Lookup file info. If it doesn't exist, create a file info struct * Lookup file info. If it doesn't exist, create a file info struct
* and open a (VFS) file for the given inode. * and open a (VFS) file for the given inode.
*
* FIXME:
* Note that we open the file O_RDONLY even when creating write locks.
* This is not quite right, but for now, we assume the client performs
* the proper R/W checking.
*/ */
__be32 __be32
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
struct nlm_lock *lock) struct nfs_fh *f)
{ {
struct nlm_file *file; struct nlm_file *file;
unsigned int hash; unsigned int hash;
__be32 nfserr; __be32 nfserr;
int mode;
nlm_debug_print_fh("nlm_lookup_file", &lock->fh); nlm_debug_print_fh("nlm_lookup_file", f);
hash = file_hash(&lock->fh); hash = file_hash(f);
mode = lock_to_openmode(&lock->fl);
/* Lock file table */ /* Lock file table */
mutex_lock(&nlm_file_mutex); mutex_lock(&nlm_file_mutex);
hlist_for_each_entry(file, &nlm_files[hash], f_list) hlist_for_each_entry(file, &nlm_files[hash], f_list)
if (!nfs_compare_fh(&file->f_handle, &lock->fh)) { if (!nfs_compare_fh(&file->f_handle, f))
mutex_lock(&file->f_mutex);
nfserr = nlm_do_fopen(rqstp, file, mode);
mutex_unlock(&file->f_mutex);
goto found; goto found;
}
nlm_debug_print_fh("creating file for", &lock->fh); nlm_debug_print_fh("creating file for", f);
nfserr = nlm_lck_denied_nolocks; nfserr = nlm_lck_denied_nolocks;
file = kzalloc(sizeof(*file), GFP_KERNEL); file = kzalloc(sizeof(*file), GFP_KERNEL);
if (!file) if (!file)
goto out_free; goto out_unlock;
memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh)); memcpy(&file->f_handle, f, sizeof(struct nfs_fh));
mutex_init(&file->f_mutex); mutex_init(&file->f_mutex);
INIT_HLIST_NODE(&file->f_list); INIT_HLIST_NODE(&file->f_list);
INIT_LIST_HEAD(&file->f_blocks); INIT_LIST_HEAD(&file->f_blocks);
nfserr = nlm_do_fopen(rqstp, file, mode); /* Open the file. Note that this must not sleep for too long, else
if (nfserr) * we would lock up lockd:-) So no NFS re-exports, folks.
goto out_unlock; *
* We have to make sure we have the right credential to open
* the file.
*/
if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) {
dprintk("lockd: open failed (error %d)\n", nfserr);
goto out_free;
}
hlist_add_head(&file->f_list, &nlm_files[hash]); hlist_add_head(&file->f_list, &nlm_files[hash]);
@ -147,6 +128,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
dprintk("lockd: found file %p (count %d)\n", file, file->f_count); dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
*result = file; *result = file;
file->f_count++; file->f_count++;
nfserr = 0;
out_unlock: out_unlock:
mutex_unlock(&nlm_file_mutex); mutex_unlock(&nlm_file_mutex);
@ -166,40 +148,13 @@ nlm_delete_file(struct nlm_file *file)
nlm_debug_print_file("closing file", file); nlm_debug_print_file("closing file", file);
if (!hlist_unhashed(&file->f_list)) { if (!hlist_unhashed(&file->f_list)) {
hlist_del(&file->f_list); hlist_del(&file->f_list);
if (file->f_file[O_RDONLY]) nlmsvc_ops->fclose(file->f_file);
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
kfree(file); kfree(file);
} else { } else {
printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
} }
} }
static int nlm_unlock_files(struct nlm_file *file, const struct file_lock *fl)
{
struct file_lock lock;
locks_init_lock(&lock);
lock.fl_type = F_UNLCK;
lock.fl_start = 0;
lock.fl_end = OFFSET_MAX;
lock.fl_owner = fl->fl_owner;
lock.fl_pid = fl->fl_pid;
lock.fl_flags = FL_POSIX;
lock.fl_file = file->f_file[O_RDONLY];
if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
goto out_err;
lock.fl_file = file->f_file[O_WRONLY];
if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
goto out_err;
return 0;
out_err:
pr_warn("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__);
return 1;
}
/* /*
* Loop over all locks on the given file and perform the specified * Loop over all locks on the given file and perform the specified
* action. * action.
@ -210,7 +165,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
{ {
struct inode *inode = nlmsvc_file_inode(file); struct inode *inode = nlmsvc_file_inode(file);
struct file_lock *fl; struct file_lock *fl;
struct file_lock_context *flctx = locks_inode_context(inode); struct file_lock_context *flctx = inode->i_flctx;
struct nlm_host *lockhost; struct nlm_host *lockhost;
if (!flctx || list_empty_careful(&flctx->flc_posix)) if (!flctx || list_empty_careful(&flctx->flc_posix))
@ -227,10 +182,17 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host; lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
if (match(lockhost, host)) { if (match(lockhost, host)) {
struct file_lock lock = *fl;
spin_unlock(&flctx->flc_lock); spin_unlock(&flctx->flc_lock);
if (nlm_unlock_files(file, fl)) lock.fl_type = F_UNLCK;
lock.fl_start = 0;
lock.fl_end = OFFSET_MAX;
if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) {
printk("lockd: unlock failure in %s:%d\n",
__FILE__, __LINE__);
return 1; return 1;
}
goto again; goto again;
} }
} }
@ -265,7 +227,7 @@ nlm_file_inuse(struct nlm_file *file)
{ {
struct inode *inode = nlmsvc_file_inode(file); struct inode *inode = nlmsvc_file_inode(file);
struct file_lock *fl; struct file_lock *fl;
struct file_lock_context *flctx = locks_inode_context(inode); struct file_lock_context *flctx = inode->i_flctx;
if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares) if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
return 1; return 1;
@ -284,14 +246,6 @@ nlm_file_inuse(struct nlm_file *file)
return 0; return 0;
} }
static void nlm_close_files(struct nlm_file *file)
{
if (file->f_file[O_RDONLY])
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
}
/* /*
* Loop over all files in the file table. * Loop over all files in the file table.
*/ */
@ -322,7 +276,7 @@ nlm_traverse_files(void *data, nlm_host_match_fn_t match,
if (list_empty(&file->f_blocks) && !file->f_locks if (list_empty(&file->f_blocks) && !file->f_locks
&& !file->f_shares && !file->f_count) { && !file->f_shares && !file->f_count) {
hlist_del(&file->f_list); hlist_del(&file->f_list);
nlm_close_files(file); nlmsvc_ops->fclose(file->f_file);
kfree(file); kfree(file);
} }
} }
@ -456,13 +410,12 @@ nlmsvc_invalidate_all(void)
nlm_traverse_files(NULL, nlmsvc_is_client, NULL); nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
} }
static int static int
nlmsvc_match_sb(void *datap, struct nlm_file *file) nlmsvc_match_sb(void *datap, struct nlm_file *file)
{ {
struct super_block *sb = datap; struct super_block *sb = datap;
return sb == nlmsvc_file_inode(file)->i_sb; return sb == locks_inode(file->f_file)->i_sb;
} }
/** /**

View File

@ -1,142 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Encode/decode NLM basic data types
*
* Basic NLMv3 XDR data types are not defined in an IETF standards
* document. X/Open has a description of these data types that
* is useful. See Chapter 10 of "Protocols for Interworking:
* XNFS, Version 3W".
*
* Basic NLMv4 XDR data types are defined in Appendix II.1.4 of
* RFC 1813: "NFS Version 3 Protocol Specification".
*
* Author: Chuck Lever <chuck.lever@oracle.com>
*
* Copyright (c) 2020, Oracle and/or its affiliates.
*/
#ifndef _LOCKD_SVCXDR_H_
#define _LOCKD_SVCXDR_H_
static inline bool
svcxdr_decode_stats(struct xdr_stream *xdr, __be32 *status)
{
__be32 *p;
p = xdr_inline_decode(xdr, XDR_UNIT);
if (!p)
return false;
*status = *p;
return true;
}
static inline bool
svcxdr_encode_stats(struct xdr_stream *xdr, __be32 status)
{
__be32 *p;
p = xdr_reserve_space(xdr, XDR_UNIT);
if (!p)
return false;
*p = status;
return true;
}
static inline bool
svcxdr_decode_string(struct xdr_stream *xdr, char **data, unsigned int *data_len)
{
__be32 *p;
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0)
return false;
if (len > NLM_MAXSTRLEN)
return false;
p = xdr_inline_decode(xdr, len);
if (!p)
return false;
*data_len = len;
*data = (char *)p;
return true;
}
/*
* NLM cookies are defined by specification to be a variable-length
* XDR opaque no longer than 1024 bytes. However, this implementation
* limits their length to 32 bytes, and treats zero-length cookies
* specially.
*/
static inline bool
svcxdr_decode_cookie(struct xdr_stream *xdr, struct nlm_cookie *cookie)
{
__be32 *p;
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0)
return false;
if (len > NLM_MAXCOOKIELEN)
return false;
if (!len)
goto out_hpux;
p = xdr_inline_decode(xdr, len);
if (!p)
return false;
cookie->len = len;
memcpy(cookie->data, p, len);
return true;
/* apparently HPUX can return empty cookies */
out_hpux:
cookie->len = 4;
memset(cookie->data, 0, 4);
return true;
}
static inline bool
svcxdr_encode_cookie(struct xdr_stream *xdr, const struct nlm_cookie *cookie)
{
__be32 *p;
if (xdr_stream_encode_u32(xdr, cookie->len) < 0)
return false;
p = xdr_reserve_space(xdr, cookie->len);
if (!p)
return false;
memcpy(p, cookie->data, cookie->len);
return true;
}
static inline bool
svcxdr_decode_owner(struct xdr_stream *xdr, struct xdr_netobj *obj)
{
__be32 *p;
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0)
return false;
if (len > XDR_MAX_NETOBJ)
return false;
p = xdr_inline_decode(xdr, len);
if (!p)
return false;
obj->len = len;
obj->data = (u8 *)p;
return true;
}
static inline bool
svcxdr_encode_owner(struct xdr_stream *xdr, const struct xdr_netobj *obj)
{
if (obj->len > XDR_MAX_NETOBJ)
return false;
return xdr_stream_encode_opaque(xdr, obj->data, obj->len) > 0;
}
#endif /* _LOCKD_SVCXDR_H_ */

View File

@ -19,7 +19,7 @@
#include <uapi/linux/nfs2.h> #include <uapi/linux/nfs2.h>
#include "svcxdr.h" #define NLMDBG_FACILITY NLMDBG_XDR
static inline loff_t static inline loff_t
@ -42,313 +42,311 @@ loff_t_to_s32(loff_t offset)
} }
/* /*
* NLM file handles are defined by specification to be a variable-length * XDR functions for basic NLM types
* XDR opaque no longer than 1024 bytes. However, this implementation
* constrains their length to exactly the length of an NFSv2 file
* handle.
*/ */
static bool static __be32 *nlm_decode_cookie(__be32 *p, struct nlm_cookie *c)
svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
{ {
__be32 *p; unsigned int len;
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0) len = ntohl(*p++);
return false;
if (len != NFS2_FHSIZE) if(len==0)
return false; {
c->len=4;
p = xdr_inline_decode(xdr, len); memset(c->data, 0, 4); /* hockeypux brain damage */
if (!p) }
return false; else if(len<=NLM_MAXCOOKIELEN)
fh->size = NFS2_FHSIZE; {
memcpy(fh->data, p, len); c->len=len;
memset(fh->data + NFS2_FHSIZE, 0, sizeof(fh->data) - NFS2_FHSIZE); memcpy(c->data, p, len);
p+=XDR_QUADLEN(len);
return true; }
else
{
dprintk("lockd: bad cookie size %d (only cookies under "
"%d bytes are supported.)\n",
len, NLM_MAXCOOKIELEN);
return NULL;
}
return p;
} }
static bool static inline __be32 *
svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) nlm_encode_cookie(__be32 *p, struct nlm_cookie *c)
{ {
struct file_lock *fl = &lock->fl; *p++ = htonl(c->len);
s32 start, len, end; memcpy(p, c->data, c->len);
p+=XDR_QUADLEN(c->len);
return p;
}
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) static __be32 *
return false; nlm_decode_fh(__be32 *p, struct nfs_fh *f)
if (!svcxdr_decode_fhandle(xdr, &lock->fh)) {
return false; unsigned int len;
if (!svcxdr_decode_owner(xdr, &lock->oh))
return false; if ((len = ntohl(*p++)) != NFS2_FHSIZE) {
if (xdr_stream_decode_u32(xdr, &lock->svid) < 0) dprintk("lockd: bad fhandle size %d (should be %d)\n",
return false; len, NFS2_FHSIZE);
if (xdr_stream_decode_u32(xdr, &start) < 0) return NULL;
return false; }
if (xdr_stream_decode_u32(xdr, &len) < 0) f->size = NFS2_FHSIZE;
return false; memset(f->data, 0, sizeof(f->data));
memcpy(f->data, p, NFS2_FHSIZE);
return p + XDR_QUADLEN(NFS2_FHSIZE);
}
/*
* Encode and decode owner handle
*/
static inline __be32 *
nlm_decode_oh(__be32 *p, struct xdr_netobj *oh)
{
return xdr_decode_netobj(p, oh);
}
static inline __be32 *
nlm_encode_oh(__be32 *p, struct xdr_netobj *oh)
{
return xdr_encode_netobj(p, oh);
}
static __be32 *
nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
{
struct file_lock *fl = &lock->fl;
s32 start, len, end;
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
&lock->len,
NLM_MAXSTRLEN))
|| !(p = nlm_decode_fh(p, &lock->fh))
|| !(p = nlm_decode_oh(p, &lock->oh)))
return NULL;
lock->svid = ntohl(*p++);
locks_init_lock(fl); locks_init_lock(fl);
fl->fl_flags = FL_POSIX; fl->fl_flags = FL_POSIX;
fl->fl_type = F_RDLCK; fl->fl_type = F_RDLCK; /* as good as anything else */
start = ntohl(*p++);
len = ntohl(*p++);
end = start + len - 1; end = start + len - 1;
fl->fl_start = s32_to_loff_t(start); fl->fl_start = s32_to_loff_t(start);
if (len == 0 || end < 0) if (len == 0 || end < 0)
fl->fl_end = OFFSET_MAX; fl->fl_end = OFFSET_MAX;
else else
fl->fl_end = s32_to_loff_t(end); fl->fl_end = s32_to_loff_t(end);
return p;
return true;
} }
static bool /*
svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock) * Encode result of a TEST/TEST_MSG call
*/
static __be32 *
nlm_encode_testres(__be32 *p, struct nlm_res *resp)
{ {
const struct file_lock *fl = &lock->fl; s32 start, len;
s32 start, len;
/* exclusive */ if (!(p = nlm_encode_cookie(p, &resp->cookie)))
if (xdr_stream_encode_bool(xdr, fl->fl_type != F_RDLCK) < 0) return NULL;
return false; *p++ = resp->status;
if (xdr_stream_encode_u32(xdr, lock->svid) < 0)
return false;
if (!svcxdr_encode_owner(xdr, &lock->oh))
return false;
start = loff_t_to_s32(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
len = 0;
else
len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
if (xdr_stream_encode_u32(xdr, start) < 0)
return false;
if (xdr_stream_encode_u32(xdr, len) < 0)
return false;
return true; if (resp->status == nlm_lck_denied) {
} struct file_lock *fl = &resp->lock.fl;
static bool *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp) *p++ = htonl(resp->lock.svid);
{
if (!svcxdr_encode_stats(xdr, resp->status)) /* Encode owner handle. */
return false; if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
switch (resp->status) { return NULL;
case nlm_lck_denied:
if (!svcxdr_encode_holder(xdr, &resp->lock)) start = loff_t_to_s32(fl->fl_start);
return false; if (fl->fl_end == OFFSET_MAX)
len = 0;
else
len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
*p++ = htonl(start);
*p++ = htonl(len);
} }
return true; return p;
} }
/* /*
* Decode Call arguments * First, the server side XDR functions
*/ */
int
bool nlmsvc_decode_testargs(struct svc_rqst *rqstp, __be32 *p)
nlmsvc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return true;
}
bool
nlmsvc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
u32 exclusive; u32 exclusive;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm_decode_cookie(p, &argp->cookie)))
return false; return 0;
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
return false; exclusive = ntohl(*p++);
if (!svcxdr_decode_lock(xdr, &argp->lock)) if (!(p = nlm_decode_lock(p, &argp->lock)))
return false; return 0;
if (exclusive) if (exclusive)
argp->lock.fl.fl_type = F_WRLCK; argp->lock.fl.fl_type = F_WRLCK;
return true; return xdr_argsize_check(rqstp, p);
} }
bool int
nlmsvc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_encode_testres(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_resp;
if (!(p = nlm_encode_testres(p, resp)))
return 0;
return xdr_ressize_check(rqstp, p);
}
int
nlmsvc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
u32 exclusive; u32 exclusive;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm_decode_cookie(p, &argp->cookie)))
return false; return 0;
if (xdr_stream_decode_bool(xdr, &argp->block) < 0) argp->block = ntohl(*p++);
return false; exclusive = ntohl(*p++);
if (xdr_stream_decode_bool(xdr, &exclusive) < 0) if (!(p = nlm_decode_lock(p, &argp->lock)))
return false; return 0;
if (!svcxdr_decode_lock(xdr, &argp->lock))
return false;
if (exclusive) if (exclusive)
argp->lock.fl.fl_type = F_WRLCK; argp->lock.fl.fl_type = F_WRLCK;
if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0) argp->reclaim = ntohl(*p++);
return false; argp->state = ntohl(*p++);
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
return false;
argp->monitor = 1; /* monitor client by default */ argp->monitor = 1; /* monitor client by default */
return true; return xdr_argsize_check(rqstp, p);
} }
bool int
nlmsvc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
u32 exclusive; u32 exclusive;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm_decode_cookie(p, &argp->cookie)))
return false; return 0;
if (xdr_stream_decode_bool(xdr, &argp->block) < 0) argp->block = ntohl(*p++);
return false; exclusive = ntohl(*p++);
if (xdr_stream_decode_bool(xdr, &exclusive) < 0) if (!(p = nlm_decode_lock(p, &argp->lock)))
return false; return 0;
if (!svcxdr_decode_lock(xdr, &argp->lock))
return false;
if (exclusive) if (exclusive)
argp->lock.fl.fl_type = F_WRLCK; argp->lock.fl.fl_type = F_WRLCK;
return xdr_argsize_check(rqstp, p);
return true;
} }
bool int
nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm_decode_cookie(p, &argp->cookie))
return false; || !(p = nlm_decode_lock(p, &argp->lock)))
if (!svcxdr_decode_lock(xdr, &argp->lock)) return 0;
return false;
argp->lock.fl.fl_type = F_UNLCK; argp->lock.fl.fl_type = F_UNLCK;
return xdr_argsize_check(rqstp, p);
return true;
} }
bool int
nlmsvc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_argp;
if (!svcxdr_decode_cookie(xdr, &resp->cookie))
return false;
if (!svcxdr_decode_stats(xdr, &resp->status))
return false;
return true;
}
bool
nlmsvc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_reboot *argp = rqstp->rq_argp;
__be32 *p;
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0)
return false;
if (len > SM_MAXSTRLEN)
return false;
p = xdr_inline_decode(xdr, len);
if (!p)
return false;
argp->len = len;
argp->mon = (char *)p;
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
return false;
p = xdr_inline_decode(xdr, SM_PRIV_SIZE);
if (!p)
return false;
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
return true;
}
bool
nlmsvc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
struct nlm_lock *lock = &argp->lock; struct nlm_lock *lock = &argp->lock;
memset(lock, 0, sizeof(*lock)); memset(lock, 0, sizeof(*lock));
locks_init_lock(&lock->fl); locks_init_lock(&lock->fl);
lock->svid = ~(u32)0; lock->svid = ~(u32) 0;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm_decode_cookie(p, &argp->cookie))
return false; || !(p = xdr_decode_string_inplace(p, &lock->caller,
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) &lock->len, NLM_MAXSTRLEN))
return false; || !(p = nlm_decode_fh(p, &lock->fh))
if (!svcxdr_decode_fhandle(xdr, &lock->fh)) || !(p = nlm_decode_oh(p, &lock->oh)))
return false; return 0;
if (!svcxdr_decode_owner(xdr, &lock->oh)) argp->fsm_mode = ntohl(*p++);
return false; argp->fsm_access = ntohl(*p++);
/* XXX: Range checks are missing in the original code */ return xdr_argsize_check(rqstp, p);
if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0)
return false;
if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0)
return false;
return true;
} }
bool int
nlmsvc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_encode_shareres(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_resp;
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
return 0;
*p++ = resp->status;
*p++ = xdr_zero; /* sequence argument */
return xdr_ressize_check(rqstp, p);
}
int
nlmsvc_encode_res(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_resp;
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
return 0;
*p++ = resp->status;
return xdr_ressize_check(rqstp, p);
}
int
nlmsvc_decode_notify(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
struct nlm_lock *lock = &argp->lock; struct nlm_lock *lock = &argp->lock;
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) if (!(p = xdr_decode_string_inplace(p, &lock->caller,
return false; &lock->len, NLM_MAXSTRLEN)))
if (xdr_stream_decode_u32(xdr, &argp->state) < 0) return 0;
return false; argp->state = ntohl(*p++);
return xdr_argsize_check(rqstp, p);
return true;
} }
int
/* nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p)
* Encode Reply results
*/
bool
nlmsvc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
return true; struct nlm_reboot *argp = rqstp->rq_argp;
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
return 0;
argp->state = ntohl(*p++);
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
p += XDR_QUADLEN(SM_PRIV_SIZE);
return xdr_argsize_check(rqstp, p);
} }
bool int
nlmsvc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_decode_res(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_res *resp = rqstp->rq_resp; struct nlm_res *resp = rqstp->rq_argp;
return svcxdr_encode_cookie(xdr, &resp->cookie) && if (!(p = nlm_decode_cookie(p, &resp->cookie)))
svcxdr_encode_testrply(xdr, resp); return 0;
resp->status = *p++;
return xdr_argsize_check(rqstp, p);
} }
bool int
nlmsvc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_decode_void(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_res *resp = rqstp->rq_resp; return xdr_argsize_check(rqstp, p);
return svcxdr_encode_cookie(xdr, &resp->cookie) &&
svcxdr_encode_stats(xdr, resp->status);
} }
bool int
nlmsvc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_res *resp = rqstp->rq_resp; return xdr_ressize_check(rqstp, p);
if (!svcxdr_encode_cookie(xdr, &resp->cookie))
return false;
if (!svcxdr_encode_stats(xdr, resp->status))
return false;
/* sequence */
if (xdr_stream_encode_u32(xdr, 0) < 0)
return false;
return true;
} }

View File

@ -18,7 +18,14 @@
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include "svcxdr.h" #define NLMDBG_FACILITY NLMDBG_XDR
static inline loff_t
s64_to_loff_t(__s64 offset)
{
return (loff_t)offset;
}
static inline s64 static inline s64
loff_t_to_s64(loff_t offset) loff_t_to_s64(loff_t offset)
@ -33,317 +40,310 @@ loff_t_to_s64(loff_t offset)
return res; return res;
} }
void nlm4svc_set_file_lock_range(struct file_lock *fl, u64 off, u64 len) /*
* XDR functions for basic NLM types
*/
static __be32 *
nlm4_decode_cookie(__be32 *p, struct nlm_cookie *c)
{ {
s64 end = off + len - 1; unsigned int len;
fl->fl_start = off; len = ntohl(*p++);
if (len == 0 || end < 0)
fl->fl_end = OFFSET_MAX; if(len==0)
else {
fl->fl_end = end; c->len=4;
memset(c->data, 0, 4); /* hockeypux brain damage */
}
else if(len<=NLM_MAXCOOKIELEN)
{
c->len=len;
memcpy(c->data, p, len);
p+=XDR_QUADLEN(len);
}
else
{
dprintk("lockd: bad cookie size %d (only cookies under "
"%d bytes are supported.)\n",
len, NLM_MAXCOOKIELEN);
return NULL;
}
return p;
}
static __be32 *
nlm4_encode_cookie(__be32 *p, struct nlm_cookie *c)
{
*p++ = htonl(c->len);
memcpy(p, c->data, c->len);
p+=XDR_QUADLEN(c->len);
return p;
}
static __be32 *
nlm4_decode_fh(__be32 *p, struct nfs_fh *f)
{
memset(f->data, 0, sizeof(f->data));
f->size = ntohl(*p++);
if (f->size > NFS_MAXFHSIZE) {
dprintk("lockd: bad fhandle size %d (should be <=%d)\n",
f->size, NFS_MAXFHSIZE);
return NULL;
}
memcpy(f->data, p, f->size);
return p + XDR_QUADLEN(f->size);
} }
/* /*
* NLM file handles are defined by specification to be a variable-length * Encode and decode owner handle
* XDR opaque no longer than 1024 bytes. However, this implementation
* limits their length to the size of an NFSv3 file handle.
*/ */
static bool static __be32 *
svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh)
{ {
__be32 *p; return xdr_decode_netobj(p, oh);
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0)
return false;
if (len > NFS_MAXFHSIZE)
return false;
p = xdr_inline_decode(xdr, len);
if (!p)
return false;
fh->size = len;
memcpy(fh->data, p, len);
memset(fh->data + len, 0, sizeof(fh->data) - len);
return true;
} }
static bool static __be32 *
svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
{ {
struct file_lock *fl = &lock->fl; struct file_lock *fl = &lock->fl;
__u64 len, start;
__s64 end;
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) if (!(p = xdr_decode_string_inplace(p, &lock->caller,
return false; &lock->len, NLM_MAXSTRLEN))
if (!svcxdr_decode_fhandle(xdr, &lock->fh)) || !(p = nlm4_decode_fh(p, &lock->fh))
return false; || !(p = nlm4_decode_oh(p, &lock->oh)))
if (!svcxdr_decode_owner(xdr, &lock->oh)) return NULL;
return false; lock->svid = ntohl(*p++);
if (xdr_stream_decode_u32(xdr, &lock->svid) < 0)
return false;
if (xdr_stream_decode_u64(xdr, &lock->lock_start) < 0)
return false;
if (xdr_stream_decode_u64(xdr, &lock->lock_len) < 0)
return false;
locks_init_lock(fl); locks_init_lock(fl);
fl->fl_flags = FL_POSIX; fl->fl_flags = FL_POSIX;
fl->fl_type = F_RDLCK; fl->fl_type = F_RDLCK; /* as good as anything else */
nlm4svc_set_file_lock_range(fl, lock->lock_start, lock->lock_len); p = xdr_decode_hyper(p, &start);
return true; p = xdr_decode_hyper(p, &len);
} end = start + len - 1;
static bool fl->fl_start = s64_to_loff_t(start);
svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock)
{
const struct file_lock *fl = &lock->fl;
s64 start, len;
/* exclusive */ if (len == 0 || end < 0)
if (xdr_stream_encode_bool(xdr, fl->fl_type != F_RDLCK) < 0) fl->fl_end = OFFSET_MAX;
return false;
if (xdr_stream_encode_u32(xdr, lock->svid) < 0)
return false;
if (!svcxdr_encode_owner(xdr, &lock->oh))
return false;
start = loff_t_to_s64(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
len = 0;
else else
len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1); fl->fl_end = s64_to_loff_t(end);
if (xdr_stream_encode_u64(xdr, start) < 0) return p;
return false;
if (xdr_stream_encode_u64(xdr, len) < 0)
return false;
return true;
} }
static bool /*
svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp) * Encode result of a TEST/TEST_MSG call
*/
static __be32 *
nlm4_encode_testres(__be32 *p, struct nlm_res *resp)
{ {
if (!svcxdr_encode_stats(xdr, resp->status)) s64 start, len;
return false;
switch (resp->status) { dprintk("xdr: before encode_testres (p %p resp %p)\n", p, resp);
case nlm_lck_denied: if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
if (!svcxdr_encode_holder(xdr, &resp->lock)) return NULL;
return false; *p++ = resp->status;
if (resp->status == nlm_lck_denied) {
struct file_lock *fl = &resp->lock.fl;
*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
*p++ = htonl(resp->lock.svid);
/* Encode owner handle. */
if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
return NULL;
start = loff_t_to_s64(fl->fl_start);
if (fl->fl_end == OFFSET_MAX)
len = 0;
else
len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
p = xdr_encode_hyper(p, start);
p = xdr_encode_hyper(p, len);
dprintk("xdr: encode_testres (status %u pid %d type %d start %Ld end %Ld)\n",
resp->status, (int)resp->lock.svid, fl->fl_type,
(long long)fl->fl_start, (long long)fl->fl_end);
} }
return true; dprintk("xdr: after encode_testres (p %p resp %p)\n", p, resp);
return p;
} }
/* /*
* Decode Call arguments * First, the server side XDR functions
*/ */
int
bool nlm4svc_decode_testargs(struct svc_rqst *rqstp, __be32 *p)
nlm4svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return true;
}
bool
nlm4svc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
u32 exclusive; u32 exclusive;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
return false; return 0;
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
return false; exclusive = ntohl(*p++);
if (!svcxdr_decode_lock(xdr, &argp->lock)) if (!(p = nlm4_decode_lock(p, &argp->lock)))
return false; return 0;
if (exclusive) if (exclusive)
argp->lock.fl.fl_type = F_WRLCK; argp->lock.fl.fl_type = F_WRLCK;
return true; return xdr_argsize_check(rqstp, p);
} }
bool int
nlm4svc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_encode_testres(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_resp;
if (!(p = nlm4_encode_testres(p, resp)))
return 0;
return xdr_ressize_check(rqstp, p);
}
int
nlm4svc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
u32 exclusive; u32 exclusive;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
return false; return 0;
if (xdr_stream_decode_bool(xdr, &argp->block) < 0) argp->block = ntohl(*p++);
return false; exclusive = ntohl(*p++);
if (xdr_stream_decode_bool(xdr, &exclusive) < 0) if (!(p = nlm4_decode_lock(p, &argp->lock)))
return false; return 0;
if (!svcxdr_decode_lock(xdr, &argp->lock))
return false;
if (exclusive) if (exclusive)
argp->lock.fl.fl_type = F_WRLCK; argp->lock.fl.fl_type = F_WRLCK;
if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0) argp->reclaim = ntohl(*p++);
return false; argp->state = ntohl(*p++);
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
return false;
argp->monitor = 1; /* monitor client by default */ argp->monitor = 1; /* monitor client by default */
return true; return xdr_argsize_check(rqstp, p);
} }
bool int
nlm4svc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
u32 exclusive; u32 exclusive;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
return false; return 0;
if (xdr_stream_decode_bool(xdr, &argp->block) < 0) argp->block = ntohl(*p++);
return false; exclusive = ntohl(*p++);
if (xdr_stream_decode_bool(xdr, &exclusive) < 0) if (!(p = nlm4_decode_lock(p, &argp->lock)))
return false; return 0;
if (!svcxdr_decode_lock(xdr, &argp->lock))
return false;
if (exclusive) if (exclusive)
argp->lock.fl.fl_type = F_WRLCK; argp->lock.fl.fl_type = F_WRLCK;
return xdr_argsize_check(rqstp, p);
return true;
} }
bool int
nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm4_decode_cookie(p, &argp->cookie))
return false; || !(p = nlm4_decode_lock(p, &argp->lock)))
if (!svcxdr_decode_lock(xdr, &argp->lock)) return 0;
return false;
argp->lock.fl.fl_type = F_UNLCK; argp->lock.fl.fl_type = F_UNLCK;
return xdr_argsize_check(rqstp, p);
return true;
} }
bool int
nlm4svc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_argp;
if (!svcxdr_decode_cookie(xdr, &resp->cookie))
return false;
if (!svcxdr_decode_stats(xdr, &resp->status))
return false;
return true;
}
bool
nlm4svc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
struct nlm_reboot *argp = rqstp->rq_argp;
__be32 *p;
u32 len;
if (xdr_stream_decode_u32(xdr, &len) < 0)
return false;
if (len > SM_MAXSTRLEN)
return false;
p = xdr_inline_decode(xdr, len);
if (!p)
return false;
argp->len = len;
argp->mon = (char *)p;
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
return false;
p = xdr_inline_decode(xdr, SM_PRIV_SIZE);
if (!p)
return false;
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
return true;
}
bool
nlm4svc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
struct nlm_lock *lock = &argp->lock; struct nlm_lock *lock = &argp->lock;
memset(lock, 0, sizeof(*lock)); memset(lock, 0, sizeof(*lock));
locks_init_lock(&lock->fl); locks_init_lock(&lock->fl);
lock->svid = ~(u32)0; lock->svid = ~(u32) 0;
if (!svcxdr_decode_cookie(xdr, &argp->cookie)) if (!(p = nlm4_decode_cookie(p, &argp->cookie))
return false; || !(p = xdr_decode_string_inplace(p, &lock->caller,
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) &lock->len, NLM_MAXSTRLEN))
return false; || !(p = nlm4_decode_fh(p, &lock->fh))
if (!svcxdr_decode_fhandle(xdr, &lock->fh)) || !(p = nlm4_decode_oh(p, &lock->oh)))
return false; return 0;
if (!svcxdr_decode_owner(xdr, &lock->oh)) argp->fsm_mode = ntohl(*p++);
return false; argp->fsm_access = ntohl(*p++);
/* XXX: Range checks are missing in the original code */ return xdr_argsize_check(rqstp, p);
if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0)
return false;
if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0)
return false;
return true;
} }
bool int
nlm4svc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_encode_shareres(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_resp;
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
return 0;
*p++ = resp->status;
*p++ = xdr_zero; /* sequence argument */
return xdr_ressize_check(rqstp, p);
}
int
nlm4svc_encode_res(struct svc_rqst *rqstp, __be32 *p)
{
struct nlm_res *resp = rqstp->rq_resp;
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
return 0;
*p++ = resp->status;
return xdr_ressize_check(rqstp, p);
}
int
nlm4svc_decode_notify(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_args *argp = rqstp->rq_argp; struct nlm_args *argp = rqstp->rq_argp;
struct nlm_lock *lock = &argp->lock; struct nlm_lock *lock = &argp->lock;
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) if (!(p = xdr_decode_string_inplace(p, &lock->caller,
return false; &lock->len, NLM_MAXSTRLEN)))
if (xdr_stream_decode_u32(xdr, &argp->state) < 0) return 0;
return false; argp->state = ntohl(*p++);
return xdr_argsize_check(rqstp, p);
return true;
} }
int
/* nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p)
* Encode Reply results
*/
bool
nlm4svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
return true; struct nlm_reboot *argp = rqstp->rq_argp;
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
return 0;
argp->state = ntohl(*p++);
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
p += XDR_QUADLEN(SM_PRIV_SIZE);
return xdr_argsize_check(rqstp, p);
} }
bool int
nlm4svc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_decode_res(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_res *resp = rqstp->rq_resp; struct nlm_res *resp = rqstp->rq_argp;
return svcxdr_encode_cookie(xdr, &resp->cookie) && if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
svcxdr_encode_testrply(xdr, resp); return 0;
resp->status = *p++;
return xdr_argsize_check(rqstp, p);
} }
bool int
nlm4svc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_decode_void(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_res *resp = rqstp->rq_resp; return xdr_argsize_check(rqstp, p);
return svcxdr_encode_cookie(xdr, &resp->cookie) &&
svcxdr_encode_stats(xdr, resp->status);
} }
bool int
nlm4svc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr) nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nlm_res *resp = rqstp->rq_resp; return xdr_ressize_check(rqstp, p);
if (!svcxdr_encode_cookie(xdr, &resp->cookie))
return false;
if (!svcxdr_encode_stats(xdr, resp->status))
return false;
/* sequence */
if (xdr_stream_encode_u32(xdr, 0) < 0)
return false;
return true;
} }

View File

@ -251,7 +251,7 @@ locks_get_lock_context(struct inode *inode, int type)
struct file_lock_context *ctx; struct file_lock_context *ctx;
/* paired with cmpxchg() below */ /* paired with cmpxchg() below */
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (likely(ctx) || type == F_UNLCK) if (likely(ctx) || type == F_UNLCK)
goto out; goto out;
@ -270,7 +270,7 @@ locks_get_lock_context(struct inode *inode, int type)
*/ */
if (cmpxchg(&inode->i_flctx, NULL, ctx)) { if (cmpxchg(&inode->i_flctx, NULL, ctx)) {
kmem_cache_free(flctx_cache, ctx); kmem_cache_free(flctx_cache, ctx);
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
} }
out: out:
trace_locks_get_lock_context(inode, type, ctx); trace_locks_get_lock_context(inode, type, ctx);
@ -323,7 +323,7 @@ locks_check_ctx_file_list(struct file *filp, struct list_head *list,
void void
locks_free_lock_context(struct inode *inode) locks_free_lock_context(struct inode *inode)
{ {
struct file_lock_context *ctx = locks_inode_context(inode); struct file_lock_context *ctx = inode->i_flctx;
if (unlikely(ctx)) { if (unlikely(ctx)) {
locks_check_ctx_lists(inode); locks_check_ctx_lists(inode);
@ -376,34 +376,6 @@ void locks_release_private(struct file_lock *fl)
} }
EXPORT_SYMBOL_GPL(locks_release_private); EXPORT_SYMBOL_GPL(locks_release_private);
/**
* locks_owner_has_blockers - Check for blocking lock requests
* @flctx: file lock context
* @owner: lock owner
*
* Return values:
* %true: @owner has at least one blocker
* %false: @owner has no blockers
*/
bool locks_owner_has_blockers(struct file_lock_context *flctx,
fl_owner_t owner)
{
struct file_lock *fl;
spin_lock(&flctx->flc_lock);
list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
if (fl->fl_owner != owner)
continue;
if (!list_empty(&fl->fl_blocked_requests)) {
spin_unlock(&flctx->flc_lock);
return true;
}
}
spin_unlock(&flctx->flc_lock);
return false;
}
EXPORT_SYMBOL_GPL(locks_owner_has_blockers);
/* Free a lock which is not in use. */ /* Free a lock which is not in use. */
void locks_free_lock(struct file_lock *fl) void locks_free_lock(struct file_lock *fl)
{ {
@ -982,32 +954,19 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
struct file_lock *cfl; struct file_lock *cfl;
struct file_lock_context *ctx; struct file_lock_context *ctx;
struct inode *inode = locks_inode(filp); struct inode *inode = locks_inode(filp);
void *owner;
void (*func)(void);
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (!ctx || list_empty_careful(&ctx->flc_posix)) { if (!ctx || list_empty_careful(&ctx->flc_posix)) {
fl->fl_type = F_UNLCK; fl->fl_type = F_UNLCK;
return; return;
} }
retry:
spin_lock(&ctx->flc_lock); spin_lock(&ctx->flc_lock);
list_for_each_entry(cfl, &ctx->flc_posix, fl_list) { list_for_each_entry(cfl, &ctx->flc_posix, fl_list) {
if (!posix_locks_conflict(fl, cfl)) if (posix_locks_conflict(fl, cfl)) {
continue; locks_copy_conflock(fl, cfl);
if (cfl->fl_lmops && cfl->fl_lmops->lm_lock_expirable goto out;
&& (*cfl->fl_lmops->lm_lock_expirable)(cfl)) {
owner = cfl->fl_lmops->lm_mod_owner;
func = cfl->fl_lmops->lm_expire_lock;
__module_get(owner);
spin_unlock(&ctx->flc_lock);
(*func)();
module_put(owner);
goto retry;
} }
locks_copy_conflock(fl, cfl);
goto out;
} }
fl->fl_type = F_UNLCK; fl->fl_type = F_UNLCK;
out: out:
@ -1181,8 +1140,6 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
int error; int error;
bool added = false; bool added = false;
LIST_HEAD(dispose); LIST_HEAD(dispose);
void *owner;
void (*func)(void);
ctx = locks_get_lock_context(inode, request->fl_type); ctx = locks_get_lock_context(inode, request->fl_type);
if (!ctx) if (!ctx)
@ -1201,7 +1158,6 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
new_fl2 = locks_alloc_lock(); new_fl2 = locks_alloc_lock();
} }
retry:
percpu_down_read(&file_rwsem); percpu_down_read(&file_rwsem);
spin_lock(&ctx->flc_lock); spin_lock(&ctx->flc_lock);
/* /*
@ -1213,17 +1169,6 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
list_for_each_entry(fl, &ctx->flc_posix, fl_list) { list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
if (!posix_locks_conflict(request, fl)) if (!posix_locks_conflict(request, fl))
continue; continue;
if (fl->fl_lmops && fl->fl_lmops->lm_lock_expirable
&& (*fl->fl_lmops->lm_lock_expirable)(fl)) {
owner = fl->fl_lmops->lm_mod_owner;
func = fl->fl_lmops->lm_expire_lock;
__module_get(owner);
spin_unlock(&ctx->flc_lock);
percpu_up_read(&file_rwsem);
(*func)();
module_put(owner);
goto retry;
}
if (conflock) if (conflock)
locks_copy_conflock(conflock, fl); locks_copy_conflock(conflock, fl);
error = -EAGAIN; error = -EAGAIN;
@ -1674,7 +1619,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
new_fl->fl_flags = type; new_fl->fl_flags = type;
/* typically we will check that ctx is non-NULL before calling */ /* typically we will check that ctx is non-NULL before calling */
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (!ctx) { if (!ctx) {
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
goto free_lock; goto free_lock;
@ -1779,7 +1724,7 @@ void lease_get_mtime(struct inode *inode, struct timespec64 *time)
struct file_lock_context *ctx; struct file_lock_context *ctx;
struct file_lock *fl; struct file_lock *fl;
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (ctx && !list_empty_careful(&ctx->flc_lease)) { if (ctx && !list_empty_careful(&ctx->flc_lease)) {
spin_lock(&ctx->flc_lock); spin_lock(&ctx->flc_lock);
fl = list_first_entry_or_null(&ctx->flc_lease, fl = list_first_entry_or_null(&ctx->flc_lease,
@ -1825,7 +1770,7 @@ int fcntl_getlease(struct file *filp)
int type = F_UNLCK; int type = F_UNLCK;
LIST_HEAD(dispose); LIST_HEAD(dispose);
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (ctx && !list_empty_careful(&ctx->flc_lease)) { if (ctx && !list_empty_careful(&ctx->flc_lease)) {
percpu_down_read(&file_rwsem); percpu_down_read(&file_rwsem);
spin_lock(&ctx->flc_lock); spin_lock(&ctx->flc_lock);
@ -1863,9 +1808,6 @@ check_conflicting_open(struct file *filp, const long arg, int flags)
if (flags & FL_LAYOUT) if (flags & FL_LAYOUT)
return 0; return 0;
if (flags & FL_DELEG)
/* We leave these checks to the caller */
return 0;
if (arg == F_RDLCK) if (arg == F_RDLCK)
return inode_is_open_for_write(inode) ? -EAGAIN : 0; return inode_is_open_for_write(inode) ? -EAGAIN : 0;
@ -2014,7 +1956,7 @@ static int generic_delete_lease(struct file *filp, void *owner)
struct file_lock_context *ctx; struct file_lock_context *ctx;
LIST_HEAD(dispose); LIST_HEAD(dispose);
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (!ctx) { if (!ctx) {
trace_generic_delete_lease(inode, NULL); trace_generic_delete_lease(inode, NULL);
return error; return error;
@ -2594,15 +2536,14 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
*/ */
if (!error && file_lock->fl_type != F_UNLCK && if (!error && file_lock->fl_type != F_UNLCK &&
!(file_lock->fl_flags & FL_OFDLCK)) { !(file_lock->fl_flags & FL_OFDLCK)) {
struct files_struct *files = current->files;
/* /*
* We need that spin_lock here - it prevents reordering between * We need that spin_lock here - it prevents reordering between
* update of i_flctx->flc_posix and check for it done in * update of i_flctx->flc_posix and check for it done in
* close(). rcu_read_lock() wouldn't do. * close(). rcu_read_lock() wouldn't do.
*/ */
spin_lock(&files->file_lock); spin_lock(&current->files->file_lock);
f = files_lookup_fd_locked(files, fd); f = fcheck(fd);
spin_unlock(&files->file_lock); spin_unlock(&current->files->file_lock);
if (f != filp) { if (f != filp) {
file_lock->fl_type = F_UNLCK; file_lock->fl_type = F_UNLCK;
error = do_lock_file_wait(filp, cmd, file_lock); error = do_lock_file_wait(filp, cmd, file_lock);
@ -2726,15 +2667,14 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
*/ */
if (!error && file_lock->fl_type != F_UNLCK && if (!error && file_lock->fl_type != F_UNLCK &&
!(file_lock->fl_flags & FL_OFDLCK)) { !(file_lock->fl_flags & FL_OFDLCK)) {
struct files_struct *files = current->files;
/* /*
* We need that spin_lock here - it prevents reordering between * We need that spin_lock here - it prevents reordering between
* update of i_flctx->flc_posix and check for it done in * update of i_flctx->flc_posix and check for it done in
* close(). rcu_read_lock() wouldn't do. * close(). rcu_read_lock() wouldn't do.
*/ */
spin_lock(&files->file_lock); spin_lock(&current->files->file_lock);
f = files_lookup_fd_locked(files, fd); f = fcheck(fd);
spin_unlock(&files->file_lock); spin_unlock(&current->files->file_lock);
if (f != filp) { if (f != filp) {
file_lock->fl_type = F_UNLCK; file_lock->fl_type = F_UNLCK;
error = do_lock_file_wait(filp, cmd, file_lock); error = do_lock_file_wait(filp, cmd, file_lock);
@ -2765,7 +2705,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
* posix_lock_file(). Another process could be setting a lock on this * posix_lock_file(). Another process could be setting a lock on this
* file at the same time, but we wouldn't remove that lock anyway. * file at the same time, but we wouldn't remove that lock anyway.
*/ */
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (!ctx || list_empty(&ctx->flc_posix)) if (!ctx || list_empty(&ctx->flc_posix))
return; return;
@ -2838,7 +2778,7 @@ void locks_remove_file(struct file *filp)
{ {
struct file_lock_context *ctx; struct file_lock_context *ctx;
ctx = locks_inode_context(locks_inode(filp)); ctx = smp_load_acquire(&locks_inode(filp)->i_flctx);
if (!ctx) if (!ctx)
return; return;
@ -2885,7 +2825,7 @@ bool vfs_inode_has_locks(struct inode *inode)
struct file_lock_context *ctx; struct file_lock_context *ctx;
bool ret; bool ret;
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (!ctx) if (!ctx)
return false; return false;
@ -3030,7 +2970,7 @@ void show_fd_locks(struct seq_file *f,
struct file_lock_context *ctx; struct file_lock_context *ctx;
int id = 0; int id = 0;
ctx = locks_inode_context(inode); ctx = smp_load_acquire(&inode->i_flctx);
if (!ctx) if (!ctx)
return; return;

View File

@ -4361,14 +4361,11 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
* ->i_mutex on parents, which works but leads to some truly excessive * ->i_mutex on parents, which works but leads to some truly excessive
* locking]. * locking].
*/ */
int vfs_rename(struct renamedata *rd) int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
struct inode **delegated_inode, unsigned int flags)
{ {
int error; int error;
struct inode *old_dir = rd->old_dir, *new_dir = rd->new_dir;
struct dentry *old_dentry = rd->old_dentry;
struct dentry *new_dentry = rd->new_dentry;
struct inode **delegated_inode = rd->delegated_inode;
unsigned int flags = rd->flags;
bool is_dir = d_is_dir(old_dentry); bool is_dir = d_is_dir(old_dentry);
struct inode *source = old_dentry->d_inode; struct inode *source = old_dentry->d_inode;
struct inode *target = new_dentry->d_inode; struct inode *target = new_dentry->d_inode;
@ -4516,7 +4513,6 @@ EXPORT_SYMBOL_NS(vfs_rename, ANDROID_GKI_VFS_EXPORT_ONLY);
int do_renameat2(int olddfd, struct filename *from, int newdfd, int do_renameat2(int olddfd, struct filename *from, int newdfd,
struct filename *to, unsigned int flags) struct filename *to, unsigned int flags)
{ {
struct renamedata rd;
struct dentry *old_dentry, *new_dentry; struct dentry *old_dentry, *new_dentry;
struct dentry *trap; struct dentry *trap;
struct path old_path, new_path; struct path old_path, new_path;
@ -4620,14 +4616,9 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
&new_path, new_dentry, flags); &new_path, new_dentry, flags);
if (error) if (error)
goto exit5; goto exit5;
error = vfs_rename(old_path.dentry->d_inode, old_dentry,
rd.old_dir = old_path.dentry->d_inode; new_path.dentry->d_inode, new_dentry,
rd.old_dentry = old_dentry; &delegated_inode, flags);
rd.new_dir = new_path.dentry->d_inode;
rd.new_dentry = new_dentry;
rd.delegated_inode = &delegated_inode;
rd.flags = flags;
error = vfs_rename(&rd);
exit5: exit5:
dput(new_dentry); dput(new_dentry);
exit4: exit4:

View File

@ -699,7 +699,7 @@ bl_alloc_lseg(struct pnfs_layout_hdr *lo, struct nfs4_layoutget_res *lgr,
xdr_init_decode_pages(&xdr, &buf, xdr_init_decode_pages(&xdr, &buf,
lgr->layoutp->pages, lgr->layoutp->len); lgr->layoutp->pages, lgr->layoutp->len);
xdr_set_scratch_page(&xdr, scratch); xdr_set_scratch_buffer(&xdr, page_address(scratch), PAGE_SIZE);
status = -EIO; status = -EIO;
p = xdr_inline_decode(&xdr, 4); p = xdr_inline_decode(&xdr, 4);

View File

@ -510,7 +510,7 @@ bl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
goto out; goto out;
xdr_init_decode_pages(&xdr, &buf, pdev->pages, pdev->pglen); xdr_init_decode_pages(&xdr, &buf, pdev->pages, pdev->pglen);
xdr_set_scratch_page(&xdr, scratch); xdr_set_scratch_buffer(&xdr, page_address(scratch), PAGE_SIZE);
p = xdr_inline_decode(&xdr, sizeof(__be32)); p = xdr_inline_decode(&xdr, sizeof(__be32));
if (!p) if (!p)

View File

@ -17,6 +17,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/sunrpc/svcauth_gss.h> #include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/bc_xprt.h> #include <linux/sunrpc/bc_xprt.h>
@ -44,18 +45,18 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
int ret; int ret;
struct nfs_net *nn = net_generic(net, nfs_net_id); struct nfs_net *nn = net_generic(net, nfs_net_id);
ret = svc_xprt_create(serv, "tcp", net, PF_INET, ret = svc_create_xprt(serv, "tcp", net, PF_INET,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
cred); cred);
if (ret <= 0) if (ret <= 0)
goto out_err; goto out_err;
nn->nfs_callback_tcpport = ret; nn->nfs_callback_tcpport = ret;
dprintk("NFS: Callback listener port = %u (af %u, net %x)\n", dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
nn->nfs_callback_tcpport, PF_INET, net->ns.inum); nn->nfs_callback_tcpport, PF_INET, net->ns.inum);
ret = svc_xprt_create(serv, "tcp", net, PF_INET6, ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
cred); cred);
if (ret > 0) { if (ret > 0) {
nn->nfs_callback_tcpport6 = ret; nn->nfs_callback_tcpport6 = ret;
dprintk("NFS: Callback listener port = %u (af %u, net %x)\n", dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
@ -80,6 +81,9 @@ nfs4_callback_svc(void *vrqstp)
set_freezable(); set_freezable();
while (!kthread_freezable_should_stop(NULL)) { while (!kthread_freezable_should_stop(NULL)) {
if (signal_pending(current))
flush_signals(current);
/* /*
* Listen for a request on the socket * Listen for a request on the socket
*/ */
@ -88,8 +92,8 @@ nfs4_callback_svc(void *vrqstp)
continue; continue;
svc_process(rqstp); svc_process(rqstp);
} }
svc_exit_thread(rqstp); svc_exit_thread(rqstp);
module_put_and_exit(0);
return 0; return 0;
} }
@ -109,7 +113,11 @@ nfs41_callback_svc(void *vrqstp)
set_freezable(); set_freezable();
while (!kthread_freezable_should_stop(NULL)) { while (!kthread_freezable_should_stop(NULL)) {
prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_IDLE);
if (signal_pending(current))
flush_signals(current);
prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
spin_lock_bh(&serv->sv_cb_lock); spin_lock_bh(&serv->sv_cb_lock);
if (!list_empty(&serv->sv_cb_list)) { if (!list_empty(&serv->sv_cb_list)) {
req = list_first_entry(&serv->sv_cb_list, req = list_first_entry(&serv->sv_cb_list,
@ -124,12 +132,12 @@ nfs41_callback_svc(void *vrqstp)
} else { } else {
spin_unlock_bh(&serv->sv_cb_lock); spin_unlock_bh(&serv->sv_cb_lock);
if (!kthread_should_stop()) if (!kthread_should_stop())
freezable_schedule(); schedule();
finish_wait(&serv->sv_cb_waitq, &wq); finish_wait(&serv->sv_cb_waitq, &wq);
} }
} }
svc_exit_thread(rqstp); svc_exit_thread(rqstp);
module_put_and_exit(0);
return 0; return 0;
} }
@ -161,12 +169,12 @@ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS) if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS)
nrservs = NFS4_MIN_NR_CALLBACK_THREADS; nrservs = NFS4_MIN_NR_CALLBACK_THREADS;
if (serv->sv_nrthreads == nrservs) if (serv->sv_nrthreads-1 == nrservs)
return 0; return 0;
ret = svc_set_num_threads(serv, NULL, nrservs); ret = serv->sv_ops->svo_setup(serv, NULL, nrservs);
if (ret) { if (ret) {
svc_set_num_threads(serv, NULL, 0); serv->sv_ops->svo_setup(serv, NULL, 0);
return ret; return ret;
} }
dprintk("nfs_callback_up: service started\n"); dprintk("nfs_callback_up: service started\n");
@ -181,7 +189,7 @@ static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struc
return; return;
dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum); dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum);
svc_xprt_destroy_all(serv, net); svc_shutdown_net(serv, net);
} }
static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
@ -224,17 +232,59 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
return ret; return ret;
} }
static const struct svc_serv_ops nfs40_cb_sv_ops = {
.svo_function = nfs4_callback_svc,
.svo_enqueue_xprt = svc_xprt_do_enqueue,
.svo_setup = svc_set_num_threads_sync,
.svo_module = THIS_MODULE,
};
#if defined(CONFIG_NFS_V4_1)
static const struct svc_serv_ops nfs41_cb_sv_ops = {
.svo_function = nfs41_callback_svc,
.svo_enqueue_xprt = svc_xprt_do_enqueue,
.svo_setup = svc_set_num_threads_sync,
.svo_module = THIS_MODULE,
};
static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
[0] = &nfs40_cb_sv_ops,
[1] = &nfs41_cb_sv_ops,
};
#else
static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
[0] = &nfs40_cb_sv_ops,
[1] = NULL,
};
#endif
static struct svc_serv *nfs_callback_create_svc(int minorversion) static struct svc_serv *nfs_callback_create_svc(int minorversion)
{ {
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
int (*threadfn)(void *data); const struct svc_serv_ops *sv_ops;
struct svc_serv *serv; struct svc_serv *serv;
/* /*
* Check whether we're already up and running. * Check whether we're already up and running.
*/ */
if (cb_info->serv) if (cb_info->serv) {
return svc_get(cb_info->serv); /*
* Note: increase service usage, because later in case of error
* svc_destroy() will be called.
*/
svc_get(cb_info->serv);
return cb_info->serv;
}
switch (minorversion) {
case 0:
sv_ops = nfs4_cb_sv_ops[0];
break;
default:
sv_ops = nfs4_cb_sv_ops[1];
}
if (sv_ops == NULL)
return ERR_PTR(-ENOTSUPP);
/* /*
* Sanity check: if there's no task, * Sanity check: if there's no task,
@ -244,16 +294,7 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n", printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
cb_info->users); cb_info->users);
threadfn = nfs4_callback_svc; serv = svc_create_pooled(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, sv_ops);
#if defined(CONFIG_NFS_V4_1)
if (minorversion)
threadfn = nfs41_callback_svc;
#else
if (minorversion)
return ERR_PTR(-ENOTSUPP);
#endif
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
threadfn);
if (!serv) { if (!serv) {
printk(KERN_ERR "nfs_callback_create_svc: create service failed\n"); printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -294,10 +335,16 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
goto err_start; goto err_start;
cb_info->users++; cb_info->users++;
/*
* svc_create creates the svc_serv with sv_nrthreads == 1, and then
* svc_prepare_thread increments that. So we need to call svc_destroy
* on both success and failure so that the refcount is 1 when the
* thread exits.
*/
err_net: err_net:
if (!cb_info->users) if (!cb_info->users)
cb_info->serv = NULL; cb_info->serv = NULL;
svc_put(serv); svc_destroy(serv);
err_create: err_create:
mutex_unlock(&nfs_callback_mutex); mutex_unlock(&nfs_callback_mutex);
return ret; return ret;
@ -322,8 +369,8 @@ void nfs_callback_down(int minorversion, struct net *net)
cb_info->users--; cb_info->users--;
if (cb_info->users == 0) { if (cb_info->users == 0) {
svc_get(serv); svc_get(serv);
svc_set_num_threads(serv, NULL, 0); serv->sv_ops->svo_setup(serv, NULL, 0);
svc_put(serv); svc_destroy(serv);
dprintk("nfs_callback_down: service destroyed\n"); dprintk("nfs_callback_down: service destroyed\n");
cb_info->serv = NULL; cb_info->serv = NULL;
} }
@ -382,8 +429,6 @@ check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
*/ */
static int nfs_callback_authenticate(struct svc_rqst *rqstp) static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{ {
rqstp->rq_auth_stat = rpc_autherr_badcred;
switch (rqstp->rq_authop->flavour) { switch (rqstp->rq_authop->flavour) {
case RPC_AUTH_NULL: case RPC_AUTH_NULL:
if (rqstp->rq_proc != CB_NULL) if (rqstp->rq_proc != CB_NULL)
@ -394,8 +439,6 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
if (svc_is_backchannel(rqstp)) if (svc_is_backchannel(rqstp))
return SVC_DENIED; return SVC_DENIED;
} }
rqstp->rq_auth_stat = rpc_auth_ok;
return SVC_OK; return SVC_OK;
} }

View File

@ -63,13 +63,14 @@ static __be32 nfs4_callback_null(struct svc_rqst *rqstp)
return htonl(NFS4_OK); return htonl(NFS4_OK);
} }
/* static int nfs4_decode_void(struct svc_rqst *rqstp, __be32 *p)
* svc_process_common() looks for an XDR encoder to know when
* not to drop a Reply.
*/
static bool nfs4_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
return true; return xdr_argsize_check(rqstp, p);
}
static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p)
{
return xdr_ressize_check(rqstp, p);
} }
static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len, static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len,
@ -983,17 +984,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)
out_invalidcred: out_invalidcred:
pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n"); pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n");
rqstp->rq_auth_stat = rpc_autherr_badcred; return svc_return_autherr(rqstp, rpc_autherr_badcred);
return rpc_success;
}
static int
nfs_callback_dispatch(struct svc_rqst *rqstp, __be32 *statp)
{
const struct svc_procedure *procp = rqstp->rq_procinfo;
*statp = procp->pc_func(rqstp);
return 1;
} }
/* /*
@ -1062,18 +1053,16 @@ static struct callback_op callback_ops[] = {
static const struct svc_procedure nfs4_callback_procedures1[] = { static const struct svc_procedure nfs4_callback_procedures1[] = {
[CB_NULL] = { [CB_NULL] = {
.pc_func = nfs4_callback_null, .pc_func = nfs4_callback_null,
.pc_decode = nfs4_decode_void,
.pc_encode = nfs4_encode_void, .pc_encode = nfs4_encode_void,
.pc_xdrressize = 1, .pc_xdrressize = 1,
.pc_name = "NULL",
}, },
[CB_COMPOUND] = { [CB_COMPOUND] = {
.pc_func = nfs4_callback_compound, .pc_func = nfs4_callback_compound,
.pc_encode = nfs4_encode_void, .pc_encode = nfs4_encode_void,
.pc_argsize = 256, .pc_argsize = 256,
.pc_argzero = 256,
.pc_ressize = 256, .pc_ressize = 256,
.pc_xdrressize = NFS4_CALLBACK_BUFSIZE, .pc_xdrressize = NFS4_CALLBACK_BUFSIZE,
.pc_name = "COMPOUND",
} }
}; };
@ -1084,7 +1073,7 @@ const struct svc_version nfs4_callback_version1 = {
.vs_proc = nfs4_callback_procedures1, .vs_proc = nfs4_callback_procedures1,
.vs_count = nfs4_callback_count1, .vs_count = nfs4_callback_count1,
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE, .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
.vs_dispatch = nfs_callback_dispatch, .vs_dispatch = NULL,
.vs_hidden = true, .vs_hidden = true,
.vs_need_cong_ctrl = true, .vs_need_cong_ctrl = true,
}; };
@ -1096,7 +1085,7 @@ const struct svc_version nfs4_callback_version4 = {
.vs_proc = nfs4_callback_procedures1, .vs_proc = nfs4_callback_procedures1,
.vs_count = nfs4_callback_count4, .vs_count = nfs4_callback_count4,
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE, .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
.vs_dispatch = nfs_callback_dispatch, .vs_dispatch = NULL,
.vs_hidden = true, .vs_hidden = true,
.vs_need_cong_ctrl = true, .vs_need_cong_ctrl = true,
}; };

View File

@ -576,7 +576,7 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
goto out_nopages; goto out_nopages;
xdr_init_decode_pages(&stream, &buf, xdr_pages, buflen); xdr_init_decode_pages(&stream, &buf, xdr_pages, buflen);
xdr_set_scratch_page(&stream, scratch); xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
do { do {
if (entry->label) if (entry->label)

View File

@ -167,25 +167,8 @@ nfs_get_parent(struct dentry *dentry)
return parent; return parent;
} }
static u64 nfs_fetch_iversion(struct inode *inode)
{
struct nfs_server *server = NFS_SERVER(inode);
if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
NFS_INO_REVAL_PAGECACHE))
__nfs_revalidate_inode(server, inode);
return inode_peek_iversion_raw(inode);
}
const struct export_operations nfs_export_ops = { const struct export_operations nfs_export_ops = {
.encode_fh = nfs_encode_fh, .encode_fh = nfs_encode_fh,
.fh_to_dentry = nfs_fh_to_dentry, .fh_to_dentry = nfs_fh_to_dentry,
.get_parent = nfs_get_parent, .get_parent = nfs_get_parent,
.fetch_iversion = nfs_fetch_iversion,
.flags = EXPORT_OP_NOWCC |
EXPORT_OP_NOSUBTREECHK |
EXPORT_OP_CLOSE_BEFORE_UNLINK |
EXPORT_OP_REMOTE_FS |
EXPORT_OP_NOATOMIC_ATTR |
EXPORT_OP_FLUSH_ON_CLOSE,
}; };

View File

@ -798,9 +798,6 @@ int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
nfs_inc_stats(inode, NFSIOS_VFSLOCK); nfs_inc_stats(inode, NFSIOS_VFSLOCK);
if (fl->fl_flags & FL_RECLAIM)
return -ENOGRACE;
/* No mandatory locks over NFS */ /* No mandatory locks over NFS */
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
goto out_err; goto out_err;

View File

@ -293,6 +293,8 @@ static void filelayout_read_call_done(struct rpc_task *task, void *data)
{ {
struct nfs_pgio_header *hdr = data; struct nfs_pgio_header *hdr = data;
dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
if (test_bit(NFS_IOHDR_REDO, &hdr->flags) && if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
task->tk_status == 0) { task->tk_status == 0) {
nfs41_sequence_done(task, &hdr->res.seq_res); nfs41_sequence_done(task, &hdr->res.seq_res);
@ -664,7 +666,7 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
return -ENOMEM; return -ENOMEM;
xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len); xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len);
xdr_set_scratch_page(&stream, scratch); xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
/* 20 = ufl_util (4), first_stripe_index (4), pattern_offset (8), /* 20 = ufl_util (4), first_stripe_index (4), pattern_offset (8),
* num_fh (4) */ * num_fh (4) */

View File

@ -82,7 +82,7 @@ nfs4_fl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
goto out_err; goto out_err;
xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen); xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen);
xdr_set_scratch_page(&stream, scratch); xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
/* Get the stripe count (number of stripe index) */ /* Get the stripe count (number of stripe index) */
p = xdr_inline_decode(&stream, 4); p = xdr_inline_decode(&stream, 4);

View File

@ -378,7 +378,7 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh,
xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages,
lgr->layoutp->len); lgr->layoutp->len);
xdr_set_scratch_page(&stream, scratch); xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
/* stripe unit and mirror_array_cnt */ /* stripe unit and mirror_array_cnt */
rc = -EIO; rc = -EIO;
@ -1419,6 +1419,8 @@ static void ff_layout_read_call_done(struct rpc_task *task, void *data)
{ {
struct nfs_pgio_header *hdr = data; struct nfs_pgio_header *hdr = data;
dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
if (test_bit(NFS_IOHDR_REDO, &hdr->flags) && if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
task->tk_status == 0) { task->tk_status == 0) {
nfs4_sequence_done(task, &hdr->res.seq_res); nfs4_sequence_done(task, &hdr->res.seq_res);

View File

@ -69,7 +69,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
INIT_LIST_HEAD(&dsaddrs); INIT_LIST_HEAD(&dsaddrs);
xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen); xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen);
xdr_set_scratch_page(&stream, scratch); xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
/* multipath count */ /* multipath count */
p = xdr_inline_decode(&stream, 4); p = xdr_inline_decode(&stream, 4);

View File

@ -1536,7 +1536,7 @@ static int nfs4_xdr_dec_listxattrs(struct rpc_rqst *rqstp,
struct compound_hdr hdr; struct compound_hdr hdr;
int status; int status;
xdr_set_scratch_page(xdr, res->scratch); xdr_set_scratch_buffer(xdr, page_address(res->scratch), PAGE_SIZE);
status = decode_compound_hdr(xdr, &hdr); status = decode_compound_hdr(xdr, &hdr);
if (status) if (status)

View File

@ -2757,7 +2757,7 @@ static int nfs4_run_state_manager(void *ptr)
goto again; goto again;
nfs_put_client(clp); nfs_put_client(clp);
module_put_and_kthread_exit(0); module_put_and_exit(0);
return 0; return 0;
} }

View File

@ -6404,8 +6404,10 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
struct compound_hdr hdr; struct compound_hdr hdr;
int status; int status;
if (res->acl_scratch != NULL) if (res->acl_scratch != NULL) {
xdr_set_scratch_page(xdr, res->acl_scratch); void *p = page_address(res->acl_scratch);
xdr_set_scratch_buffer(xdr, p, PAGE_SIZE);
}
status = decode_compound_hdr(xdr, &hdr); status = decode_compound_hdr(xdr, &hdr);
if (status) if (status)
goto out; goto out;

View File

@ -870,6 +870,9 @@ static void nfs_pgio_result(struct rpc_task *task, void *calldata)
struct nfs_pgio_header *hdr = calldata; struct nfs_pgio_header *hdr = calldata;
struct inode *inode = hdr->inode; struct inode *inode = hdr->inode;
dprintk("NFS: %s: %5u, (status %d)\n", __func__,
task->tk_pid, task->tk_status);
if (hdr->rw_ops->rw_done(task, hdr, inode) != 0) if (hdr->rw_ops->rw_done(task, hdr, inode) != 0)
return; return;
if (task->tk_status < 0) if (task->tk_status < 0)

View File

@ -86,11 +86,9 @@ const struct super_operations nfs_sops = {
}; };
EXPORT_SYMBOL_GPL(nfs_sops); EXPORT_SYMBOL_GPL(nfs_sops);
#ifdef CONFIG_NFS_V4_2
static const struct nfs_ssc_client_ops nfs_ssc_clnt_ops_tbl = { static const struct nfs_ssc_client_ops nfs_ssc_clnt_ops_tbl = {
.sco_sb_deactive = nfs_sb_deactive, .sco_sb_deactive = nfs_sb_deactive,
}; };
#endif
#if IS_ENABLED(CONFIG_NFS_V4) #if IS_ENABLED(CONFIG_NFS_V4)
static int __init register_nfs4_fs(void) static int __init register_nfs4_fs(void)
@ -113,7 +111,6 @@ static void unregister_nfs4_fs(void)
} }
#endif #endif
#ifdef CONFIG_NFS_V4_2
static void nfs_ssc_register_ops(void) static void nfs_ssc_register_ops(void)
{ {
nfs_ssc_register(&nfs_ssc_clnt_ops_tbl); nfs_ssc_register(&nfs_ssc_clnt_ops_tbl);
@ -123,7 +120,6 @@ static void nfs_ssc_unregister_ops(void)
{ {
nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl); nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl);
} }
#endif /* CONFIG_NFS_V4_2 */
static struct shrinker acl_shrinker = { static struct shrinker acl_shrinker = {
.count_objects = nfs_access_cache_count, .count_objects = nfs_access_cache_count,
@ -152,9 +148,7 @@ int __init register_nfs_fs(void)
ret = register_shrinker(&acl_shrinker); ret = register_shrinker(&acl_shrinker);
if (ret < 0) if (ret < 0)
goto error_3; goto error_3;
#ifdef CONFIG_NFS_V4_2
nfs_ssc_register_ops(); nfs_ssc_register_ops();
#endif
return 0; return 0;
error_3: error_3:
nfs_unregister_sysctl(); nfs_unregister_sysctl();
@ -174,9 +168,7 @@ void __exit unregister_nfs_fs(void)
unregister_shrinker(&acl_shrinker); unregister_shrinker(&acl_shrinker);
nfs_unregister_sysctl(); nfs_unregister_sysctl();
unregister_nfs4_fs(); unregister_nfs4_fs();
#ifdef CONFIG_NFS_V4_2
nfs_ssc_unregister_ops(); nfs_ssc_unregister_ops();
#endif
unregister_filesystem(&nfs_fs_type); unregister_filesystem(&nfs_fs_type);
} }

View File

@ -1809,6 +1809,9 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
{ {
struct nfs_commit_data *data = calldata; struct nfs_commit_data *data = calldata;
dprintk("NFS: %5u nfs_commit_done (status %d)\n",
task->tk_pid, task->tk_status);
/* Call the NFS version-specific code */ /* Call the NFS version-specific code */
NFS_PROTO(data->inode)->commit_done(task, data); NFS_PROTO(data->inode)->commit_done(task, data);
trace_nfs_commit_done(task, data); trace_nfs_commit_done(task, data);

View File

@ -7,4 +7,4 @@ obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
nfs_acl-objs := nfsacl.o nfs_acl-objs := nfsacl.o
obj-$(CONFIG_GRACE_PERIOD) += grace.o obj-$(CONFIG_GRACE_PERIOD) += grace.o
obj-$(CONFIG_NFS_V4_2_SSC_HELPER) += nfs_ssc.o obj-$(CONFIG_GRACE_PERIOD) += nfs_ssc.o

View File

@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* fs/nfs_common/nfs_ssc_comm.c
*
* Helper for knfsd's SSC to access ops in NFS client modules * Helper for knfsd's SSC to access ops in NFS client modules
* *
* Author: Dai Ngo <dai.ngo@oracle.com> * Author: Dai Ngo <dai.ngo@oracle.com>

View File

@ -136,77 +136,6 @@ int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
} }
EXPORT_SYMBOL_GPL(nfsacl_encode); EXPORT_SYMBOL_GPL(nfsacl_encode);
/**
* nfs_stream_encode_acl - Encode an NFSv3 ACL
*
* @xdr: an xdr_stream positioned to receive an encoded ACL
* @inode: inode of file whose ACL this is
* @acl: posix_acl to encode
* @encode_entries: whether to encode ACEs as well
* @typeflag: ACL type: NFS_ACL_DEFAULT or zero
*
* Return values:
* %false: The ACL could not be encoded
* %true: @xdr is advanced to the next available position
*/
bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
struct posix_acl *acl, int encode_entries,
int typeflag)
{
const size_t elem_size = XDR_UNIT * 3;
u32 entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
struct nfsacl_encode_desc nfsacl_desc = {
.desc = {
.elem_size = elem_size,
.array_len = encode_entries ? entries : 0,
.xcode = xdr_nfsace_encode,
},
.acl = acl,
.typeflag = typeflag,
.uid = inode->i_uid,
.gid = inode->i_gid,
};
struct nfsacl_simple_acl aclbuf;
unsigned int base;
int err;
if (entries > NFS_ACL_MAX_ENTRIES)
return false;
if (xdr_stream_encode_u32(xdr, entries) < 0)
return false;
if (encode_entries && acl && acl->a_count == 3) {
struct posix_acl *acl2 = &aclbuf.acl;
/* Avoid the use of posix_acl_alloc(). nfsacl_encode() is
* invoked in contexts where a memory allocation failure is
* fatal. Fortunately this fake ACL is small enough to
* construct on the stack. */
posix_acl_init(acl2, 4);
/* Insert entries in canonical order: other orders seem
to confuse Solaris VxFS. */
acl2->a_entries[0] = acl->a_entries[0]; /* ACL_USER_OBJ */
acl2->a_entries[1] = acl->a_entries[1]; /* ACL_GROUP_OBJ */
acl2->a_entries[2] = acl->a_entries[1]; /* ACL_MASK */
acl2->a_entries[2].e_tag = ACL_MASK;
acl2->a_entries[3] = acl->a_entries[2]; /* ACL_OTHER */
nfsacl_desc.acl = acl2;
}
base = xdr_stream_pos(xdr);
if (!xdr_reserve_space(xdr, XDR_UNIT +
elem_size * nfsacl_desc.desc.array_len))
return false;
err = xdr_encode_array2(xdr->buf, base, &nfsacl_desc.desc);
if (err)
return false;
return true;
}
EXPORT_SYMBOL_GPL(nfs_stream_encode_acl);
struct nfsacl_decode_desc { struct nfsacl_decode_desc {
struct xdr_array2_desc desc; struct xdr_array2_desc desc;
unsigned int count; unsigned int count;
@ -366,55 +295,3 @@ int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
nfsacl_desc.desc.array_len; nfsacl_desc.desc.array_len;
} }
EXPORT_SYMBOL_GPL(nfsacl_decode); EXPORT_SYMBOL_GPL(nfsacl_decode);
/**
* nfs_stream_decode_acl - Decode an NFSv3 ACL
*
* @xdr: an xdr_stream positioned at an encoded ACL
* @aclcnt: OUT: count of ACEs in decoded posix_acl
* @pacl: OUT: a dynamically-allocated buffer containing the decoded posix_acl
*
* Return values:
* %false: The encoded ACL is not valid
* %true: @pacl contains a decoded ACL, and @xdr is advanced
*
* On a successful return, caller must release *pacl using posix_acl_release().
*/
bool nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
struct posix_acl **pacl)
{
const size_t elem_size = XDR_UNIT * 3;
struct nfsacl_decode_desc nfsacl_desc = {
.desc = {
.elem_size = elem_size,
.xcode = pacl ? xdr_nfsace_decode : NULL,
},
};
unsigned int base;
u32 entries;
if (xdr_stream_decode_u32(xdr, &entries) < 0)
return false;
if (entries > NFS_ACL_MAX_ENTRIES)
return false;
base = xdr_stream_pos(xdr);
if (!xdr_inline_decode(xdr, XDR_UNIT + elem_size * entries))
return false;
nfsacl_desc.desc.array_maxlen = entries;
if (xdr_decode_array2(xdr->buf, base, &nfsacl_desc.desc))
return false;
if (pacl) {
if (entries != nfsacl_desc.desc.array_len ||
posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
posix_acl_release(nfsacl_desc.acl);
return false;
}
*pacl = nfsacl_desc.acl;
}
if (aclcnt)
*aclcnt = entries;
return true;
}
EXPORT_SYMBOL_GPL(nfs_stream_decode_acl);

View File

@ -8,7 +8,6 @@ config NFSD
select SUNRPC select SUNRPC
select EXPORTFS select EXPORTFS
select NFS_ACL_SUPPORT if NFSD_V2_ACL select NFS_ACL_SUPPORT if NFSD_V2_ACL
select NFS_ACL_SUPPORT if NFSD_V3_ACL
depends on MULTIUSER depends on MULTIUSER
help help
Choose Y here if you want to allow other computers to access Choose Y here if you want to allow other computers to access
@ -27,29 +26,28 @@ config NFSD
Below you can choose which versions of the NFS protocol are Below you can choose which versions of the NFS protocol are
available to clients mounting the NFS server on this system. available to clients mounting the NFS server on this system.
Support for NFS version 3 (RFC 1813) is always available when Support for NFS version 2 (RFC 1094) is always available when
CONFIG_NFSD is selected. CONFIG_NFSD is selected.
If unsure, say N. If unsure, say N.
config NFSD_V2
bool "NFS server support for NFS version 2 (DEPRECATED)"
depends on NFSD
default n
help
NFSv2 (RFC 1094) was the first publicly-released version of NFS.
Unless you are hosting ancient (1990's era) NFS clients, you don't
need this.
If unsure, say N.
config NFSD_V2_ACL config NFSD_V2_ACL
bool "NFS server support for the NFSv2 ACL protocol extension" bool
depends on NFSD_V2 depends on NFSD
config NFSD_V3
bool "NFS server support for NFS version 3"
depends on NFSD
help
This option enables support in your system's NFS server for
version 3 of the NFS protocol (RFC 1813).
If unsure, say Y.
config NFSD_V3_ACL config NFSD_V3_ACL
bool "NFS server support for the NFSv3 ACL protocol extension" bool "NFS server support for the NFSv3 ACL protocol extension"
depends on NFSD depends on NFSD_V3
select NFSD_V2_ACL
help help
Solaris NFS servers support an auxiliary NFSv3 ACL protocol that Solaris NFS servers support an auxiliary NFSv3 ACL protocol that
never became an official part of the NFS version 3 protocol. never became an official part of the NFS version 3 protocol.
@ -72,13 +70,13 @@ config NFSD_V3_ACL
config NFSD_V4 config NFSD_V4
bool "NFS server support for NFS version 4" bool "NFS server support for NFS version 4"
depends on NFSD && PROC_FS depends on NFSD && PROC_FS
select NFSD_V3
select FS_POSIX_ACL select FS_POSIX_ACL
select SUNRPC_GSS select SUNRPC_GSS
select CRYPTO select CRYPTO
select CRYPTO_MD5 select CRYPTO_MD5
select CRYPTO_SHA256 select CRYPTO_SHA256
select GRACE_PERIOD select GRACE_PERIOD
select NFS_V4_2_SSC_HELPER if NFS_V4_2
help help
This option enables support in your system's NFS server for This option enables support in your system's NFS server for
version 4 of the NFS protocol (RFC 3530). version 4 of the NFS protocol (RFC 3530).
@ -100,7 +98,7 @@ config NFSD_BLOCKLAYOUT
help help
This option enables support for the exporting pNFS block layouts This option enables support for the exporting pNFS block layouts
in the kernel's NFS server. The pNFS block layout enables NFS in the kernel's NFS server. The pNFS block layout enables NFS
clients to directly perform I/O to block devices accessible to both clients to directly perform I/O to block devices accesible to both
the server and the clients. See RFC 5663 for more details. the server and the clients. See RFC 5663 for more details.
If unsure, say N. If unsure, say N.
@ -114,7 +112,7 @@ config NFSD_SCSILAYOUT
help help
This option enables support for the exporting pNFS SCSI layouts This option enables support for the exporting pNFS SCSI layouts
in the kernel's NFS server. The pNFS SCSI layout enables NFS in the kernel's NFS server. The pNFS SCSI layout enables NFS
clients to directly perform I/O to SCSI devices accessible to both clients to directly perform I/O to SCSI devices accesible to both
the server and the clients. See draft-ietf-nfsv4-scsi-layout for the server and the clients. See draft-ietf-nfsv4-scsi-layout for
more details. more details.
@ -128,7 +126,7 @@ config NFSD_FLEXFILELAYOUT
This option enables support for the exporting pNFS Flex File This option enables support for the exporting pNFS Flex File
layouts in the kernel's NFS server. The pNFS Flex File layout layouts in the kernel's NFS server. The pNFS Flex File layout
enables NFS clients to directly perform I/O to NFSv3 devices enables NFS clients to directly perform I/O to NFSv3 devices
accessible to both the server and the clients. See accesible to both the server and the clients. See
draft-ietf-nfsv4-flex-files for more details. draft-ietf-nfsv4-flex-files for more details.
Warning, this server implements the bare minimum functionality Warning, this server implements the bare minimum functionality
@ -139,7 +137,7 @@ config NFSD_FLEXFILELAYOUT
config NFSD_V4_2_INTER_SSC config NFSD_V4_2_INTER_SSC
bool "NFSv4.2 inter server to server COPY" bool "NFSv4.2 inter server to server COPY"
depends on NFSD_V4 && NFS_V4_2 depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2
help help
This option enables support for NFSv4.2 inter server to This option enables support for NFSv4.2 inter server to
server copy where the destination server calls the NFSv4.2 server copy where the destination server calls the NFSv4.2

View File

@ -10,11 +10,11 @@ obj-$(CONFIG_NFSD) += nfsd.o
# this one should be compiled first, as the tracing macros can easily blow up # this one should be compiled first, as the tracing macros can easily blow up
nfsd-y += trace.o nfsd-y += trace.o
nfsd-y += nfssvc.o nfsctl.o nfsfh.o vfs.o \ nfsd-y += nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o \ export.o auth.o lockd.o nfscache.o nfsxdr.o \
stats.o filecache.o nfs3proc.o nfs3xdr.o stats.o filecache.o
nfsd-$(CONFIG_NFSD_V2) += nfsproc.o nfsxdr.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \ nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
nfs4acl.o nfs4callback.o nfs4recover.o nfs4acl.o nfs4callback.o nfs4recover.o

View File

@ -38,8 +38,6 @@
struct nfs4_acl; struct nfs4_acl;
struct svc_fh; struct svc_fh;
struct svc_rqst; struct svc_rqst;
struct nfsd_attrs;
enum nfs_ftype4;
int nfs4_acl_bytes(int entries); int nfs4_acl_bytes(int entries);
int nfs4_acl_get_whotype(char *, u32); int nfs4_acl_get_whotype(char *, u32);
@ -47,7 +45,7 @@ __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
struct nfs4_acl **acl); struct nfs4_acl **acl);
__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, __be32 nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_attrs *attr); struct nfs4_acl *acl);
#endif /* LINUX_NFS4_ACL_H */ #endif /* LINUX_NFS4_ACL_H */

View File

@ -16,7 +16,6 @@
#include "blocklayoutxdr.h" #include "blocklayoutxdr.h"
#include "pnfs.h" #include "pnfs.h"
#include "filecache.h" #include "filecache.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS #define NFSDDBG_FACILITY NFSDDBG_PNFS

View File

@ -9,7 +9,6 @@
#include "nfsd.h" #include "nfsd.h"
#include "blocklayoutxdr.h" #include "blocklayoutxdr.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS #define NFSDDBG_FACILITY NFSDDBG_PNFS

View File

@ -84,6 +84,6 @@ int nfsd_reply_cache_init(struct nfsd_net *);
void nfsd_reply_cache_shutdown(struct nfsd_net *); void nfsd_reply_cache_shutdown(struct nfsd_net *);
int nfsd_cache_lookup(struct svc_rqst *); int nfsd_cache_lookup(struct svc_rqst *);
void nfsd_cache_update(struct svc_rqst *, int, __be32 *); void nfsd_cache_update(struct svc_rqst *, int, __be32 *);
int nfsd_reply_cache_stats_show(struct seq_file *m, void *v); int nfsd_reply_cache_stats_open(struct inode *, struct file *);
#endif /* NFSCACHE_H */ #endif /* NFSCACHE_H */

View File

@ -331,29 +331,12 @@ static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc)
fsloc->locations = NULL; fsloc->locations = NULL;
} }
static int export_stats_init(struct export_stats *stats)
{
stats->start_time = ktime_get_seconds();
return nfsd_percpu_counters_init(stats->counter, EXP_STATS_COUNTERS_NUM);
}
static void export_stats_reset(struct export_stats *stats)
{
nfsd_percpu_counters_reset(stats->counter, EXP_STATS_COUNTERS_NUM);
}
static void export_stats_destroy(struct export_stats *stats)
{
nfsd_percpu_counters_destroy(stats->counter, EXP_STATS_COUNTERS_NUM);
}
static void svc_export_put(struct kref *ref) 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);
nfsd4_fslocs_free(&exp->ex_fslocs); nfsd4_fslocs_free(&exp->ex_fslocs);
export_stats_destroy(&exp->ex_stats);
kfree(exp->ex_uuid); kfree(exp->ex_uuid);
kfree_rcu(exp, ex_rcu); kfree_rcu(exp, ex_rcu);
} }
@ -425,12 +408,6 @@ static int check_export(struct inode *inode, int *flags, unsigned char *uuid)
return -EINVAL; return -EINVAL;
} }
if (inode->i_sb->s_export_op->flags & EXPORT_OP_NOSUBTREECHK &&
!(*flags & NFSEXP_NOSUBTREECHECK)) {
dprintk("%s: %s does not support subtree checking!\n",
__func__, inode->i_sb->s_type->name);
return -EINVAL;
}
return 0; return 0;
} }
@ -709,47 +686,22 @@ static void exp_flags(struct seq_file *m, int flag, int fsid,
kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fslocs); kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fslocs);
static void show_secinfo(struct seq_file *m, struct svc_export *exp); static void show_secinfo(struct seq_file *m, struct svc_export *exp);
static int is_export_stats_file(struct seq_file *m)
{
/*
* The export_stats file uses the same ops as the exports file.
* We use the file's name to determine the reported info per export.
* There is no rename in nsfdfs, so d_name.name is stable.
*/
return !strcmp(m->file->f_path.dentry->d_name.name, "export_stats");
}
static int svc_export_show(struct seq_file *m, static int svc_export_show(struct seq_file *m,
struct cache_detail *cd, struct cache_detail *cd,
struct cache_head *h) struct cache_head *h)
{ {
struct svc_export *exp; struct svc_export *exp ;
bool export_stats = is_export_stats_file(m);
if (h == NULL) { if (h ==NULL) {
if (export_stats) seq_puts(m, "#path domain(flags)\n");
seq_puts(m, "#path domain start-time\n#\tstats\n");
else
seq_puts(m, "#path domain(flags)\n");
return 0; return 0;
} }
exp = container_of(h, struct svc_export, h); exp = container_of(h, struct svc_export, h);
seq_path(m, &exp->ex_path, " \t\n\\"); seq_path(m, &exp->ex_path, " \t\n\\");
seq_putc(m, '\t'); seq_putc(m, '\t');
seq_escape(m, exp->ex_client->name, " \t\n\\"); seq_escape(m, exp->ex_client->name, " \t\n\\");
if (export_stats) {
seq_printf(m, "\t%lld\n", exp->ex_stats.start_time);
seq_printf(m, "\tfh_stale: %lld\n",
percpu_counter_sum_positive(&exp->ex_stats.counter[EXP_STATS_FH_STALE]));
seq_printf(m, "\tio_read: %lld\n",
percpu_counter_sum_positive(&exp->ex_stats.counter[EXP_STATS_IO_READ]));
seq_printf(m, "\tio_write: %lld\n",
percpu_counter_sum_positive(&exp->ex_stats.counter[EXP_STATS_IO_WRITE]));
seq_putc(m, '\n');
return 0;
}
seq_putc(m, '('); seq_putc(m, '(');
if (test_bit(CACHE_VALID, &h->flags) && if (test_bit(CACHE_VALID, &h->flags) &&
!test_bit(CACHE_NEGATIVE, &h->flags)) { !test_bit(CACHE_NEGATIVE, &h->flags)) {
exp_flags(m, exp->ex_flags, exp->ex_fsid, exp_flags(m, exp->ex_flags, exp->ex_fsid,
exp->ex_anon_uid, exp->ex_anon_gid, &exp->ex_fslocs); exp->ex_anon_uid, exp->ex_anon_gid, &exp->ex_fslocs);
@ -790,7 +742,6 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem)
new->ex_layout_types = 0; new->ex_layout_types = 0;
new->ex_uuid = NULL; new->ex_uuid = NULL;
new->cd = item->cd; new->cd = item->cd;
export_stats_reset(&new->ex_stats);
} }
static void export_update(struct cache_head *cnew, struct cache_head *citem) static void export_update(struct cache_head *cnew, struct cache_head *citem)
@ -823,15 +774,10 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem)
static struct cache_head *svc_export_alloc(void) static struct cache_head *svc_export_alloc(void)
{ {
struct svc_export *i = kmalloc(sizeof(*i), GFP_KERNEL); struct svc_export *i = kmalloc(sizeof(*i), GFP_KERNEL);
if (!i) if (i)
return &i->h;
else
return NULL; return NULL;
if (export_stats_init(&i->ex_stats)) {
kfree(i);
return NULL;
}
return &i->h;
} }
static const struct cache_detail svc_export_cache_template = { static const struct cache_detail svc_export_cache_template = {
@ -1293,14 +1239,10 @@ static int e_show(struct seq_file *m, void *p)
struct cache_head *cp = p; struct cache_head *cp = p;
struct svc_export *exp = container_of(cp, struct svc_export, h); struct svc_export *exp = container_of(cp, struct svc_export, h);
struct cache_detail *cd = m->private; struct cache_detail *cd = m->private;
bool export_stats = is_export_stats_file(m);
if (p == SEQ_START_TOKEN) { if (p == SEQ_START_TOKEN) {
seq_puts(m, "# Version 1.1\n"); seq_puts(m, "# Version 1.1\n");
if (export_stats) seq_puts(m, "# Path Client(Flags) # IPs\n");
seq_puts(m, "# Path Client Start-time\n#\tStats\n");
else
seq_puts(m, "# Path Client(Flags) # IPs\n");
return 0; return 0;
} }

View File

@ -6,7 +6,6 @@
#define NFSD_EXPORT_H #define NFSD_EXPORT_H
#include <linux/sunrpc/cache.h> #include <linux/sunrpc/cache.h>
#include <linux/percpu_counter.h>
#include <uapi/linux/nfsd/export.h> #include <uapi/linux/nfsd/export.h>
#include <linux/nfs4.h> #include <linux/nfs4.h>
@ -47,19 +46,6 @@ struct exp_flavor_info {
u32 flags; u32 flags;
}; };
/* Per-export stats */
enum {
EXP_STATS_FH_STALE,
EXP_STATS_IO_READ,
EXP_STATS_IO_WRITE,
EXP_STATS_COUNTERS_NUM
};
struct export_stats {
time64_t start_time;
struct percpu_counter counter[EXP_STATS_COUNTERS_NUM];
};
struct svc_export { struct svc_export {
struct cache_head h; struct cache_head h;
struct auth_domain * ex_client; struct auth_domain * ex_client;
@ -76,7 +62,6 @@ struct svc_export {
struct nfsd4_deviceid_map *ex_devid_map; struct nfsd4_deviceid_map *ex_devid_map;
struct cache_detail *cd; struct cache_detail *cd;
struct rcu_head ex_rcu; struct rcu_head ex_rcu;
struct export_stats ex_stats;
}; };
/* an "export key" (expkey) maps a filehandlefragement to an /* an "export key" (expkey) maps a filehandlefragement to an
@ -115,6 +100,7 @@ struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *);
int exp_rootfh(struct net *, struct auth_domain *, int exp_rootfh(struct net *, struct auth_domain *,
char *path, struct knfsd_fh *, int maxsize); char *path, struct knfsd_fh *, int maxsize);
__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *); __be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *);
__be32 nfserrno(int errno);
static inline void exp_put(struct svc_export *exp) static inline void exp_put(struct svc_export *exp)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -29,23 +29,23 @@ struct nfsd_file_mark {
* never be dereferenced, only used for comparison. * never be dereferenced, only used for comparison.
*/ */
struct nfsd_file { struct nfsd_file {
struct rhlist_head nf_rlist; struct hlist_node nf_node;
void *nf_inode; struct list_head nf_lru;
struct rcu_head nf_rcu;
struct file *nf_file; struct file *nf_file;
const struct cred *nf_cred; const struct cred *nf_cred;
struct net *nf_net; struct net *nf_net;
#define NFSD_FILE_HASHED (0) #define NFSD_FILE_HASHED (0)
#define NFSD_FILE_PENDING (1) #define NFSD_FILE_PENDING (1)
#define NFSD_FILE_REFERENCED (2) #define NFSD_FILE_BREAK_READ (2)
#define NFSD_FILE_GC (3) #define NFSD_FILE_BREAK_WRITE (3)
#define NFSD_FILE_REFERENCED (4)
unsigned long nf_flags; unsigned long nf_flags;
struct inode *nf_inode;
unsigned int nf_hashval;
refcount_t nf_ref; refcount_t nf_ref;
unsigned char nf_may; unsigned char nf_may;
struct nfsd_file_mark *nf_mark; struct nfsd_file_mark *nf_mark;
struct list_head nf_lru;
struct rcu_head nf_rcu;
ktime_t nf_birthtime;
}; };
int nfsd_file_cache_init(void); int nfsd_file_cache_init(void);
@ -57,12 +57,7 @@ void nfsd_file_put(struct nfsd_file *nf);
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
void nfsd_file_close_inode_sync(struct inode *inode); void nfsd_file_close_inode_sync(struct inode *inode);
bool nfsd_file_is_cached(struct inode *inode); bool nfsd_file_is_cached(struct inode *inode);
__be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **nfp);
__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **nfp); unsigned int may_flags, struct nfsd_file **nfp);
__be32 nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp, int nfsd_file_cache_stats_open(struct inode *, struct file *);
unsigned int may_flags, struct file *file,
struct nfsd_file **nfp);
int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
#endif /* _FS_NFSD_FILECACHE_H */ #endif /* _FS_NFSD_FILECACHE_H */

View File

@ -15,7 +15,6 @@
#include "flexfilelayoutxdr.h" #include "flexfilelayoutxdr.h"
#include "pnfs.h" #include "pnfs.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS #define NFSDDBG_FACILITY NFSDDBG_PNFS
@ -62,7 +61,7 @@ nfsd4_ff_proc_layoutget(struct inode *inode, const struct svc_fh *fhp,
goto out_error; goto out_error;
fl->fh.size = fhp->fh_handle.fh_size; fl->fh.size = fhp->fh_handle.fh_size;
memcpy(fl->fh.data, &fhp->fh_handle.fh_raw, fl->fh.size); memcpy(fl->fh.data, &fhp->fh_handle.fh_base, fl->fh.size);
/* Give whole file layout segments */ /* Give whole file layout segments */
seg->offset = 0; seg->offset = 0;

View File

@ -25,22 +25,18 @@
* Note: we hold the dentry use count while the file is open. * Note: we hold the dentry use count while the file is open.
*/ */
static __be32 static __be32
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp, nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
int mode)
{ {
__be32 nfserr; __be32 nfserr;
int access;
struct svc_fh fh; struct svc_fh fh;
/* must initialize before using! but maxsize doesn't matter */ /* must initialize before using! but maxsize doesn't matter */
fh_init(&fh,0); fh_init(&fh,0);
fh.fh_handle.fh_size = f->size; fh.fh_handle.fh_size = f->size;
memcpy(&fh.fh_handle.fh_raw, f->data, f->size); memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
fh.fh_export = NULL; fh.fh_export = NULL;
access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ; nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp);
access |= NFSD_MAY_LOCK;
nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
fh_put(&fh); fh_put(&fh);
/* We return nlm error codes as nlm doesn't know /* We return nlm error codes as nlm doesn't know
* about nfsd, but nfsd does know about nlm.. * about nfsd, but nfsd does know about nlm..

View File

@ -10,8 +10,6 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
#include <linux/percpu_counter.h>
#include <linux/siphash.h>
/* Hash tables for nfs4_clientid state */ /* Hash tables for nfs4_clientid state */
#define CLIENT_HASH_BITS 4 #define CLIENT_HASH_BITS 4
@ -23,14 +21,6 @@
struct cld_net; struct cld_net;
struct nfsd4_client_tracking_ops; struct nfsd4_client_tracking_ops;
enum {
/* cache misses due only to checksum comparison failures */
NFSD_NET_PAYLOAD_MISSES,
/* amount of memory (in bytes) currently consumed by the DRC */
NFSD_NET_DRC_MEM_USAGE,
NFSD_NET_COUNTERS_NUM
};
/* /*
* Represents a nfsd "container". With respect to nfsv4 state tracking, the * Represents a nfsd "container". With respect to nfsv4 state tracking, the
* fields of interest are the *_id_hashtbls and the *_name_tree. These track * fields of interest are the *_id_hashtbls and the *_name_tree. These track
@ -109,8 +99,9 @@ struct nfsd_net {
bool nfsd_net_up; bool nfsd_net_up;
bool lockd_up; bool lockd_up;
seqlock_t writeverf_lock; /* Time of server startup */
unsigned char writeverf[8]; struct timespec64 nfssvc_boot;
seqlock_t boot_lock;
/* /*
* Max number of connections this nfsd container will allow. Defaults * Max number of connections this nfsd container will allow. Defaults
@ -123,13 +114,12 @@ struct nfsd_net {
u32 clverifier_counter; u32 clverifier_counter;
struct svc_serv *nfsd_serv; struct svc_serv *nfsd_serv;
/* When a listening socket is added to nfsd, keep_active is set
* and this justifies a reference on nfsd_serv. This stops wait_queue_head_t ntf_wq;
* nfsd_serv from being freed. When the number of threads is atomic_t ntf_refcnt;
* set, keep_active is cleared and the reference is dropped. So
* when the last thread exits, the service will be destroyed. /* Allow umount to wait for nfsd state cleanup */
*/ struct completion nfsd_shutdown_complete;
int keep_active;
/* /*
* clientid and stateid data for construction of net unique COPY * clientid and stateid data for construction of net unique COPY
@ -159,16 +149,20 @@ struct nfsd_net {
/* /*
* Stats and other tracking of on the duplicate reply cache. * Stats and other tracking of on the duplicate reply cache.
* The longest_chain* fields are modified with only the per-bucket * These fields and the "rc" fields in nfsdstats are modified
* cache lock, which isn't really safe and should be fixed if we want * with only the per-bucket cache lock, which isn't really safe
* these statistics to be completely accurate. * and should be fixed if we want the statistics to be
* completely accurate.
*/ */
/* total number of entries */ /* total number of entries */
atomic_t num_drc_entries; atomic_t num_drc_entries;
/* Per-netns stats counters */ /* cache misses due only to checksum comparison failures */
struct percpu_counter counter[NFSD_NET_COUNTERS_NUM]; unsigned int payload_misses;
/* amount of memory (in bytes) currently consumed by the DRC */
unsigned int drc_mem_usage;
/* longest hash chain seen */ /* longest hash chain seen */
unsigned int longest_chain; unsigned int longest_chain;
@ -177,25 +171,8 @@ struct nfsd_net {
unsigned int longest_chain_cachesize; unsigned int longest_chain_cachesize;
struct shrinker nfsd_reply_cache_shrinker; struct shrinker nfsd_reply_cache_shrinker;
/* tracking server-to-server copy mounts */
spinlock_t nfsd_ssc_lock;
struct list_head nfsd_ssc_mount_list;
wait_queue_head_t nfsd_ssc_waitq;
/* utsname taken from the process that starts the server */ /* utsname taken from the process that starts the server */
char nfsd_name[UNX_MAXNODENAME+1]; char nfsd_name[UNX_MAXNODENAME+1];
struct nfsd_fcache_disposal *fcache_disposal;
siphash_key_t siphash_key;
atomic_t nfs4_client_count;
int nfs4_max_clients;
atomic_t nfsd_courtesy_clients;
struct shrinker nfsd_client_shrinker;
struct work_struct nfsd_shrinker_work;
}; };
/* Simple check to find out if a given net was properly initialized */ /* Simple check to find out if a given net was properly initialized */
@ -205,6 +182,6 @@ extern void nfsd_netns_free_versions(struct nfsd_net *nn);
extern unsigned int nfsd_net_id; extern unsigned int nfsd_net_id;
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn); void nfsd_copy_boot_verifier(__be32 verf[2], struct nfsd_net *nn);
void nfsd_reset_write_verifier(struct nfsd_net *nn); void nfsd_reset_boot_verifier(struct nfsd_net *nn);
#endif /* __NFSD_NETNS_H__ */ #endif /* __NFSD_NETNS_H__ */

View File

@ -111,7 +111,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
if (error) if (error)
goto out_errno; goto out_errno;
inode_lock(inode); fh_lock(fh);
error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access); error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
if (error) if (error)
@ -120,7 +120,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
if (error) if (error)
goto out_drop_lock; goto out_drop_lock;
inode_unlock(inode); fh_unlock(fh);
fh_drop_write(fh); fh_drop_write(fh);
@ -134,7 +134,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
return rpc_success; return rpc_success;
out_drop_lock: out_drop_lock:
inode_unlock(inode); fh_unlock(fh);
fh_drop_write(fh); fh_drop_write(fh);
out_errno: out_errno:
resp->status = nfserrno(error); resp->status = nfserrno(error);
@ -185,106 +185,161 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp)
/* /*
* XDR decode functions * XDR decode functions
*/ */
static int nfsaclsvc_decode_voidarg(struct svc_rqst *rqstp, __be32 *p)
{
return 1;
}
static bool static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p)
nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_getaclargs *argp = rqstp->rq_argp; struct nfsd3_getaclargs *argp = rqstp->rq_argp;
if (!svcxdr_decode_fhandle(xdr, &argp->fh)) p = nfs2svc_decode_fh(p, &argp->fh);
return false; if (!p)
if (xdr_stream_decode_u32(xdr, &argp->mask) < 0) return 0;
return false; argp->mask = ntohl(*p); p++;
return true; return xdr_argsize_check(rqstp, p);
} }
static bool
nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nfsd3_setaclargs *argp = rqstp->rq_argp; struct nfsd3_setaclargs *argp = rqstp->rq_argp;
struct kvec *head = rqstp->rq_arg.head;
unsigned int base;
int n;
if (!svcxdr_decode_fhandle(xdr, &argp->fh)) p = nfs2svc_decode_fh(p, &argp->fh);
return false; if (!p)
if (xdr_stream_decode_u32(xdr, &argp->mask) < 0) return 0;
return false; argp->mask = ntohl(*p++);
if (argp->mask & ~NFS_ACL_MASK) if (argp->mask & ~NFS_ACL_MASK ||
return false; !xdr_argsize_check(rqstp, p))
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_ACL) ? return 0;
&argp->acl_access : NULL))
return false;
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_DFACL) ?
&argp->acl_default : NULL))
return false;
return true; base = (char *)p - (char *)head->iov_base;
n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
(argp->mask & NFS_ACL) ?
&argp->acl_access : NULL);
if (n > 0)
n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
(argp->mask & NFS_DFACL) ?
&argp->acl_default : NULL);
return (n > 0);
} }
static bool static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_accessargs *args = rqstp->rq_argp; struct nfsd_fhandle *argp = rqstp->rq_argp;
if (!svcxdr_decode_fhandle(xdr, &args->fh)) p = nfs2svc_decode_fh(p, &argp->fh);
return false; if (!p)
if (xdr_stream_decode_u32(xdr, &args->access) < 0) return 0;
return false; return xdr_argsize_check(rqstp, p);
}
return true; static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p)
{
struct nfsd3_accessargs *argp = rqstp->rq_argp;
p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0;
argp->access = ntohl(*p++);
return xdr_argsize_check(rqstp, p);
} }
/* /*
* XDR encode functions * XDR encode functions
*/ */
/*
* There must be an encoding function for void results so svc_process
* will work properly.
*/
static int nfsaclsvc_encode_voidres(struct svc_rqst *rqstp, __be32 *p)
{
return xdr_ressize_check(rqstp, p);
}
/* GETACL */ /* GETACL */
static bool static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_getaclres *resp = rqstp->rq_resp; struct nfsd3_getaclres *resp = rqstp->rq_resp;
struct dentry *dentry = resp->fh.fh_dentry; struct dentry *dentry = resp->fh.fh_dentry;
struct inode *inode; struct inode *inode;
struct kvec *head = rqstp->rq_res.head;
unsigned int base;
int n;
int w;
if (!svcxdr_encode_stat(xdr, resp->status)) *p++ = resp->status;
return false; if (resp->status != nfs_ok)
return xdr_ressize_check(rqstp, p);
/*
* Since this is version 2, the check for nfserr in
* nfsd_dispatch actually ensures the following cannot happen.
* However, it seems fragile to depend on that.
*/
if (dentry == NULL || d_really_is_negative(dentry)) if (dentry == NULL || d_really_is_negative(dentry))
return true; return 0;
inode = d_inode(dentry); inode = d_inode(dentry);
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat)) p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
return false; *p++ = htonl(resp->mask);
if (xdr_stream_encode_u32(xdr, resp->mask) < 0) if (!xdr_ressize_check(rqstp, p))
return false; return 0;
base = (char *)p - (char *)head->iov_base;
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access, rqstp->rq_res.page_len = w = nfsacl_size(
resp->mask & NFS_ACL, 0)) (resp->mask & NFS_ACL) ? resp->acl_access : NULL,
return false; (resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default, while (w > 0) {
resp->mask & NFS_DFACL, NFS_ACL_DEFAULT)) if (!*(rqstp->rq_next_page++))
return false; return 0;
w -= PAGE_SIZE;
}
return true; n = nfsacl_encode(&rqstp->rq_res, base, inode,
resp->acl_access,
resp->mask & NFS_ACL, 0);
if (n > 0)
n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
resp->acl_default,
resp->mask & NFS_DFACL,
NFS_ACL_DEFAULT);
return (n > 0);
}
static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p)
{
struct nfsd_attrstat *resp = rqstp->rq_resp;
*p++ = resp->status;
if (resp->status != nfs_ok)
goto out;
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
out:
return xdr_ressize_check(rqstp, p);
} }
/* ACCESS */ /* ACCESS */
static bool static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, __be32 *p)
nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_accessres *resp = rqstp->rq_resp; struct nfsd3_accessres *resp = rqstp->rq_resp;
if (!svcxdr_encode_stat(xdr, resp->status)) *p++ = resp->status;
return false; if (resp->status != nfs_ok)
switch (resp->status) { goto out;
case nfs_ok:
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
return false;
if (xdr_stream_encode_u32(xdr, resp->access) < 0)
return false;
break;
}
return true; p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
*p++ = htonl(resp->access);
out:
return xdr_ressize_check(rqstp, p);
} }
/* /*
@ -299,6 +354,13 @@ static void nfsaclsvc_release_getacl(struct svc_rqst *rqstp)
posix_acl_release(resp->acl_default); posix_acl_release(resp->acl_default);
} }
static void nfsaclsvc_release_attrstat(struct svc_rqst *rqstp)
{
struct nfsd_attrstat *resp = rqstp->rq_resp;
fh_put(&resp->fh);
}
static void nfsaclsvc_release_access(struct svc_rqst *rqstp) static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
{ {
struct nfsd3_accessres *resp = rqstp->rq_resp; struct nfsd3_accessres *resp = rqstp->rq_resp;
@ -316,14 +378,12 @@ struct nfsd3_voidargs { int dummy; };
static const struct svc_procedure nfsd_acl_procedures2[5] = { static const struct svc_procedure nfsd_acl_procedures2[5] = {
[ACLPROC2_NULL] = { [ACLPROC2_NULL] = {
.pc_func = nfsacld_proc_null, .pc_func = nfsacld_proc_null,
.pc_decode = nfssvc_decode_voidarg, .pc_decode = nfsaclsvc_decode_voidarg,
.pc_encode = nfssvc_encode_voidres, .pc_encode = nfsaclsvc_encode_voidres,
.pc_argsize = sizeof(struct nfsd_voidargs), .pc_argsize = sizeof(struct nfsd3_voidargs),
.pc_argzero = sizeof(struct nfsd_voidargs), .pc_ressize = sizeof(struct nfsd3_voidargs),
.pc_ressize = sizeof(struct nfsd_voidres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "NULL",
}, },
[ACLPROC2_GETACL] = { [ACLPROC2_GETACL] = {
.pc_func = nfsacld_proc_getacl, .pc_func = nfsacld_proc_getacl,
@ -331,35 +391,29 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
.pc_encode = nfsaclsvc_encode_getaclres, .pc_encode = nfsaclsvc_encode_getaclres,
.pc_release = nfsaclsvc_release_getacl, .pc_release = nfsaclsvc_release_getacl,
.pc_argsize = sizeof(struct nfsd3_getaclargs), .pc_argsize = sizeof(struct nfsd3_getaclargs),
.pc_argzero = sizeof(struct nfsd3_getaclargs),
.pc_ressize = sizeof(struct nfsd3_getaclres), .pc_ressize = sizeof(struct nfsd3_getaclres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+1+2*(1+ACL), .pc_xdrressize = ST+1+2*(1+ACL),
.pc_name = "GETACL",
}, },
[ACLPROC2_SETACL] = { [ACLPROC2_SETACL] = {
.pc_func = nfsacld_proc_setacl, .pc_func = nfsacld_proc_setacl,
.pc_decode = nfsaclsvc_decode_setaclargs, .pc_decode = nfsaclsvc_decode_setaclargs,
.pc_encode = nfssvc_encode_attrstatres, .pc_encode = nfsaclsvc_encode_attrstatres,
.pc_release = nfssvc_release_attrstat, .pc_release = nfsaclsvc_release_attrstat,
.pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argsize = sizeof(struct nfsd3_setaclargs),
.pc_argzero = sizeof(struct nfsd3_setaclargs),
.pc_ressize = sizeof(struct nfsd_attrstat), .pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT, .pc_xdrressize = ST+AT,
.pc_name = "SETACL",
}, },
[ACLPROC2_GETATTR] = { [ACLPROC2_GETATTR] = {
.pc_func = nfsacld_proc_getattr, .pc_func = nfsacld_proc_getattr,
.pc_decode = nfssvc_decode_fhandleargs, .pc_decode = nfsaclsvc_decode_fhandleargs,
.pc_encode = nfssvc_encode_attrstatres, .pc_encode = nfsaclsvc_encode_attrstatres,
.pc_release = nfssvc_release_attrstat, .pc_release = nfsaclsvc_release_attrstat,
.pc_argsize = sizeof(struct nfsd_fhandle), .pc_argsize = sizeof(struct nfsd_fhandle),
.pc_argzero = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd_attrstat), .pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT, .pc_xdrressize = ST+AT,
.pc_name = "GETATTR",
}, },
[ACLPROC2_ACCESS] = { [ACLPROC2_ACCESS] = {
.pc_func = nfsacld_proc_access, .pc_func = nfsacld_proc_access,
@ -367,11 +421,9 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
.pc_encode = nfsaclsvc_encode_accessres, .pc_encode = nfsaclsvc_encode_accessres,
.pc_release = nfsaclsvc_release_access, .pc_release = nfsaclsvc_release_access,
.pc_argsize = sizeof(struct nfsd3_accessargs), .pc_argsize = sizeof(struct nfsd3_accessargs),
.pc_argzero = sizeof(struct nfsd3_accessargs),
.pc_ressize = sizeof(struct nfsd3_accessres), .pc_ressize = sizeof(struct nfsd3_accessres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT+1, .pc_xdrressize = ST+AT+1,
.pc_name = "SETATTR",
}, },
}; };

View File

@ -101,7 +101,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
if (error) if (error)
goto out_errno; goto out_errno;
inode_lock(inode); fh_lock(fh);
error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access); error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
if (error) if (error)
@ -109,7 +109,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default); error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default);
out_drop_lock: out_drop_lock:
inode_unlock(inode); fh_unlock(fh);
fh_drop_write(fh); fh_drop_write(fh);
out_errno: out_errno:
resp->status = nfserrno(error); resp->status = nfserrno(error);
@ -124,39 +124,43 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
/* /*
* XDR decode functions * XDR decode functions
*/ */
static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p)
static bool
nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_getaclargs *args = rqstp->rq_argp; struct nfsd3_getaclargs *args = rqstp->rq_argp;
if (!svcxdr_decode_nfs_fh3(xdr, &args->fh)) p = nfs3svc_decode_fh(p, &args->fh);
return false; if (!p)
if (xdr_stream_decode_u32(xdr, &args->mask) < 0) return 0;
return false; args->mask = ntohl(*p); p++;
return true; return xdr_argsize_check(rqstp, p);
} }
static bool
nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
{ {
struct nfsd3_setaclargs *argp = rqstp->rq_argp; struct nfsd3_setaclargs *args = rqstp->rq_argp;
struct kvec *head = rqstp->rq_arg.head;
unsigned int base;
int n;
if (!svcxdr_decode_nfs_fh3(xdr, &argp->fh)) p = nfs3svc_decode_fh(p, &args->fh);
return false; if (!p)
if (xdr_stream_decode_u32(xdr, &argp->mask) < 0) return 0;
return false; args->mask = ntohl(*p++);
if (argp->mask & ~NFS_ACL_MASK) if (args->mask & ~NFS_ACL_MASK ||
return false; !xdr_argsize_check(rqstp, p))
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_ACL) ? return 0;
&argp->acl_access : NULL))
return false;
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_DFACL) ?
&argp->acl_default : NULL))
return false;
return true; base = (char *)p - (char *)head->iov_base;
n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
(args->mask & NFS_ACL) ?
&args->acl_access : NULL);
if (n > 0)
n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
(args->mask & NFS_DFACL) ?
&args->acl_default : NULL);
return (n > 0);
} }
/* /*
@ -164,47 +168,59 @@ nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
*/ */
/* GETACL */ /* GETACL */
static bool static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
nfs3svc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_getaclres *resp = rqstp->rq_resp; struct nfsd3_getaclres *resp = rqstp->rq_resp;
struct dentry *dentry = resp->fh.fh_dentry; struct dentry *dentry = resp->fh.fh_dentry;
struct inode *inode;
if (!svcxdr_encode_nfsstat3(xdr, resp->status)) *p++ = resp->status;
return false; p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
switch (resp->status) { if (resp->status == 0 && dentry && d_really_is_positive(dentry)) {
case nfs_ok: struct inode *inode = d_inode(dentry);
inode = d_inode(dentry); struct kvec *head = rqstp->rq_res.head;
if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh)) unsigned int base;
return false; int n;
if (xdr_stream_encode_u32(xdr, resp->mask) < 0) int w;
return false;
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access, *p++ = htonl(resp->mask);
resp->mask & NFS_ACL, 0)) if (!xdr_ressize_check(rqstp, p))
return false; return 0;
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default, base = (char *)p - (char *)head->iov_base;
resp->mask & NFS_DFACL,
NFS_ACL_DEFAULT))
return false;
break;
default:
if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
return false;
}
return true; rqstp->rq_res.page_len = w = nfsacl_size(
(resp->mask & NFS_ACL) ? resp->acl_access : NULL,
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
while (w > 0) {
if (!*(rqstp->rq_next_page++))
return 0;
w -= PAGE_SIZE;
}
n = nfsacl_encode(&rqstp->rq_res, base, inode,
resp->acl_access,
resp->mask & NFS_ACL, 0);
if (n > 0)
n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
resp->acl_default,
resp->mask & NFS_DFACL,
NFS_ACL_DEFAULT);
if (n <= 0)
return 0;
} else
if (!xdr_ressize_check(rqstp, p))
return 0;
return 1;
} }
/* SETACL */ /* SETACL */
static bool static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, __be32 *p)
nfs3svc_encode_setaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_attrstat *resp = rqstp->rq_resp; struct nfsd3_attrstat *resp = rqstp->rq_resp;
return svcxdr_encode_nfsstat3(xdr, resp->status) && *p++ = resp->status;
svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh); p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
return xdr_ressize_check(rqstp, p);
} }
/* /*
@ -229,14 +245,12 @@ struct nfsd3_voidargs { int dummy; };
static const struct svc_procedure nfsd_acl_procedures3[3] = { static const struct svc_procedure nfsd_acl_procedures3[3] = {
[ACLPROC3_NULL] = { [ACLPROC3_NULL] = {
.pc_func = nfsd3_proc_null, .pc_func = nfsd3_proc_null,
.pc_decode = nfssvc_decode_voidarg, .pc_decode = nfs3svc_decode_voidarg,
.pc_encode = nfssvc_encode_voidres, .pc_encode = nfs3svc_encode_voidres,
.pc_argsize = sizeof(struct nfsd_voidargs), .pc_argsize = sizeof(struct nfsd3_voidargs),
.pc_argzero = sizeof(struct nfsd_voidargs), .pc_ressize = sizeof(struct nfsd3_voidargs),
.pc_ressize = sizeof(struct nfsd_voidres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "NULL",
}, },
[ACLPROC3_GETACL] = { [ACLPROC3_GETACL] = {
.pc_func = nfsd3_proc_getacl, .pc_func = nfsd3_proc_getacl,
@ -244,11 +258,9 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
.pc_encode = nfs3svc_encode_getaclres, .pc_encode = nfs3svc_encode_getaclres,
.pc_release = nfs3svc_release_getacl, .pc_release = nfs3svc_release_getacl,
.pc_argsize = sizeof(struct nfsd3_getaclargs), .pc_argsize = sizeof(struct nfsd3_getaclargs),
.pc_argzero = sizeof(struct nfsd3_getaclargs),
.pc_ressize = sizeof(struct nfsd3_getaclres), .pc_ressize = sizeof(struct nfsd3_getaclres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+1+2*(1+ACL), .pc_xdrressize = ST+1+2*(1+ACL),
.pc_name = "GETACL",
}, },
[ACLPROC3_SETACL] = { [ACLPROC3_SETACL] = {
.pc_func = nfsd3_proc_setacl, .pc_func = nfsd3_proc_setacl,
@ -256,11 +268,9 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
.pc_encode = nfs3svc_encode_setaclres, .pc_encode = nfs3svc_encode_setaclres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argsize = sizeof(struct nfsd3_setaclargs),
.pc_argzero = sizeof(struct nfsd3_setaclargs),
.pc_ressize = sizeof(struct nfsd3_attrstat), .pc_ressize = sizeof(struct nfsd3_attrstat),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT, .pc_xdrressize = ST+pAT,
.pc_name = "SETACL",
}, },
}; };

View File

@ -8,12 +8,10 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/ext2_fs.h> #include <linux/ext2_fs.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/namei.h>
#include "cache.h" #include "cache.h"
#include "xdr3.h" #include "xdr3.h"
#include "vfs.h" #include "vfs.h"
#include "filecache.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC #define NFSDDBG_FACILITY NFSDDBG_PROC
@ -68,15 +66,12 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp)
{ {
struct nfsd3_sattrargs *argp = rqstp->rq_argp; struct nfsd3_sattrargs *argp = rqstp->rq_argp;
struct nfsd3_attrstat *resp = rqstp->rq_resp; struct nfsd3_attrstat *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
dprintk("nfsd: SETATTR(3) %s\n", dprintk("nfsd: SETATTR(3) %s\n",
SVCFH_fmt(&argp->fh)); SVCFH_fmt(&argp->fh));
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, resp->status = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,
argp->check_guard, argp->guardtime); argp->check_guard, argp->guardtime);
return rpc_success; return rpc_success;
} }
@ -129,7 +124,7 @@ nfsd3_proc_access(struct svc_rqst *rqstp)
static __be32 static __be32
nfsd3_proc_readlink(struct svc_rqst *rqstp) nfsd3_proc_readlink(struct svc_rqst *rqstp)
{ {
struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd3_readlinkargs *argp = rqstp->rq_argp;
struct nfsd3_readlinkres *resp = rqstp->rq_resp; struct nfsd3_readlinkres *resp = rqstp->rq_resp;
dprintk("nfsd: READLINK(3) %s\n", SVCFH_fmt(&argp->fh)); dprintk("nfsd: READLINK(3) %s\n", SVCFH_fmt(&argp->fh));
@ -137,9 +132,7 @@ nfsd3_proc_readlink(struct svc_rqst *rqstp)
/* Read the symlink. */ /* Read the symlink. */
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->len = NFS3_MAXPATHLEN; resp->len = NFS3_MAXPATHLEN;
resp->pages = rqstp->rq_next_page++; resp->status = nfsd_readlink(rqstp, &resp->fh, argp->buffer, &resp->len);
resp->status = nfsd_readlink(rqstp, &resp->fh,
page_address(*resp->pages), &resp->len);
return rpc_success; return rpc_success;
} }
@ -151,43 +144,25 @@ nfsd3_proc_read(struct svc_rqst *rqstp)
{ {
struct nfsd3_readargs *argp = rqstp->rq_argp; struct nfsd3_readargs *argp = rqstp->rq_argp;
struct nfsd3_readres *resp = rqstp->rq_resp; struct nfsd3_readres *resp = rqstp->rq_resp;
unsigned int len; u32 max_blocksize = svc_max_payload(rqstp);
int v; unsigned long cnt = min(argp->count, max_blocksize);
dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n", dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
(unsigned long) argp->count, (unsigned long) argp->count,
(unsigned long long) argp->offset); (unsigned long long) argp->offset);
argp->count = min_t(u32, argp->count, svc_max_payload(rqstp));
argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen);
if (argp->offset > (u64)OFFSET_MAX)
argp->offset = (u64)OFFSET_MAX;
if (argp->offset + argp->count > (u64)OFFSET_MAX)
argp->count = (u64)OFFSET_MAX - argp->offset;
v = 0;
len = argp->count;
resp->pages = rqstp->rq_next_page;
while (len > 0) {
struct page *page = *(rqstp->rq_next_page++);
rqstp->rq_vec[v].iov_base = page_address(page);
rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
len -= rqstp->rq_vec[v].iov_len;
v++;
}
/* Obtain buffer pointer for payload. /* Obtain buffer pointer for payload.
* 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof) * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
* + 1 (xdr opaque byte count) = 26 * + 1 (xdr opaque byte count) = 26
*/ */
resp->count = argp->count; resp->count = cnt;
svc_reserve_auth(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4); svc_reserve_auth(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4);
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->status = nfsd_read(rqstp, &resp->fh, argp->offset, resp->status = nfsd_read(rqstp, &resp->fh, argp->offset,
rqstp->rq_vec, v, &resp->count, &resp->eof); rqstp->rq_vec, argp->vlen, &resp->count,
&resp->eof);
return rpc_success; return rpc_success;
} }
@ -215,147 +190,32 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->committed = argp->stable; resp->committed = argp->stable;
nvecs = svc_fill_write_vector(rqstp, &argp->payload); nvecs = svc_fill_write_vector(rqstp, rqstp->rq_arg.pages,
&argp->first, cnt);
if (!nvecs) {
resp->status = nfserr_io;
goto out;
}
resp->status = nfsd_write(rqstp, &resp->fh, argp->offset, resp->status = nfsd_write(rqstp, &resp->fh, argp->offset,
rqstp->rq_vec, nvecs, &cnt, rqstp->rq_vec, nvecs, &cnt,
resp->committed, resp->verf); resp->committed, resp->verf);
resp->count = cnt; resp->count = cnt;
out:
return rpc_success; return rpc_success;
} }
/* /*
* Implement NFSv3's unchecked, guarded, and exclusive CREATE * With NFSv3, CREATE processing is a lot easier than with NFSv2.
* semantics for regular files. Except for the created file, * At least in theory; we'll see how it fares in practice when the
* this operation is stateless on the server. * first reports about SunOS compatibility problems start to pour in...
*
* Upon return, caller must release @fhp and @resfhp.
*/ */
static __be32
nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct svc_fh *resfhp, struct nfsd3_createargs *argp)
{
struct iattr *iap = &argp->attrs;
struct dentry *parent, *child;
struct nfsd_attrs attrs = {
.na_iattr = iap,
};
__u32 v_mtime, v_atime;
struct inode *inode;
__be32 status;
int host_err;
if (isdotent(argp->name, argp->len))
return nfserr_exist;
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
if (status != nfs_ok)
return status;
parent = fhp->fh_dentry;
inode = d_inode(parent);
host_err = fh_want_write(fhp);
if (host_err)
return nfserrno(host_err);
inode_lock_nested(inode, I_MUTEX_PARENT);
child = lookup_one_len(argp->name, parent, argp->len);
if (IS_ERR(child)) {
status = nfserrno(PTR_ERR(child));
goto out;
}
if (d_really_is_negative(child)) {
status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
if (status != nfs_ok)
goto out;
}
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
if (status != nfs_ok)
goto out;
v_mtime = 0;
v_atime = 0;
if (argp->createmode == NFS3_CREATE_EXCLUSIVE) {
u32 *verifier = (u32 *)argp->verf;
/*
* Solaris 7 gets confused (bugid 4218508) if these have
* the high bit set, as do xfs filesystems without the
* "bigtime" feature. So just clear the high bits.
*/
v_mtime = verifier[0] & 0x7fffffff;
v_atime = verifier[1] & 0x7fffffff;
}
if (d_really_is_positive(child)) {
status = nfs_ok;
switch (argp->createmode) {
case NFS3_CREATE_UNCHECKED:
if (!d_is_reg(child))
break;
iap->ia_valid &= ATTR_SIZE;
goto set_attr;
case NFS3_CREATE_GUARDED:
status = nfserr_exist;
break;
case NFS3_CREATE_EXCLUSIVE:
if (d_inode(child)->i_mtime.tv_sec == v_mtime &&
d_inode(child)->i_atime.tv_sec == v_atime &&
d_inode(child)->i_size == 0) {
break;
}
status = nfserr_exist;
}
goto out;
}
if (!IS_POSIXACL(inode))
iap->ia_mode &= ~current_umask();
fh_fill_pre_attrs(fhp);
host_err = vfs_create(inode, child, iap->ia_mode, true);
if (host_err < 0) {
status = nfserrno(host_err);
goto out;
}
fh_fill_post_attrs(fhp);
/* A newly created file already has a file size of zero. */
if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
iap->ia_valid &= ~ATTR_SIZE;
if (argp->createmode == NFS3_CREATE_EXCLUSIVE) {
iap->ia_valid = ATTR_MTIME | ATTR_ATIME |
ATTR_MTIME_SET | ATTR_ATIME_SET;
iap->ia_mtime.tv_sec = v_mtime;
iap->ia_atime.tv_sec = v_atime;
iap->ia_mtime.tv_nsec = 0;
iap->ia_atime.tv_nsec = 0;
}
set_attr:
status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
out:
inode_unlock(inode);
if (child && !IS_ERR(child))
dput(child);
fh_drop_write(fhp);
return status;
}
static __be32 static __be32
nfsd3_proc_create(struct svc_rqst *rqstp) nfsd3_proc_create(struct svc_rqst *rqstp)
{ {
struct nfsd3_createargs *argp = rqstp->rq_argp; struct nfsd3_createargs *argp = rqstp->rq_argp;
struct nfsd3_diropres *resp = rqstp->rq_resp; struct nfsd3_diropres *resp = rqstp->rq_resp;
svc_fh *dirfhp, *newfhp; svc_fh *dirfhp, *newfhp = NULL;
struct iattr *attr;
dprintk("nfsd: CREATE(3) %s %.*s\n", dprintk("nfsd: CREATE(3) %s %.*s\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
@ -364,8 +224,21 @@ nfsd3_proc_create(struct svc_rqst *rqstp)
dirfhp = fh_copy(&resp->dirfh, &argp->fh); dirfhp = fh_copy(&resp->dirfh, &argp->fh);
newfhp = fh_init(&resp->fh, NFS3_FHSIZE); newfhp = fh_init(&resp->fh, NFS3_FHSIZE);
attr = &argp->attrs;
resp->status = nfsd3_create_file(rqstp, dirfhp, newfhp, argp); /* Unfudge the mode bits */
attr->ia_mode &= ~S_IFMT;
if (!(attr->ia_valid & ATTR_MODE)) {
attr->ia_valid |= ATTR_MODE;
attr->ia_mode = S_IFREG;
} else {
attr->ia_mode = (attr->ia_mode & ~S_IFMT) | S_IFREG;
}
/* Now create the file and set attributes */
resp->status = do_nfsd_create(rqstp, dirfhp, argp->name, argp->len,
attr, newfhp, argp->createmode,
(u32 *)argp->verf, NULL, NULL);
return rpc_success; return rpc_success;
} }
@ -377,9 +250,6 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp)
{ {
struct nfsd3_createargs *argp = rqstp->rq_argp; struct nfsd3_createargs *argp = rqstp->rq_argp;
struct nfsd3_diropres *resp = rqstp->rq_resp; struct nfsd3_diropres *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
dprintk("nfsd: MKDIR(3) %s %.*s\n", dprintk("nfsd: MKDIR(3) %s %.*s\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
@ -390,7 +260,8 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp)
fh_copy(&resp->dirfh, &argp->fh); fh_copy(&resp->dirfh, &argp->fh);
fh_init(&resp->fh, NFS3_FHSIZE); fh_init(&resp->fh, NFS3_FHSIZE);
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&attrs, S_IFDIR, 0, &resp->fh); &argp->attrs, S_IFDIR, 0, &resp->fh);
fh_unlock(&resp->dirfh);
return rpc_success; return rpc_success;
} }
@ -399,9 +270,6 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
{ {
struct nfsd3_symlinkargs *argp = rqstp->rq_argp; struct nfsd3_symlinkargs *argp = rqstp->rq_argp;
struct nfsd3_diropres *resp = rqstp->rq_resp; struct nfsd3_diropres *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
if (argp->tlen == 0) { if (argp->tlen == 0) {
resp->status = nfserr_inval; resp->status = nfserr_inval;
@ -428,7 +296,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
fh_copy(&resp->dirfh, &argp->ffh); fh_copy(&resp->dirfh, &argp->ffh);
fh_init(&resp->fh, NFS3_FHSIZE); fh_init(&resp->fh, NFS3_FHSIZE);
resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname,
argp->flen, argp->tname, &attrs, &resp->fh); argp->flen, argp->tname, &resp->fh);
kfree(argp->tname); kfree(argp->tname);
out: out:
return rpc_success; return rpc_success;
@ -442,9 +310,6 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp)
{ {
struct nfsd3_mknodargs *argp = rqstp->rq_argp; struct nfsd3_mknodargs *argp = rqstp->rq_argp;
struct nfsd3_diropres *resp = rqstp->rq_resp; struct nfsd3_diropres *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
int type; int type;
dev_t rdev = 0; dev_t rdev = 0;
@ -470,7 +335,8 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp)
type = nfs3_ftypes[argp->ftype]; type = nfs3_ftypes[argp->ftype];
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&attrs, type, rdev, &resp->fh); &argp->attrs, type, rdev, &resp->fh);
fh_unlock(&resp->dirfh);
out: out:
return rpc_success; return rpc_success;
} }
@ -493,6 +359,7 @@ nfsd3_proc_remove(struct svc_rqst *rqstp)
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->status = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, resp->status = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR,
argp->name, argp->len); argp->name, argp->len);
fh_unlock(&resp->fh);
return rpc_success; return rpc_success;
} }
@ -513,6 +380,7 @@ nfsd3_proc_rmdir(struct svc_rqst *rqstp)
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->status = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, resp->status = nfsd_unlink(rqstp, &resp->fh, S_IFDIR,
argp->name, argp->len); argp->name, argp->len);
fh_unlock(&resp->fh);
return rpc_success; return rpc_success;
} }
@ -558,26 +426,6 @@ nfsd3_proc_link(struct svc_rqst *rqstp)
return rpc_success; return rpc_success;
} }
static void nfsd3_init_dirlist_pages(struct svc_rqst *rqstp,
struct nfsd3_readdirres *resp,
u32 count)
{
struct xdr_buf *buf = &resp->dirlist;
struct xdr_stream *xdr = &resp->xdr;
unsigned int sendbuf = min_t(unsigned int, rqstp->rq_res.buflen,
svc_max_payload(rqstp));
memset(buf, 0, sizeof(*buf));
/* Reserve room for the NULL ptr & eof flag (-2 words) */
buf->buflen = clamp(count, (u32)(XDR_UNIT * 2), sendbuf);
buf->buflen -= XDR_UNIT * 2;
buf->pages = rqstp->rq_next_page;
rqstp->rq_next_page += (buf->buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
xdr_init_encode_pages(xdr, buf, buf->pages, NULL);
}
/* /*
* Read a portion of a directory. * Read a portion of a directory.
*/ */
@ -586,26 +434,53 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp)
{ {
struct nfsd3_readdirargs *argp = rqstp->rq_argp; struct nfsd3_readdirargs *argp = rqstp->rq_argp;
struct nfsd3_readdirres *resp = rqstp->rq_resp; struct nfsd3_readdirres *resp = rqstp->rq_resp;
loff_t offset; int count = 0;
struct page **p;
caddr_t page_addr = NULL;
dprintk("nfsd: READDIR(3) %s %d bytes at %d\n", dprintk("nfsd: READDIR(3) %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->count, (u32) argp->cookie); argp->count, (u32) argp->cookie);
nfsd3_init_dirlist_pages(rqstp, resp, argp->count); /* Make sure we've room for the NULL ptr & eof flag, and shrink to
* client read size */
count = (argp->count >> 2) - 2;
/* Read directory and encode entries on the fly */
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->common.err = nfs_ok;
resp->cookie_offset = 0;
resp->rqstp = rqstp;
offset = argp->cookie;
resp->status = nfsd_readdir(rqstp, &resp->fh, &offset,
&resp->common, nfs3svc_encode_entry3);
memcpy(resp->verf, argp->verf, 8);
nfs3svc_encode_cookie3(resp, offset);
/* Recycle only pages that were part of the reply */ resp->buflen = count;
rqstp->rq_next_page = resp->xdr.page_ptr + 1; resp->common.err = nfs_ok;
resp->buffer = argp->buffer;
resp->rqstp = rqstp;
resp->status = nfsd_readdir(rqstp, &resp->fh, (loff_t *)&argp->cookie,
&resp->common, nfs3svc_encode_entry);
memcpy(resp->verf, argp->verf, 8);
count = 0;
for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) {
page_addr = page_address(*p);
if (((caddr_t)resp->buffer >= page_addr) &&
((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) {
count += (caddr_t)resp->buffer - page_addr;
break;
}
count += PAGE_SIZE;
}
resp->count = count >> 2;
if (resp->offset) {
loff_t offset = argp->cookie;
if (unlikely(resp->offset1)) {
/* we ended up with offset on a page boundary */
*resp->offset = htonl(offset >> 32);
*resp->offset1 = htonl(offset & 0xffffffff);
resp->offset1 = NULL;
} else {
xdr_encode_hyper(resp->offset, offset);
}
resp->offset = NULL;
}
return rpc_success; return rpc_success;
} }
@ -619,17 +494,25 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp)
{ {
struct nfsd3_readdirargs *argp = rqstp->rq_argp; struct nfsd3_readdirargs *argp = rqstp->rq_argp;
struct nfsd3_readdirres *resp = rqstp->rq_resp; struct nfsd3_readdirres *resp = rqstp->rq_resp;
int count = 0;
loff_t offset; loff_t offset;
struct page **p;
caddr_t page_addr = NULL;
dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n", dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->count, (u32) argp->cookie); argp->count, (u32) argp->cookie);
nfsd3_init_dirlist_pages(rqstp, resp, argp->count); /* Convert byte count to number of words (i.e. >> 2),
* and reserve room for the NULL ptr & eof flag (-2 words) */
resp->count = (argp->count >> 2) - 2;
/* Read directory and encode entries on the fly */
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->common.err = nfs_ok; resp->common.err = nfs_ok;
resp->cookie_offset = 0; resp->buffer = argp->buffer;
resp->buflen = resp->count;
resp->rqstp = rqstp; resp->rqstp = rqstp;
offset = argp->cookie; offset = argp->cookie;
@ -643,12 +526,30 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp)
} }
resp->status = nfsd_readdir(rqstp, &resp->fh, &offset, resp->status = nfsd_readdir(rqstp, &resp->fh, &offset,
&resp->common, nfs3svc_encode_entryplus3); &resp->common, nfs3svc_encode_entry_plus);
memcpy(resp->verf, argp->verf, 8); memcpy(resp->verf, argp->verf, 8);
nfs3svc_encode_cookie3(resp, offset); for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) {
page_addr = page_address(*p);
/* Recycle only pages that were part of the reply */ if (((caddr_t)resp->buffer >= page_addr) &&
rqstp->rq_next_page = resp->xdr.page_ptr + 1; ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) {
count += (caddr_t)resp->buffer - page_addr;
break;
}
count += PAGE_SIZE;
}
resp->count = count >> 2;
if (resp->offset) {
if (unlikely(resp->offset1)) {
/* we ended up with offset on a page boundary */
*resp->offset = htonl(offset >> 32);
*resp->offset1 = htonl(offset & 0xffffffff);
resp->offset1 = NULL;
} else {
xdr_encode_hyper(resp->offset, offset);
}
resp->offset = NULL;
}
out: out:
return rpc_success; return rpc_success;
@ -764,21 +665,20 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
{ {
struct nfsd3_commitargs *argp = rqstp->rq_argp; struct nfsd3_commitargs *argp = rqstp->rq_argp;
struct nfsd3_commitres *resp = rqstp->rq_resp; struct nfsd3_commitres *resp = rqstp->rq_resp;
struct nfsd_file *nf;
dprintk("nfsd: COMMIT(3) %s %u@%Lu\n", dprintk("nfsd: COMMIT(3) %s %u@%Lu\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->count, argp->count,
(unsigned long long) argp->offset); (unsigned long long) argp->offset);
fh_copy(&resp->fh, &argp->fh); if (argp->offset > NFS_OFFSET_MAX) {
resp->status = nfsd_file_acquire_gc(rqstp, &resp->fh, NFSD_MAY_WRITE | resp->status = nfserr_inval;
NFSD_MAY_NOT_BREAK_LEASE, &nf);
if (resp->status)
goto out; goto out;
resp->status = nfsd_commit(rqstp, &resp->fh, nf, argp->offset, }
fh_copy(&resp->fh, &argp->fh);
resp->status = nfsd_commit(rqstp, &resp->fh, argp->offset,
argp->count, resp->verf); argp->count, resp->verf);
nfsd_file_put(nf);
out: out:
return rpc_success; return rpc_success;
} }
@ -788,14 +688,18 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
* NFSv3 Server procedures. * NFSv3 Server procedures.
* Only the results of non-idempotent operations are cached. * Only the results of non-idempotent operations are cached.
*/ */
#define nfs3svc_decode_fhandleargs nfs3svc_decode_fhandle
#define nfs3svc_encode_attrstatres nfs3svc_encode_attrstat #define nfs3svc_encode_attrstatres nfs3svc_encode_attrstat
#define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat #define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat
#define nfsd3_mkdirargs nfsd3_createargs #define nfsd3_mkdirargs nfsd3_createargs
#define nfsd3_readdirplusargs nfsd3_readdirargs #define nfsd3_readdirplusargs nfsd3_readdirargs
#define nfsd3_fhandleargs nfsd_fhandle #define nfsd3_fhandleargs nfsd_fhandle
#define nfsd3_fhandleres nfsd3_attrstat
#define nfsd3_attrstatres nfsd3_attrstat #define nfsd3_attrstatres nfsd3_attrstat
#define nfsd3_wccstatres nfsd3_attrstat #define nfsd3_wccstatres nfsd3_attrstat
#define nfsd3_createres nfsd3_diropres #define nfsd3_createres nfsd3_diropres
#define nfsd3_voidres nfsd3_voidargs
struct nfsd3_voidargs { int dummy; };
#define ST 1 /* status*/ #define ST 1 /* status*/
#define FH 17 /* filehandle with length */ #define FH 17 /* filehandle with length */
@ -806,26 +710,22 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
static const struct svc_procedure nfsd_procedures3[22] = { static const struct svc_procedure nfsd_procedures3[22] = {
[NFS3PROC_NULL] = { [NFS3PROC_NULL] = {
.pc_func = nfsd3_proc_null, .pc_func = nfsd3_proc_null,
.pc_decode = nfssvc_decode_voidarg, .pc_decode = nfs3svc_decode_voidarg,
.pc_encode = nfssvc_encode_voidres, .pc_encode = nfs3svc_encode_voidres,
.pc_argsize = sizeof(struct nfsd_voidargs), .pc_argsize = sizeof(struct nfsd3_voidargs),
.pc_argzero = sizeof(struct nfsd_voidargs), .pc_ressize = sizeof(struct nfsd3_voidres),
.pc_ressize = sizeof(struct nfsd_voidres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "NULL",
}, },
[NFS3PROC_GETATTR] = { [NFS3PROC_GETATTR] = {
.pc_func = nfsd3_proc_getattr, .pc_func = nfsd3_proc_getattr,
.pc_decode = nfs3svc_decode_fhandleargs, .pc_decode = nfs3svc_decode_fhandleargs,
.pc_encode = nfs3svc_encode_getattrres, .pc_encode = nfs3svc_encode_attrstatres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_fhandle), .pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_argzero = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd3_attrstatres), .pc_ressize = sizeof(struct nfsd3_attrstatres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT, .pc_xdrressize = ST+AT,
.pc_name = "GETATTR",
}, },
[NFS3PROC_SETATTR] = { [NFS3PROC_SETATTR] = {
.pc_func = nfsd3_proc_setattr, .pc_func = nfsd3_proc_setattr,
@ -833,23 +733,19 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_wccstatres, .pc_encode = nfs3svc_encode_wccstatres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_sattrargs), .pc_argsize = sizeof(struct nfsd3_sattrargs),
.pc_argzero = sizeof(struct nfsd3_sattrargs),
.pc_ressize = sizeof(struct nfsd3_wccstatres), .pc_ressize = sizeof(struct nfsd3_wccstatres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC, .pc_xdrressize = ST+WC,
.pc_name = "SETATTR",
}, },
[NFS3PROC_LOOKUP] = { [NFS3PROC_LOOKUP] = {
.pc_func = nfsd3_proc_lookup, .pc_func = nfsd3_proc_lookup,
.pc_decode = nfs3svc_decode_diropargs, .pc_decode = nfs3svc_decode_diropargs,
.pc_encode = nfs3svc_encode_lookupres, .pc_encode = nfs3svc_encode_diropres,
.pc_release = nfs3svc_release_fhandle2, .pc_release = nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_diropargs), .pc_argsize = sizeof(struct nfsd3_diropargs),
.pc_argzero = sizeof(struct nfsd3_diropargs),
.pc_ressize = sizeof(struct nfsd3_diropres), .pc_ressize = sizeof(struct nfsd3_diropres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+FH+pAT+pAT, .pc_xdrressize = ST+FH+pAT+pAT,
.pc_name = "LOOKUP",
}, },
[NFS3PROC_ACCESS] = { [NFS3PROC_ACCESS] = {
.pc_func = nfsd3_proc_access, .pc_func = nfsd3_proc_access,
@ -857,23 +753,19 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_accessres, .pc_encode = nfs3svc_encode_accessres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_accessargs), .pc_argsize = sizeof(struct nfsd3_accessargs),
.pc_argzero = sizeof(struct nfsd3_accessargs),
.pc_ressize = sizeof(struct nfsd3_accessres), .pc_ressize = sizeof(struct nfsd3_accessres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+1, .pc_xdrressize = ST+pAT+1,
.pc_name = "ACCESS",
}, },
[NFS3PROC_READLINK] = { [NFS3PROC_READLINK] = {
.pc_func = nfsd3_proc_readlink, .pc_func = nfsd3_proc_readlink,
.pc_decode = nfs3svc_decode_fhandleargs, .pc_decode = nfs3svc_decode_readlinkargs,
.pc_encode = nfs3svc_encode_readlinkres, .pc_encode = nfs3svc_encode_readlinkres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_fhandle), .pc_argsize = sizeof(struct nfsd3_readlinkargs),
.pc_argzero = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd3_readlinkres), .pc_ressize = sizeof(struct nfsd3_readlinkres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+1+NFS3_MAXPATHLEN/4, .pc_xdrressize = ST+pAT+1+NFS3_MAXPATHLEN/4,
.pc_name = "READLINK",
}, },
[NFS3PROC_READ] = { [NFS3PROC_READ] = {
.pc_func = nfsd3_proc_read, .pc_func = nfsd3_proc_read,
@ -881,11 +773,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_readres, .pc_encode = nfs3svc_encode_readres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_readargs), .pc_argsize = sizeof(struct nfsd3_readargs),
.pc_argzero = sizeof(struct nfsd3_readargs),
.pc_ressize = sizeof(struct nfsd3_readres), .pc_ressize = sizeof(struct nfsd3_readres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+4+NFSSVC_MAXBLKSIZE/4, .pc_xdrressize = ST+pAT+4+NFSSVC_MAXBLKSIZE/4,
.pc_name = "READ",
}, },
[NFS3PROC_WRITE] = { [NFS3PROC_WRITE] = {
.pc_func = nfsd3_proc_write, .pc_func = nfsd3_proc_write,
@ -893,11 +783,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_writeres, .pc_encode = nfs3svc_encode_writeres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_writeargs), .pc_argsize = sizeof(struct nfsd3_writeargs),
.pc_argzero = sizeof(struct nfsd3_writeargs),
.pc_ressize = sizeof(struct nfsd3_writeres), .pc_ressize = sizeof(struct nfsd3_writeres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC+4, .pc_xdrressize = ST+WC+4,
.pc_name = "WRITE",
}, },
[NFS3PROC_CREATE] = { [NFS3PROC_CREATE] = {
.pc_func = nfsd3_proc_create, .pc_func = nfsd3_proc_create,
@ -905,11 +793,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_createres, .pc_encode = nfs3svc_encode_createres,
.pc_release = nfs3svc_release_fhandle2, .pc_release = nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_createargs), .pc_argsize = sizeof(struct nfsd3_createargs),
.pc_argzero = sizeof(struct nfsd3_createargs),
.pc_ressize = sizeof(struct nfsd3_createres), .pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC, .pc_xdrressize = ST+(1+FH+pAT)+WC,
.pc_name = "CREATE",
}, },
[NFS3PROC_MKDIR] = { [NFS3PROC_MKDIR] = {
.pc_func = nfsd3_proc_mkdir, .pc_func = nfsd3_proc_mkdir,
@ -917,11 +803,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_createres, .pc_encode = nfs3svc_encode_createres,
.pc_release = nfs3svc_release_fhandle2, .pc_release = nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_mkdirargs), .pc_argsize = sizeof(struct nfsd3_mkdirargs),
.pc_argzero = sizeof(struct nfsd3_mkdirargs),
.pc_ressize = sizeof(struct nfsd3_createres), .pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC, .pc_xdrressize = ST+(1+FH+pAT)+WC,
.pc_name = "MKDIR",
}, },
[NFS3PROC_SYMLINK] = { [NFS3PROC_SYMLINK] = {
.pc_func = nfsd3_proc_symlink, .pc_func = nfsd3_proc_symlink,
@ -929,11 +813,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_createres, .pc_encode = nfs3svc_encode_createres,
.pc_release = nfs3svc_release_fhandle2, .pc_release = nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_symlinkargs), .pc_argsize = sizeof(struct nfsd3_symlinkargs),
.pc_argzero = sizeof(struct nfsd3_symlinkargs),
.pc_ressize = sizeof(struct nfsd3_createres), .pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC, .pc_xdrressize = ST+(1+FH+pAT)+WC,
.pc_name = "SYMLINK",
}, },
[NFS3PROC_MKNOD] = { [NFS3PROC_MKNOD] = {
.pc_func = nfsd3_proc_mknod, .pc_func = nfsd3_proc_mknod,
@ -941,11 +823,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_createres, .pc_encode = nfs3svc_encode_createres,
.pc_release = nfs3svc_release_fhandle2, .pc_release = nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_mknodargs), .pc_argsize = sizeof(struct nfsd3_mknodargs),
.pc_argzero = sizeof(struct nfsd3_mknodargs),
.pc_ressize = sizeof(struct nfsd3_createres), .pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC, .pc_xdrressize = ST+(1+FH+pAT)+WC,
.pc_name = "MKNOD",
}, },
[NFS3PROC_REMOVE] = { [NFS3PROC_REMOVE] = {
.pc_func = nfsd3_proc_remove, .pc_func = nfsd3_proc_remove,
@ -953,11 +833,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_wccstatres, .pc_encode = nfs3svc_encode_wccstatres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_diropargs), .pc_argsize = sizeof(struct nfsd3_diropargs),
.pc_argzero = sizeof(struct nfsd3_diropargs),
.pc_ressize = sizeof(struct nfsd3_wccstatres), .pc_ressize = sizeof(struct nfsd3_wccstatres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC, .pc_xdrressize = ST+WC,
.pc_name = "REMOVE",
}, },
[NFS3PROC_RMDIR] = { [NFS3PROC_RMDIR] = {
.pc_func = nfsd3_proc_rmdir, .pc_func = nfsd3_proc_rmdir,
@ -965,11 +843,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_wccstatres, .pc_encode = nfs3svc_encode_wccstatres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_diropargs), .pc_argsize = sizeof(struct nfsd3_diropargs),
.pc_argzero = sizeof(struct nfsd3_diropargs),
.pc_ressize = sizeof(struct nfsd3_wccstatres), .pc_ressize = sizeof(struct nfsd3_wccstatres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC, .pc_xdrressize = ST+WC,
.pc_name = "RMDIR",
}, },
[NFS3PROC_RENAME] = { [NFS3PROC_RENAME] = {
.pc_func = nfsd3_proc_rename, .pc_func = nfsd3_proc_rename,
@ -977,11 +853,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_renameres, .pc_encode = nfs3svc_encode_renameres,
.pc_release = nfs3svc_release_fhandle2, .pc_release = nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_renameargs), .pc_argsize = sizeof(struct nfsd3_renameargs),
.pc_argzero = sizeof(struct nfsd3_renameargs),
.pc_ressize = sizeof(struct nfsd3_renameres), .pc_ressize = sizeof(struct nfsd3_renameres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC+WC, .pc_xdrressize = ST+WC+WC,
.pc_name = "RENAME",
}, },
[NFS3PROC_LINK] = { [NFS3PROC_LINK] = {
.pc_func = nfsd3_proc_link, .pc_func = nfsd3_proc_link,
@ -989,11 +863,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_linkres, .pc_encode = nfs3svc_encode_linkres,
.pc_release = nfs3svc_release_fhandle2, .pc_release = nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_linkargs), .pc_argsize = sizeof(struct nfsd3_linkargs),
.pc_argzero = sizeof(struct nfsd3_linkargs),
.pc_ressize = sizeof(struct nfsd3_linkres), .pc_ressize = sizeof(struct nfsd3_linkres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+pAT+WC, .pc_xdrressize = ST+pAT+WC,
.pc_name = "LINK",
}, },
[NFS3PROC_READDIR] = { [NFS3PROC_READDIR] = {
.pc_func = nfsd3_proc_readdir, .pc_func = nfsd3_proc_readdir,
@ -1001,10 +873,8 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_readdirres, .pc_encode = nfs3svc_encode_readdirres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_readdirargs), .pc_argsize = sizeof(struct nfsd3_readdirargs),
.pc_argzero = sizeof(struct nfsd3_readdirargs),
.pc_ressize = sizeof(struct nfsd3_readdirres), .pc_ressize = sizeof(struct nfsd3_readdirres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_name = "READDIR",
}, },
[NFS3PROC_READDIRPLUS] = { [NFS3PROC_READDIRPLUS] = {
.pc_func = nfsd3_proc_readdirplus, .pc_func = nfsd3_proc_readdirplus,
@ -1012,43 +882,35 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_readdirres, .pc_encode = nfs3svc_encode_readdirres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_readdirplusargs), .pc_argsize = sizeof(struct nfsd3_readdirplusargs),
.pc_argzero = sizeof(struct nfsd3_readdirplusargs),
.pc_ressize = sizeof(struct nfsd3_readdirres), .pc_ressize = sizeof(struct nfsd3_readdirres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_name = "READDIRPLUS",
}, },
[NFS3PROC_FSSTAT] = { [NFS3PROC_FSSTAT] = {
.pc_func = nfsd3_proc_fsstat, .pc_func = nfsd3_proc_fsstat,
.pc_decode = nfs3svc_decode_fhandleargs, .pc_decode = nfs3svc_decode_fhandleargs,
.pc_encode = nfs3svc_encode_fsstatres, .pc_encode = nfs3svc_encode_fsstatres,
.pc_argsize = sizeof(struct nfsd3_fhandleargs), .pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_argzero = sizeof(struct nfsd3_fhandleargs),
.pc_ressize = sizeof(struct nfsd3_fsstatres), .pc_ressize = sizeof(struct nfsd3_fsstatres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+2*6+1, .pc_xdrressize = ST+pAT+2*6+1,
.pc_name = "FSSTAT",
}, },
[NFS3PROC_FSINFO] = { [NFS3PROC_FSINFO] = {
.pc_func = nfsd3_proc_fsinfo, .pc_func = nfsd3_proc_fsinfo,
.pc_decode = nfs3svc_decode_fhandleargs, .pc_decode = nfs3svc_decode_fhandleargs,
.pc_encode = nfs3svc_encode_fsinfores, .pc_encode = nfs3svc_encode_fsinfores,
.pc_argsize = sizeof(struct nfsd3_fhandleargs), .pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_argzero = sizeof(struct nfsd3_fhandleargs),
.pc_ressize = sizeof(struct nfsd3_fsinfores), .pc_ressize = sizeof(struct nfsd3_fsinfores),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+12, .pc_xdrressize = ST+pAT+12,
.pc_name = "FSINFO",
}, },
[NFS3PROC_PATHCONF] = { [NFS3PROC_PATHCONF] = {
.pc_func = nfsd3_proc_pathconf, .pc_func = nfsd3_proc_pathconf,
.pc_decode = nfs3svc_decode_fhandleargs, .pc_decode = nfs3svc_decode_fhandleargs,
.pc_encode = nfs3svc_encode_pathconfres, .pc_encode = nfs3svc_encode_pathconfres,
.pc_argsize = sizeof(struct nfsd3_fhandleargs), .pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_argzero = sizeof(struct nfsd3_fhandleargs),
.pc_ressize = sizeof(struct nfsd3_pathconfres), .pc_ressize = sizeof(struct nfsd3_pathconfres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+6, .pc_xdrressize = ST+pAT+6,
.pc_name = "PATHCONF",
}, },
[NFS3PROC_COMMIT] = { [NFS3PROC_COMMIT] = {
.pc_func = nfsd3_proc_commit, .pc_func = nfsd3_proc_commit,
@ -1056,11 +918,9 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_encode = nfs3svc_encode_commitres, .pc_encode = nfs3svc_encode_commitres,
.pc_release = nfs3svc_release_fhandle, .pc_release = nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_commitargs), .pc_argsize = sizeof(struct nfsd3_commitargs),
.pc_argzero = sizeof(struct nfsd3_commitargs),
.pc_ressize = sizeof(struct nfsd3_commitres), .pc_ressize = sizeof(struct nfsd3_commitres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+WC+2, .pc_xdrressize = ST+WC+2,
.pc_name = "COMMIT",
}, },
}; };

File diff suppressed because it is too large Load Diff

View File

@ -751,26 +751,57 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
return ret; return ret;
} }
__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, __be32
struct nfsd_attrs *attr) nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfs4_acl *acl)
{ {
__be32 error;
int host_error; int host_error;
struct dentry *dentry;
struct inode *inode;
struct posix_acl *pacl = NULL, *dpacl = NULL;
unsigned int flags = 0; unsigned int flags = 0;
if (!acl) /* Get inode */
return nfs_ok; error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
if (error)
return error;
if (type == NF4DIR) dentry = fhp->fh_dentry;
inode = d_inode(dentry);
if (S_ISDIR(inode->i_mode))
flags = NFS4_ACL_DIR; flags = NFS4_ACL_DIR;
host_error = nfs4_acl_nfsv4_to_posix(acl, &attr->na_pacl, host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
&attr->na_dpacl, flags);
if (host_error == -EINVAL) if (host_error == -EINVAL)
return nfserr_attrnotsupp; return nfserr_attrnotsupp;
if (host_error < 0)
goto out_nfserr;
fh_lock(fhp);
host_error = set_posix_acl(inode, ACL_TYPE_ACCESS, pacl);
if (host_error < 0)
goto out_drop_lock;
if (S_ISDIR(inode->i_mode)) {
host_error = set_posix_acl(inode, ACL_TYPE_DEFAULT, dpacl);
}
out_drop_lock:
fh_unlock(fhp);
posix_acl_release(pacl);
posix_acl_release(dpacl);
out_nfserr:
if (host_error == -EOPNOTSUPP)
return nfserr_attrnotsupp;
else else
return nfserrno(host_error); return nfserrno(host_error);
} }
static short static short
ace2type(struct nfs4_ace *ace) ace2type(struct nfs4_ace *ace)
{ {

View File

@ -76,17 +76,6 @@ static __be32 *xdr_encode_empty_array(__be32 *p)
* 1 Protocol" * 1 Protocol"
*/ */
static void encode_uint32(struct xdr_stream *xdr, u32 n)
{
WARN_ON_ONCE(xdr_stream_encode_u32(xdr, n) < 0);
}
static void encode_bitmap4(struct xdr_stream *xdr, const __u32 *bitmap,
size_t len)
{
WARN_ON_ONCE(xdr_stream_encode_uint32_array(xdr, bitmap, len) < 0);
}
/* /*
* nfs_cb_opnum4 * nfs_cb_opnum4
* *
@ -132,7 +121,7 @@ static void encode_nfs_fh4(struct xdr_stream *xdr, const struct knfsd_fh *fh)
BUG_ON(length > NFS4_FHSIZE); BUG_ON(length > NFS4_FHSIZE);
p = xdr_reserve_space(xdr, 4 + length); p = xdr_reserve_space(xdr, 4 + length);
xdr_encode_opaque(p, &fh->fh_raw, length); xdr_encode_opaque(p, &fh->fh_base, length);
} }
/* /*
@ -339,24 +328,6 @@ static void encode_cb_recall4args(struct xdr_stream *xdr,
hdr->nops++; hdr->nops++;
} }
/*
* CB_RECALLANY4args
*
* struct CB_RECALLANY4args {
* uint32_t craa_objects_to_keep;
* bitmap4 craa_type_mask;
* };
*/
static void
encode_cb_recallany4args(struct xdr_stream *xdr,
struct nfs4_cb_compound_hdr *hdr, struct nfsd4_cb_recall_any *ra)
{
encode_nfs_cb_opnum4(xdr, OP_CB_RECALL_ANY);
encode_uint32(xdr, ra->ra_keep);
encode_bitmap4(xdr, ra->ra_bmval, ARRAY_SIZE(ra->ra_bmval));
hdr->nops++;
}
/* /*
* CB_SEQUENCE4args * CB_SEQUENCE4args
* *
@ -511,26 +482,6 @@ static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_cb_nops(&hdr); encode_cb_nops(&hdr);
} }
/*
* 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
*/
static void
nfs4_xdr_enc_cb_recall_any(struct rpc_rqst *req,
struct xdr_stream *xdr, const void *data)
{
const struct nfsd4_callback *cb = data;
struct nfsd4_cb_recall_any *ra;
struct nfs4_cb_compound_hdr hdr = {
.ident = cb->cb_clp->cl_cb_ident,
.minorversion = cb->cb_clp->cl_minorversion,
};
ra = container_of(cb, struct nfsd4_cb_recall_any, ra_cb);
encode_cb_compound4args(xdr, &hdr);
encode_cb_sequence4args(xdr, cb, &hdr);
encode_cb_recallany4args(xdr, &hdr, ra);
encode_cb_nops(&hdr);
}
/* /*
* NFSv4.0 and NFSv4.1 XDR decode functions * NFSv4.0 and NFSv4.1 XDR decode functions
@ -569,28 +520,6 @@ static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status); return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status);
} }
/*
* 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
*/
static int
nfs4_xdr_dec_cb_recall_any(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfsd4_callback *cb = data;
struct nfs4_cb_compound_hdr hdr;
int status;
status = decode_cb_compound4res(xdr, &hdr);
if (unlikely(status))
return status;
status = decode_cb_sequence4res(xdr, cb);
if (unlikely(status || cb->cb_seq_status))
return status;
status = decode_cb_op_status(xdr, OP_CB_RECALL_ANY, &cb->cb_status);
return status;
}
#ifdef CONFIG_NFSD_PNFS #ifdef CONFIG_NFSD_PNFS
/* /*
* CB_LAYOUTRECALL4args * CB_LAYOUTRECALL4args
@ -750,7 +679,7 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
* case NFS4_OK: * case NFS4_OK:
* write_response4 coa_resok4; * write_response4 coa_resok4;
* default: * default:
* length4 coa_bytes_copied; * length4 coa_bytes_copied;
* }; * };
* struct CB_OFFLOAD4args { * struct CB_OFFLOAD4args {
* nfs_fh4 coa_fh; * nfs_fh4 coa_fh;
@ -759,22 +688,21 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
* }; * };
*/ */
static void encode_offload_info4(struct xdr_stream *xdr, static void encode_offload_info4(struct xdr_stream *xdr,
const struct nfsd4_cb_offload *cbo) __be32 nfserr,
const struct nfsd4_copy *cp)
{ {
__be32 *p; __be32 *p;
p = xdr_reserve_space(xdr, 4); p = xdr_reserve_space(xdr, 4);
*p = cbo->co_nfserr; *p++ = nfserr;
switch (cbo->co_nfserr) { if (!nfserr) {
case nfs_ok:
p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE); p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
p = xdr_encode_empty_array(p); p = xdr_encode_empty_array(p);
p = xdr_encode_hyper(p, cbo->co_res.wr_bytes_written); p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);
*p++ = cpu_to_be32(cbo->co_res.wr_stable_how); *p++ = cpu_to_be32(cp->cp_res.wr_stable_how);
p = xdr_encode_opaque_fixed(p, cbo->co_res.wr_verifier.data, p = xdr_encode_opaque_fixed(p, cp->cp_res.wr_verifier.data,
NFS4_VERIFIER_SIZE); NFS4_VERIFIER_SIZE);
break; } else {
default:
p = xdr_reserve_space(xdr, 8); p = xdr_reserve_space(xdr, 8);
/* We always return success if bytes were written */ /* We always return success if bytes were written */
p = xdr_encode_hyper(p, 0); p = xdr_encode_hyper(p, 0);
@ -782,16 +710,18 @@ static void encode_offload_info4(struct xdr_stream *xdr,
} }
static void encode_cb_offload4args(struct xdr_stream *xdr, static void encode_cb_offload4args(struct xdr_stream *xdr,
const struct nfsd4_cb_offload *cbo, __be32 nfserr,
const struct knfsd_fh *fh,
const struct nfsd4_copy *cp,
struct nfs4_cb_compound_hdr *hdr) struct nfs4_cb_compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
p = xdr_reserve_space(xdr, 4); p = xdr_reserve_space(xdr, 4);
*p = cpu_to_be32(OP_CB_OFFLOAD); *p++ = cpu_to_be32(OP_CB_OFFLOAD);
encode_nfs_fh4(xdr, &cbo->co_fh); encode_nfs_fh4(xdr, fh);
encode_stateid4(xdr, &cbo->co_res.cb_stateid); encode_stateid4(xdr, &cp->cp_res.cb_stateid);
encode_offload_info4(xdr, cbo); encode_offload_info4(xdr, nfserr, cp);
hdr->nops++; hdr->nops++;
} }
@ -801,8 +731,8 @@ static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
const void *data) const void *data)
{ {
const struct nfsd4_callback *cb = data; const struct nfsd4_callback *cb = data;
const struct nfsd4_cb_offload *cbo = const struct nfsd4_copy *cp =
container_of(cb, struct nfsd4_cb_offload, co_cb); container_of(cb, struct nfsd4_copy, cp_cb);
struct nfs4_cb_compound_hdr hdr = { struct nfs4_cb_compound_hdr hdr = {
.ident = 0, .ident = 0,
.minorversion = cb->cb_clp->cl_minorversion, .minorversion = cb->cb_clp->cl_minorversion,
@ -810,7 +740,7 @@ static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
encode_cb_compound4args(xdr, &hdr); encode_cb_compound4args(xdr, &hdr);
encode_cb_sequence4args(xdr, cb, &hdr); encode_cb_sequence4args(xdr, cb, &hdr);
encode_cb_offload4args(xdr, cbo, &hdr); encode_cb_offload4args(xdr, cp->nfserr, &cp->fh, cp, &hdr);
encode_cb_nops(&hdr); encode_cb_nops(&hdr);
} }
@ -854,7 +784,6 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = {
#endif #endif
PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock), PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock),
PROC(CB_OFFLOAD, COMPOUND, cb_offload, cb_offload), PROC(CB_OFFLOAD, COMPOUND, cb_offload, cb_offload),
PROC(CB_RECALL_ANY, COMPOUND, cb_recall_any, cb_recall_any),
}; };
static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)]; static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)];
@ -1012,43 +941,37 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
clp->cl_cb_conn.cb_xprt = conn->cb_xprt; clp->cl_cb_conn.cb_xprt = conn->cb_xprt;
clp->cl_cb_client = client; clp->cl_cb_client = client;
clp->cl_cb_cred = cred; clp->cl_cb_cred = cred;
rcu_read_lock(); trace_nfsd_cb_setup(clp);
trace_nfsd_cb_setup(clp, rpc_peeraddr2str(client, RPC_DISPLAY_NETID),
args.authflavor);
rcu_read_unlock();
return 0; return 0;
} }
static void nfsd4_mark_cb_state(struct nfs4_client *clp, int newstate)
{
if (clp->cl_cb_state != newstate) {
clp->cl_cb_state = newstate;
trace_nfsd_cb_state(clp);
}
}
static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason) static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
{ {
if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags)) if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
return; return;
nfsd4_mark_cb_state(clp, NFSD4_CB_DOWN); clp->cl_cb_state = NFSD4_CB_DOWN;
trace_nfsd_cb_state(clp);
} }
static void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason) static void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason)
{ {
if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags)) if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
return; return;
nfsd4_mark_cb_state(clp, NFSD4_CB_FAULT); clp->cl_cb_state = NFSD4_CB_FAULT;
trace_nfsd_cb_state(clp);
} }
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);
trace_nfsd_cb_done(clp, task->tk_status);
if (task->tk_status) if (task->tk_status)
nfsd4_mark_cb_down(clp, task->tk_status); nfsd4_mark_cb_down(clp, task->tk_status);
else else {
nfsd4_mark_cb_state(clp, NFSD4_CB_UP); clp->cl_cb_state = NFSD4_CB_UP;
trace_nfsd_cb_state(clp);
}
} }
static void nfsd4_cb_probe_release(void *calldata) static void nfsd4_cb_probe_release(void *calldata)
@ -1072,8 +995,8 @@ static const struct rpc_call_ops nfsd4_cb_probe_ops = {
*/ */
void nfsd4_probe_callback(struct nfs4_client *clp) void nfsd4_probe_callback(struct nfs4_client *clp)
{ {
trace_nfsd_cb_probe(clp); clp->cl_cb_state = NFSD4_CB_UNKNOWN;
nfsd4_mark_cb_state(clp, NFSD4_CB_UNKNOWN); trace_nfsd_cb_state(clp);
set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags); set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags);
nfsd4_run_cb(&clp->cl_cb_null); nfsd4_run_cb(&clp->cl_cb_null);
} }
@ -1086,10 +1009,11 @@ void nfsd4_probe_callback_sync(struct nfs4_client *clp)
void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
{ {
nfsd4_mark_cb_state(clp, NFSD4_CB_UNKNOWN); clp->cl_cb_state = NFSD4_CB_UNKNOWN;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn)); memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
trace_nfsd_cb_state(clp);
} }
/* /*
@ -1246,6 +1170,8 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
struct nfsd4_callback *cb = calldata; struct nfsd4_callback *cb = calldata;
struct nfs4_client *clp = cb->cb_clp; struct nfs4_client *clp = cb->cb_clp;
trace_nfsd_cb_done(clp, task->tk_status);
if (!nfsd4_cb_sequence_done(task, cb)) if (!nfsd4_cb_sequence_done(task, cb))
return; return;
@ -1305,9 +1231,6 @@ void nfsd4_destroy_callback_queue(void)
/* must be called under the state lock */ /* must be called under the state lock */
void nfsd4_shutdown_callback(struct nfs4_client *clp) void nfsd4_shutdown_callback(struct nfs4_client *clp)
{ {
if (clp->cl_cb_state != NFSD4_CB_UNKNOWN)
trace_nfsd_cb_shutdown(clp);
set_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags); set_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags);
/* /*
* Note this won't actually result in a null callback; * Note this won't actually result in a null callback;
@ -1353,6 +1276,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
* kill the old client: * kill the old client:
*/ */
if (clp->cl_cb_client) { if (clp->cl_cb_client) {
trace_nfsd_cb_shutdown(clp);
rpc_shutdown_client(clp->cl_cb_client); rpc_shutdown_client(clp->cl_cb_client);
clp->cl_cb_client = NULL; clp->cl_cb_client = NULL;
put_cred(clp->cl_cb_cred); put_cred(clp->cl_cb_cred);
@ -1398,6 +1322,8 @@ nfsd4_run_cb_work(struct work_struct *work)
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
int flags; int flags;
trace_nfsd_cb_work(clp, cb->cb_msg.rpc_proc->p_name);
if (cb->cb_need_restart) { if (cb->cb_need_restart) {
cb->cb_need_restart = false; cb->cb_need_restart = false;
} else { } else {
@ -1419,7 +1345,7 @@ nfsd4_run_cb_work(struct work_struct *work)
* Don't send probe messages for 4.1 or later. * Don't send probe messages for 4.1 or later.
*/ */
if (!cb->cb_ops && clp->cl_minorversion) { if (!cb->cb_ops && clp->cl_minorversion) {
nfsd4_mark_cb_state(clp, NFSD4_CB_UP); clp->cl_cb_state = NFSD4_CB_UP;
nfsd41_destroy_cb(cb); nfsd41_destroy_cb(cb);
return; return;
} }
@ -1445,21 +1371,11 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
cb->cb_holds_slot = false; cb->cb_holds_slot = false;
} }
/** void nfsd4_run_cb(struct nfsd4_callback *cb)
* nfsd4_run_cb - queue up a callback job to run
* @cb: callback to queue
*
* Kick off a callback to do its thing. Returns false if it was already
* on a queue, true otherwise.
*/
bool nfsd4_run_cb(struct nfsd4_callback *cb)
{ {
struct nfs4_client *clp = cb->cb_clp; struct nfs4_client *clp = cb->cb_clp;
bool queued;
nfsd41_cb_inflight_begin(clp); nfsd41_cb_inflight_begin(clp);
queued = nfsd4_queue_cb(cb); if (!nfsd4_queue_cb(cb))
if (!queued)
nfsd41_cb_inflight_end(clp); nfsd41_cb_inflight_end(clp);
return queued;
} }

View File

@ -41,7 +41,6 @@
#include "idmap.h" #include "idmap.h"
#include "nfsd.h" #include "nfsd.h"
#include "netns.h" #include "netns.h"
#include "vfs.h"
/* /*
* Turn off idmapping when using AUTH_SYS. * Turn off idmapping when using AUTH_SYS.
@ -83,8 +82,8 @@ ent_init(struct cache_head *cnew, struct cache_head *citm)
new->id = itm->id; new->id = itm->id;
new->type = itm->type; new->type = itm->type;
strscpy(new->name, itm->name, sizeof(new->name)); strlcpy(new->name, itm->name, sizeof(new->name));
strscpy(new->authname, itm->authname, sizeof(new->authname)); strlcpy(new->authname, itm->authname, sizeof(new->authname));
} }
static void static void
@ -549,7 +548,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen
return nfserr_badowner; return nfserr_badowner;
memcpy(key.name, name, namelen); memcpy(key.name, name, namelen);
key.name[namelen] = '\0'; key.name[namelen] = '\0';
strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item); ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item);
if (ret == -ENOENT) if (ret == -ENOENT)
return nfserr_badowner; return nfserr_badowner;
@ -585,7 +584,7 @@ static __be32 idmap_id_to_name(struct xdr_stream *xdr,
int ret; int ret;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item); ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);
if (ret == -ENOENT) if (ret == -ENOENT)
return encode_ascii_id(xdr, id); return encode_ascii_id(xdr, id);

View File

@ -421,7 +421,7 @@ nfsd4_insert_layout(struct nfsd4_layoutget *lgp, struct nfs4_layout_stateid *ls)
new = kmem_cache_alloc(nfs4_layout_cache, GFP_KERNEL); new = kmem_cache_alloc(nfs4_layout_cache, GFP_KERNEL);
if (!new) if (!new)
return nfserr_jukebox; return nfserr_jukebox;
memcpy(&new->lo_seg, seg, sizeof(new->lo_seg)); memcpy(&new->lo_seg, seg, sizeof(lp->lo_seg));
new->lo_state = ls; new->lo_state = ls;
spin_lock(&fp->fi_lock); spin_lock(&fp->fi_lock);
@ -657,7 +657,7 @@ nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task)
ktime_t now, cutoff; ktime_t now, cutoff;
const struct nfsd4_layout_ops *ops; const struct nfsd4_layout_ops *ops;
trace_nfsd_cb_layout_done(&ls->ls_stid.sc_stateid, task);
switch (task->tk_status) { switch (task->tk_status) {
case 0: case 0:
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:

File diff suppressed because it is too large Load Diff

View File

@ -626,7 +626,7 @@ nfsd4_legacy_tracking_init(struct net *net)
status = nfsd4_load_reboot_recovery_data(net); status = nfsd4_load_reboot_recovery_data(net);
if (status) if (status)
goto err; goto err;
pr_info("NFSD: Using legacy client tracking operations.\n"); printk("NFSD: Using legacy client tracking operations.\n");
return 0; return 0;
err: err:
@ -807,17 +807,17 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
if (get_user(namelen, &ci->cc_name.cn_len)) if (get_user(namelen, &ci->cc_name.cn_len))
return -EFAULT; return -EFAULT;
name.data = memdup_user(&ci->cc_name.cn_id, namelen); name.data = memdup_user(&ci->cc_name.cn_id, namelen);
if (IS_ERR(name.data)) if (IS_ERR_OR_NULL(name.data))
return PTR_ERR(name.data); return -EFAULT;
name.len = namelen; name.len = namelen;
get_user(princhashlen, &ci->cc_princhash.cp_len); get_user(princhashlen, &ci->cc_princhash.cp_len);
if (princhashlen > 0) { if (princhashlen > 0) {
princhash.data = memdup_user( princhash.data = memdup_user(
&ci->cc_princhash.cp_data, &ci->cc_princhash.cp_data,
princhashlen); princhashlen);
if (IS_ERR(princhash.data)) { if (IS_ERR_OR_NULL(princhash.data)) {
kfree(name.data); kfree(name.data);
return PTR_ERR(princhash.data); return -EFAULT;
} }
princhash.len = princhashlen; princhash.len = princhashlen;
} else } else
@ -829,8 +829,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
if (get_user(namelen, &cnm->cn_len)) if (get_user(namelen, &cnm->cn_len))
return -EFAULT; return -EFAULT;
name.data = memdup_user(&cnm->cn_id, namelen); name.data = memdup_user(&cnm->cn_id, namelen);
if (IS_ERR(name.data)) if (IS_ERR_OR_NULL(name.data))
return PTR_ERR(name.data); return -EFAULT;
name.len = namelen; name.len = namelen;
} }
if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) {
@ -1030,7 +1030,7 @@ nfsd4_init_cld_pipe(struct net *net)
status = __nfsd4_init_cld_pipe(net); status = __nfsd4_init_cld_pipe(net);
if (!status) if (!status)
pr_info("NFSD: Using old nfsdcld client tracking operations.\n"); printk("NFSD: Using old nfsdcld client tracking operations.\n");
return status; return status;
} }
@ -1607,7 +1607,7 @@ nfsd4_cld_tracking_init(struct net *net)
nfs4_release_reclaim(nn); nfs4_release_reclaim(nn);
goto err_remove; goto err_remove;
} else } else
pr_info("NFSD: Using nfsdcld client tracking operations.\n"); printk("NFSD: Using nfsdcld client tracking operations.\n");
return 0; return 0;
err_remove: err_remove:
@ -1866,7 +1866,7 @@ nfsd4_umh_cltrack_init(struct net *net)
ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL); ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
kfree(grace_start); kfree(grace_start);
if (!ret) if (!ret)
pr_info("NFSD: Using UMH upcall client tracking operations.\n"); printk("NFSD: Using UMH upcall client tracking operations.\n");
return ret; return ret;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -84,6 +84,12 @@ nfsd_hashsize(unsigned int limit)
return roundup_pow_of_two(limit / TARGET_BUCKET_SIZE); return roundup_pow_of_two(limit / TARGET_BUCKET_SIZE);
} }
static u32
nfsd_cache_hash(__be32 xid, struct nfsd_net *nn)
{
return hash_32(be32_to_cpu(xid), nn->maskbits);
}
static struct svc_cacherep * static struct svc_cacherep *
nfsd_reply_cache_alloc(struct svc_rqst *rqstp, __wsum csum, nfsd_reply_cache_alloc(struct svc_rqst *rqstp, __wsum csum,
struct nfsd_net *nn) struct nfsd_net *nn)
@ -115,14 +121,14 @@ nfsd_reply_cache_free_locked(struct nfsd_drc_bucket *b, struct svc_cacherep *rp,
struct nfsd_net *nn) struct nfsd_net *nn)
{ {
if (rp->c_type == RC_REPLBUFF && rp->c_replvec.iov_base) { if (rp->c_type == RC_REPLBUFF && rp->c_replvec.iov_base) {
nfsd_stats_drc_mem_usage_sub(nn, rp->c_replvec.iov_len); nn->drc_mem_usage -= rp->c_replvec.iov_len;
kfree(rp->c_replvec.iov_base); kfree(rp->c_replvec.iov_base);
} }
if (rp->c_state != RC_UNUSED) { if (rp->c_state != RC_UNUSED) {
rb_erase(&rp->c_node, &b->rb_head); rb_erase(&rp->c_node, &b->rb_head);
list_del(&rp->c_lru); list_del(&rp->c_lru);
atomic_dec(&nn->num_drc_entries); atomic_dec(&nn->num_drc_entries);
nfsd_stats_drc_mem_usage_sub(nn, sizeof(*rp)); nn->drc_mem_usage -= sizeof(*rp);
} }
kmem_cache_free(drc_slab, rp); kmem_cache_free(drc_slab, rp);
} }
@ -148,16 +154,6 @@ void nfsd_drc_slab_free(void)
kmem_cache_destroy(drc_slab); kmem_cache_destroy(drc_slab);
} }
static int nfsd_reply_cache_stats_init(struct nfsd_net *nn)
{
return nfsd_percpu_counters_init(nn->counter, NFSD_NET_COUNTERS_NUM);
}
static void nfsd_reply_cache_stats_destroy(struct nfsd_net *nn)
{
nfsd_percpu_counters_destroy(nn->counter, NFSD_NET_COUNTERS_NUM);
}
int nfsd_reply_cache_init(struct nfsd_net *nn) int nfsd_reply_cache_init(struct nfsd_net *nn)
{ {
unsigned int hashsize; unsigned int hashsize;
@ -169,16 +165,12 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
hashsize = nfsd_hashsize(nn->max_drc_entries); hashsize = nfsd_hashsize(nn->max_drc_entries);
nn->maskbits = ilog2(hashsize); nn->maskbits = ilog2(hashsize);
status = nfsd_reply_cache_stats_init(nn);
if (status)
goto out_nomem;
nn->nfsd_reply_cache_shrinker.scan_objects = nfsd_reply_cache_scan; nn->nfsd_reply_cache_shrinker.scan_objects = nfsd_reply_cache_scan;
nn->nfsd_reply_cache_shrinker.count_objects = nfsd_reply_cache_count; nn->nfsd_reply_cache_shrinker.count_objects = nfsd_reply_cache_count;
nn->nfsd_reply_cache_shrinker.seeks = 1; nn->nfsd_reply_cache_shrinker.seeks = 1;
status = register_shrinker(&nn->nfsd_reply_cache_shrinker); status = register_shrinker(&nn->nfsd_reply_cache_shrinker);
if (status) if (status)
goto out_stats_destroy; goto out_nomem;
nn->drc_hashtbl = kvzalloc(array_size(hashsize, nn->drc_hashtbl = kvzalloc(array_size(hashsize,
sizeof(*nn->drc_hashtbl)), GFP_KERNEL); sizeof(*nn->drc_hashtbl)), GFP_KERNEL);
@ -194,8 +186,6 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
return 0; return 0;
out_shrinker: out_shrinker:
unregister_shrinker(&nn->nfsd_reply_cache_shrinker); unregister_shrinker(&nn->nfsd_reply_cache_shrinker);
out_stats_destroy:
nfsd_reply_cache_stats_destroy(nn);
out_nomem: out_nomem:
printk(KERN_ERR "nfsd: failed to allocate reply cache\n"); printk(KERN_ERR "nfsd: failed to allocate reply cache\n");
return -ENOMEM; return -ENOMEM;
@ -216,7 +206,6 @@ void nfsd_reply_cache_shutdown(struct nfsd_net *nn)
rp, nn); rp, nn);
} }
} }
nfsd_reply_cache_stats_destroy(nn);
kvfree(nn->drc_hashtbl); kvfree(nn->drc_hashtbl);
nn->drc_hashtbl = NULL; nn->drc_hashtbl = NULL;
@ -235,16 +224,8 @@ lru_put_end(struct nfsd_drc_bucket *b, struct svc_cacherep *rp)
list_move_tail(&rp->c_lru, &b->lru_head); list_move_tail(&rp->c_lru, &b->lru_head);
} }
static noinline struct nfsd_drc_bucket * static long
nfsd_cache_bucket_find(__be32 xid, struct nfsd_net *nn) prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn)
{
unsigned int hash = hash_32((__force u32)xid, nn->maskbits);
return &nn->drc_hashtbl[hash];
}
static long prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn,
unsigned int max)
{ {
struct svc_cacherep *rp, *tmp; struct svc_cacherep *rp, *tmp;
long freed = 0; long freed = 0;
@ -260,17 +241,11 @@ static long prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn,
time_before(jiffies, rp->c_timestamp + RC_EXPIRE)) time_before(jiffies, rp->c_timestamp + RC_EXPIRE))
break; break;
nfsd_reply_cache_free_locked(b, rp, nn); nfsd_reply_cache_free_locked(b, rp, nn);
if (max && freed++ > max) freed++;
break;
} }
return freed; return freed;
} }
static long nfsd_prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn)
{
return prune_bucket(b, nn, 3);
}
/* /*
* Walk the LRU list and prune off entries that are older than RC_EXPIRE. * Walk the LRU list and prune off entries that are older than RC_EXPIRE.
* Also prune the oldest ones when the total exceeds the max number of entries. * Also prune the oldest ones when the total exceeds the max number of entries.
@ -287,7 +262,7 @@ prune_cache_entries(struct nfsd_net *nn)
if (list_empty(&b->lru_head)) if (list_empty(&b->lru_head))
continue; continue;
spin_lock(&b->cache_lock); spin_lock(&b->cache_lock);
freed += prune_bucket(b, nn, 0); freed += prune_bucket(b, nn);
spin_unlock(&b->cache_lock); spin_unlock(&b->cache_lock);
} }
return freed; return freed;
@ -349,7 +324,7 @@ nfsd_cache_key_cmp(const struct svc_cacherep *key,
{ {
if (key->c_key.k_xid == rp->c_key.k_xid && if (key->c_key.k_xid == rp->c_key.k_xid &&
key->c_key.k_csum != rp->c_key.k_csum) { key->c_key.k_csum != rp->c_key.k_csum) {
nfsd_stats_payload_misses_inc(nn); ++nn->payload_misses;
trace_nfsd_drc_mismatch(nn, key, rp); trace_nfsd_drc_mismatch(nn, key, rp);
} }
@ -421,16 +396,18 @@ nfsd_cache_insert(struct nfsd_drc_bucket *b, struct svc_cacherep *key,
*/ */
int nfsd_cache_lookup(struct svc_rqst *rqstp) int nfsd_cache_lookup(struct svc_rqst *rqstp)
{ {
struct nfsd_net *nn; struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct svc_cacherep *rp, *found; struct svc_cacherep *rp, *found;
__be32 xid = rqstp->rq_xid;
__wsum csum; __wsum csum;
struct nfsd_drc_bucket *b; u32 hash = nfsd_cache_hash(xid, nn);
struct nfsd_drc_bucket *b = &nn->drc_hashtbl[hash];
int type = rqstp->rq_cachetype; int type = rqstp->rq_cachetype;
int rtn = RC_DOIT; int rtn = RC_DOIT;
rqstp->rq_cacherep = NULL; rqstp->rq_cacherep = NULL;
if (type == RC_NOCACHE) { if (type == RC_NOCACHE) {
nfsd_stats_rc_nocache_inc(); nfsdstats.rcnocache++;
goto out; goto out;
} }
@ -440,25 +417,27 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp)
* Since the common case is a cache miss followed by an insert, * Since the common case is a cache miss followed by an insert,
* preallocate an entry. * preallocate an entry.
*/ */
nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
rp = nfsd_reply_cache_alloc(rqstp, csum, nn); rp = nfsd_reply_cache_alloc(rqstp, csum, nn);
if (!rp) if (!rp)
goto out; goto out;
b = nfsd_cache_bucket_find(rqstp->rq_xid, nn);
spin_lock(&b->cache_lock); spin_lock(&b->cache_lock);
found = nfsd_cache_insert(b, rp, nn); found = nfsd_cache_insert(b, rp, nn);
if (found != rp) if (found != rp) {
nfsd_reply_cache_free_locked(NULL, rp, nn);
rp = found;
goto found_entry; goto found_entry;
}
nfsd_stats_rc_misses_inc(); nfsdstats.rcmisses++;
rqstp->rq_cacherep = rp; rqstp->rq_cacherep = rp;
rp->c_state = RC_INPROG; rp->c_state = RC_INPROG;
atomic_inc(&nn->num_drc_entries); atomic_inc(&nn->num_drc_entries);
nfsd_stats_drc_mem_usage_add(nn, sizeof(*rp)); nn->drc_mem_usage += sizeof(*rp);
nfsd_prune_bucket(b, nn); /* go ahead and prune the cache */
prune_bucket(b, nn);
out_unlock: out_unlock:
spin_unlock(&b->cache_lock); spin_unlock(&b->cache_lock);
@ -467,10 +446,8 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp)
found_entry: found_entry:
/* We found a matching entry which is either in progress or done. */ /* We found a matching entry which is either in progress or done. */
nfsd_reply_cache_free_locked(NULL, rp, nn); nfsdstats.rchits++;
nfsd_stats_rc_hits_inc();
rtn = RC_DROPIT; rtn = RC_DROPIT;
rp = found;
/* Request being processed */ /* Request being processed */
if (rp->c_state == RC_INPROG) if (rp->c_state == RC_INPROG)
@ -529,6 +506,7 @@ void nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct svc_cacherep *rp = rqstp->rq_cacherep; struct svc_cacherep *rp = rqstp->rq_cacherep;
struct kvec *resv = &rqstp->rq_res.head[0], *cachv; struct kvec *resv = &rqstp->rq_res.head[0], *cachv;
u32 hash;
struct nfsd_drc_bucket *b; struct nfsd_drc_bucket *b;
int len; int len;
size_t bufsize = 0; size_t bufsize = 0;
@ -536,7 +514,8 @@ void nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
if (!rp) if (!rp)
return; return;
b = nfsd_cache_bucket_find(rp->c_key.k_xid, nn); hash = nfsd_cache_hash(rp->c_key.k_xid, nn);
b = &nn->drc_hashtbl[hash];
len = resv->iov_len - ((char*)statp - (char*)resv->iov_base); len = resv->iov_len - ((char*)statp - (char*)resv->iov_base);
len >>= 2; len >>= 2;
@ -569,7 +548,7 @@ void nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
return; return;
} }
spin_lock(&b->cache_lock); spin_lock(&b->cache_lock);
nfsd_stats_drc_mem_usage_add(nn, bufsize); nn->drc_mem_usage += bufsize;
lru_put_end(b, rp); lru_put_end(b, rp);
rp->c_secure = test_bit(RQ_SECURE, &rqstp->rq_flags); rp->c_secure = test_bit(RQ_SECURE, &rqstp->rq_flags);
rp->c_type = cachetype; rp->c_type = cachetype;
@ -603,26 +582,28 @@ nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
* scraping this file for info should test the labels to ensure they're * scraping this file for info should test the labels to ensure they're
* getting the correct field. * getting the correct field.
*/ */
int nfsd_reply_cache_stats_show(struct seq_file *m, void *v) static int nfsd_reply_cache_stats_show(struct seq_file *m, void *v)
{ {
struct nfsd_net *nn = net_generic(file_inode(m->file)->i_sb->s_fs_info, struct nfsd_net *nn = m->private;
nfsd_net_id);
seq_printf(m, "max entries: %u\n", nn->max_drc_entries); seq_printf(m, "max entries: %u\n", nn->max_drc_entries);
seq_printf(m, "num entries: %u\n", seq_printf(m, "num entries: %u\n",
atomic_read(&nn->num_drc_entries)); atomic_read(&nn->num_drc_entries));
seq_printf(m, "hash buckets: %u\n", 1 << nn->maskbits); seq_printf(m, "hash buckets: %u\n", 1 << nn->maskbits);
seq_printf(m, "mem usage: %lld\n", seq_printf(m, "mem usage: %u\n", nn->drc_mem_usage);
percpu_counter_sum_positive(&nn->counter[NFSD_NET_DRC_MEM_USAGE])); seq_printf(m, "cache hits: %u\n", nfsdstats.rchits);
seq_printf(m, "cache hits: %lld\n", seq_printf(m, "cache misses: %u\n", nfsdstats.rcmisses);
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_HITS])); seq_printf(m, "not cached: %u\n", nfsdstats.rcnocache);
seq_printf(m, "cache misses: %lld\n", seq_printf(m, "payload misses: %u\n", nn->payload_misses);
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_MISSES]));
seq_printf(m, "not cached: %lld\n",
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_NOCACHE]));
seq_printf(m, "payload misses: %lld\n",
percpu_counter_sum_positive(&nn->counter[NFSD_NET_PAYLOAD_MISSES]));
seq_printf(m, "longest chain len: %u\n", nn->longest_chain); seq_printf(m, "longest chain len: %u\n", nn->longest_chain);
seq_printf(m, "cachesize at longest: %u\n", nn->longest_chain_cachesize); seq_printf(m, "cachesize at longest: %u\n", nn->longest_chain_cachesize);
return 0; return 0;
} }
int nfsd_reply_cache_stats_open(struct inode *inode, struct file *file)
{
struct nfsd_net *nn = net_generic(file_inode(file)->i_sb->s_fs_info,
nfsd_net_id);
return single_open(file, nfsd_reply_cache_stats_show, nn);
}

View File

@ -25,7 +25,6 @@
#include "state.h" #include "state.h"
#include "netns.h" #include "netns.h"
#include "pnfs.h" #include "pnfs.h"
#include "filecache.h"
/* /*
* We have a single directory with several nodes in it. * We have a single directory with several nodes in it.
@ -33,7 +32,6 @@
enum { enum {
NFSD_Root = 1, NFSD_Root = 1,
NFSD_List, NFSD_List,
NFSD_Export_Stats,
NFSD_Export_features, NFSD_Export_features,
NFSD_Fh, NFSD_Fh,
NFSD_FO_UnlockIP, NFSD_FO_UnlockIP,
@ -46,7 +44,6 @@ enum {
NFSD_Ports, NFSD_Ports,
NFSD_MaxBlkSize, NFSD_MaxBlkSize,
NFSD_MaxConnections, NFSD_MaxConnections,
NFSD_Filecache,
NFSD_SupportedEnctypes, NFSD_SupportedEnctypes,
/* /*
* The below MUST come last. Otherwise we leave a hole in nfsd_files[] * The below MUST come last. Otherwise we leave a hole in nfsd_files[]
@ -185,7 +182,17 @@ static int export_features_show(struct seq_file *m, void *v)
return 0; return 0;
} }
DEFINE_SHOW_ATTRIBUTE(export_features); static int export_features_open(struct inode *inode, struct file *file)
{
return single_open(file, export_features_show, NULL);
}
static const struct file_operations export_features_operations = {
.open = export_features_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE) #if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
static int supported_enctypes_show(struct seq_file *m, void *v) static int supported_enctypes_show(struct seq_file *m, void *v)
@ -194,7 +201,17 @@ static int supported_enctypes_show(struct seq_file *m, void *v)
return 0; return 0;
} }
DEFINE_SHOW_ATTRIBUTE(supported_enctypes); static int supported_enctypes_open(struct inode *inode, struct file *file)
{
return single_open(file, supported_enctypes_show, NULL);
}
static const struct file_operations supported_enctypes_ops = {
.open = supported_enctypes_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ #endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
static const struct file_operations pool_stats_operations = { static const struct file_operations pool_stats_operations = {
@ -204,9 +221,12 @@ static const struct file_operations pool_stats_operations = {
.release = nfsd_pool_stats_release, .release = nfsd_pool_stats_release,
}; };
DEFINE_SHOW_ATTRIBUTE(nfsd_reply_cache_stats); static const struct file_operations reply_cache_stats_operations = {
.open = nfsd_reply_cache_stats_open,
DEFINE_SHOW_ATTRIBUTE(nfsd_file_cache_stats); .read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
/* /*
@ -374,12 +394,12 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
auth_domain_put(dom); auth_domain_put(dom);
if (len) if (len)
return len; return len;
mesg = buf; mesg = buf;
len = SIMPLE_TRANSACTION_LIMIT; len = SIMPLE_TRANSACTION_LIMIT;
qword_addhex(&mesg, &len, fh.fh_raw, fh.fh_size); qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
mesg[-1] = '\n'; mesg[-1] = '\n';
return mesg - buf; return mesg - buf;
} }
/* /*
@ -581,9 +601,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET; cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET;
switch(num) { switch(num) {
#ifdef CONFIG_NFSD_V2
case 2: case 2:
#endif
case 3: case 3:
nfsd_vers(nn, num, cmd); nfsd_vers(nn, num, cmd);
break; break;
@ -603,9 +621,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
} }
break; break;
default: default:
/* Ignore requests to disable non-existent versions */ return -EINVAL;
if (cmd == NFSD_SET)
return -EINVAL;
} }
vers += len + 1; vers += len + 1;
} while ((len = qword_get(&mesg, vers, size)) > 0); } while ((len = qword_get(&mesg, vers, size)) > 0);
@ -616,6 +632,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
} }
/* Now write current state into reply buffer */ /* Now write current state into reply buffer */
len = 0;
sep = ""; sep = "";
remaining = SIMPLE_TRANSACTION_LIMIT; remaining = SIMPLE_TRANSACTION_LIMIT;
for (num=2 ; num <= 4 ; num++) { for (num=2 ; num <= 4 ; num++) {
@ -709,25 +726,28 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred
char *mesg = buf; char *mesg = buf;
int fd, err; int fd, err;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
err = get_int(&mesg, &fd); err = get_int(&mesg, &fd);
if (err != 0 || fd < 0) if (err != 0 || fd < 0)
return -EINVAL; return -EINVAL;
if (svc_alien_sock(net, fd)) {
printk(KERN_ERR "%s: socket net is different to NFSd's one\n", __func__);
return -EINVAL;
}
err = nfsd_create_serv(net); err = nfsd_create_serv(net);
if (err != 0) if (err != 0)
return err; return err;
serv = nn->nfsd_serv; err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
err = svc_addsock(serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); if (err < 0) {
nfsd_destroy(net);
return err;
}
if (err < 0 && !serv->sv_nrthreads && !nn->keep_active) /* Decrease the count, but don't shut down the service */
nfsd_last_thread(net); nn->nfsd_serv->sv_nrthreads--;
else if (err >= 0 && !serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
svc_get(serv);
svc_put(serv);
return err; return err;
} }
@ -741,7 +761,6 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
struct svc_xprt *xprt; struct svc_xprt *xprt;
int port, err; int port, err;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
if (sscanf(buf, "%15s %5u", transport, &port) != 2) if (sscanf(buf, "%15s %5u", transport, &port) != 2)
return -EINVAL; return -EINVAL;
@ -753,33 +772,30 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
if (err != 0) if (err != 0)
return err; return err;
serv = nn->nfsd_serv; err = svc_create_xprt(nn->nfsd_serv, transport, net,
err = svc_xprt_create(serv, transport, net, PF_INET, port, SVC_SOCK_ANONYMOUS, cred);
PF_INET, port, SVC_SOCK_ANONYMOUS, cred);
if (err < 0) if (err < 0)
goto out_err; goto out_err;
err = svc_xprt_create(serv, transport, net, err = svc_create_xprt(nn->nfsd_serv, transport, net,
PF_INET6, port, SVC_SOCK_ANONYMOUS, cred); PF_INET6, port, SVC_SOCK_ANONYMOUS, cred);
if (err < 0 && err != -EAFNOSUPPORT) if (err < 0 && err != -EAFNOSUPPORT)
goto out_close; goto out_close;
if (!serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) /* Decrease the count, but don't shut down the service */
svc_get(serv); nn->nfsd_serv->sv_nrthreads--;
svc_put(serv);
return 0; return 0;
out_close: out_close:
xprt = svc_find_xprt(serv, transport, net, PF_INET, port); xprt = svc_find_xprt(nn->nfsd_serv, transport, net, PF_INET, port);
if (xprt != NULL) { if (xprt != NULL) {
svc_xprt_close(xprt); svc_close_xprt(xprt);
svc_xprt_put(xprt); svc_xprt_put(xprt);
} }
out_err: out_err:
if (!serv->sv_nrthreads && !nn->keep_active) if (!list_empty(&nn->nfsd_serv->sv_permsocks))
nfsd_last_thread(net); nn->nfsd_serv->sv_nrthreads--;
else
svc_put(serv); nfsd_destroy(net);
return err; return err;
} }
@ -1152,7 +1168,6 @@ static struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode)
inode->i_fop = &simple_dir_operations; inode->i_fop = &simple_dir_operations;
inode->i_op = &simple_dir_inode_operations; inode->i_op = &simple_dir_inode_operations;
inc_nlink(inode); inc_nlink(inode);
break;
default: default:
break; break;
} }
@ -1254,8 +1269,7 @@ static void nfsdfs_remove_files(struct dentry *root)
/* XXX: cut'n'paste from simple_fill_super; figure out if we could share /* XXX: cut'n'paste from simple_fill_super; figure out if we could share
* code instead. */ * code instead. */
static int nfsdfs_create_files(struct dentry *root, static int nfsdfs_create_files(struct dentry *root,
const struct tree_descr *files, const struct tree_descr *files)
struct dentry **fdentries)
{ {
struct inode *dir = d_inode(root); struct inode *dir = d_inode(root);
struct inode *inode; struct inode *inode;
@ -1264,6 +1278,8 @@ static int nfsdfs_create_files(struct dentry *root,
inode_lock(dir); inode_lock(dir);
for (i = 0; files->name && files->name[0]; i++, files++) { for (i = 0; files->name && files->name[0]; i++, files++) {
if (!files->name)
continue;
dentry = d_alloc_name(root, files->name); dentry = d_alloc_name(root, files->name);
if (!dentry) if (!dentry)
goto out; goto out;
@ -1277,8 +1293,6 @@ static int nfsdfs_create_files(struct dentry *root,
inode->i_private = __get_nfsdfs_client(dir); inode->i_private = __get_nfsdfs_client(dir);
d_add(dentry, inode); d_add(dentry, inode);
fsnotify_create(dir, dentry); fsnotify_create(dir, dentry);
if (fdentries)
fdentries[i] = dentry;
} }
inode_unlock(dir); inode_unlock(dir);
return 0; return 0;
@ -1290,9 +1304,8 @@ static int nfsdfs_create_files(struct dentry *root,
/* on success, returns positive number unique to that client. */ /* on success, returns positive number unique to that client. */
struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
struct nfsdfs_client *ncl, u32 id, struct nfsdfs_client *ncl, u32 id,
const struct tree_descr *files, const struct tree_descr *files)
struct dentry **fdentries)
{ {
struct dentry *dentry; struct dentry *dentry;
char name[11]; char name[11];
@ -1303,7 +1316,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name); dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name);
if (IS_ERR(dentry)) /* XXX: tossing errors? */ if (IS_ERR(dentry)) /* XXX: tossing errors? */
return NULL; return NULL;
ret = nfsdfs_create_files(dentry, files, fdentries); ret = nfsdfs_create_files(dentry, files);
if (ret) { if (ret) {
nfsd_client_rmdir(dentry); nfsd_client_rmdir(dentry);
return NULL; return NULL;
@ -1339,10 +1352,8 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
static const struct tree_descr nfsd_files[] = { static const struct tree_descr nfsd_files[] = {
[NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO}, [NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO},
/* Per-export io stats use same ops as exports file */
[NFSD_Export_Stats] = {"export_stats", &exports_nfsd_operations, S_IRUGO},
[NFSD_Export_features] = {"export_features", [NFSD_Export_features] = {"export_features",
&export_features_fops, S_IRUGO}, &export_features_operations, S_IRUGO},
[NFSD_FO_UnlockIP] = {"unlock_ip", [NFSD_FO_UnlockIP] = {"unlock_ip",
&transaction_ops, S_IWUSR|S_IRUSR}, &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_FO_UnlockFS] = {"unlock_filesystem", [NFSD_FO_UnlockFS] = {"unlock_filesystem",
@ -1351,16 +1362,13 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO}, [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
[NFSD_Reply_Cache_Stats] = {"reply_cache_stats", [NFSD_Reply_Cache_Stats] = {"reply_cache_stats", &reply_cache_stats_operations, S_IRUGO},
&nfsd_reply_cache_stats_fops, S_IRUGO},
[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_Filecache] = {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO},
#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE) #if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
[NFSD_SupportedEnctypes] = {"supported_krb5_enctypes", [NFSD_SupportedEnctypes] = {"supported_krb5_enctypes", &supported_enctypes_ops, S_IRUGO},
&supported_enctypes_fops, S_IRUGO},
#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ #endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
@ -1460,16 +1468,25 @@ static __net_init int nfsd_init_net(struct net *net)
goto out_idmap_error; goto out_idmap_error;
nn->nfsd_versions = NULL; nn->nfsd_versions = NULL;
nn->nfsd4_minorversions = NULL; nn->nfsd4_minorversions = NULL;
nfsd4_init_leases_net(nn);
retval = nfsd_reply_cache_init(nn); retval = nfsd_reply_cache_init(nn);
if (retval) if (retval)
goto out_cache_error; goto out_drc_error;
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key)); nn->nfsd4_lease = 90; /* default lease time */
seqlock_init(&nn->writeverf_lock); nn->nfsd4_grace = 90;
nn->somebody_reclaimed = false;
nn->track_reclaim_completes = false;
nn->clverifier_counter = prandom_u32();
nn->clientid_base = prandom_u32();
nn->clientid_counter = nn->clientid_base + 1;
nn->s2s_cp_cl_id = nn->clientid_counter++;
atomic_set(&nn->ntf_refcnt, 0);
init_waitqueue_head(&nn->ntf_wq);
seqlock_init(&nn->boot_lock);
return 0; return 0;
out_cache_error: out_drc_error:
nfsd_idmap_shutdown(net); nfsd_idmap_shutdown(net);
out_idmap_error: out_idmap_error:
nfsd_export_shutdown(net); nfsd_export_shutdown(net);
@ -1497,6 +1514,7 @@ static struct pernet_operations nfsd_net_ops = {
static int __init init_nfsd(void) static int __init init_nfsd(void)
{ {
int retval; int retval;
printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
retval = nfsd4_init_slabs(); retval = nfsd4_init_slabs();
if (retval) if (retval)
@ -1504,9 +1522,7 @@ static int __init init_nfsd(void)
retval = nfsd4_init_pnfs(); retval = nfsd4_init_pnfs();
if (retval) if (retval)
goto out_free_slabs; goto out_free_slabs;
retval = nfsd_stat_init(); /* Statistics */ nfsd_stat_init(); /* Statistics */
if (retval)
goto out_free_pnfs;
retval = nfsd_drc_slab_create(); retval = nfsd_drc_slab_create();
if (retval) if (retval)
goto out_free_stat; goto out_free_stat;
@ -1514,25 +1530,20 @@ static int __init init_nfsd(void)
retval = create_proc_exports_entry(); retval = create_proc_exports_entry();
if (retval) if (retval)
goto out_free_lockd; goto out_free_lockd;
retval = register_filesystem(&nfsd_fs_type);
if (retval)
goto out_free_exports;
retval = register_pernet_subsys(&nfsd_net_ops); retval = register_pernet_subsys(&nfsd_net_ops);
if (retval < 0) if (retval < 0)
goto out_free_exports; goto out_free_filesystem;
retval = register_cld_notifier(); retval = register_cld_notifier();
if (retval)
goto out_free_subsys;
retval = nfsd4_create_laundry_wq();
if (retval)
goto out_free_cld;
retval = register_filesystem(&nfsd_fs_type);
if (retval) if (retval)
goto out_free_all; goto out_free_all;
return 0; return 0;
out_free_all: out_free_all:
nfsd4_destroy_laundry_wq();
out_free_cld:
unregister_cld_notifier();
out_free_subsys:
unregister_pernet_subsys(&nfsd_net_ops); unregister_pernet_subsys(&nfsd_net_ops);
out_free_filesystem:
unregister_filesystem(&nfsd_fs_type);
out_free_exports: out_free_exports:
remove_proc_entry("fs/nfs/exports", NULL); remove_proc_entry("fs/nfs/exports", NULL);
remove_proc_entry("fs/nfs", NULL); remove_proc_entry("fs/nfs", NULL);
@ -1541,7 +1552,6 @@ static int __init init_nfsd(void)
nfsd_drc_slab_free(); nfsd_drc_slab_free();
out_free_stat: out_free_stat:
nfsd_stat_shutdown(); nfsd_stat_shutdown();
out_free_pnfs:
nfsd4_exit_pnfs(); nfsd4_exit_pnfs();
out_free_slabs: out_free_slabs:
nfsd4_free_slabs(); nfsd4_free_slabs();
@ -1550,8 +1560,6 @@ static int __init init_nfsd(void)
static void __exit exit_nfsd(void) static void __exit exit_nfsd(void)
{ {
unregister_filesystem(&nfsd_fs_type);
nfsd4_destroy_laundry_wq();
unregister_cld_notifier(); unregister_cld_notifier();
unregister_pernet_subsys(&nfsd_net_ops); unregister_pernet_subsys(&nfsd_net_ops);
nfsd_drc_slab_free(); nfsd_drc_slab_free();
@ -1561,6 +1569,7 @@ static void __exit exit_nfsd(void)
nfsd_lockd_shutdown(); nfsd_lockd_shutdown();
nfsd4_free_slabs(); nfsd4_free_slabs();
nfsd4_exit_pnfs(); nfsd4_exit_pnfs();
unregister_filesystem(&nfsd_fs_type);
} }
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");

View File

@ -24,8 +24,8 @@
#include <uapi/linux/nfsd/debug.h> #include <uapi/linux/nfsd/debug.h>
#include "netns.h" #include "netns.h"
#include "export.h"
#include "stats.h" #include "stats.h"
#include "export.h"
#undef ifdebug #undef ifdebug
#ifdef CONFIG_SUNRPC_DEBUG #ifdef CONFIG_SUNRPC_DEBUG
@ -64,7 +64,8 @@ struct readdir_cd {
extern struct svc_program nfsd_program; extern struct svc_program nfsd_program;
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4; extern const struct svc_version nfsd_version2, nfsd_version3,
nfsd_version4;
extern struct mutex nfsd_mutex; extern struct mutex nfsd_mutex;
extern spinlock_t nfsd_drc_lock; extern spinlock_t nfsd_drc_lock;
extern unsigned long nfsd_drc_max_mem; extern unsigned long nfsd_drc_max_mem;
@ -72,16 +73,6 @@ extern unsigned long nfsd_drc_mem_used;
extern const struct seq_operations nfs_exports_op; extern const struct seq_operations nfs_exports_op;
/*
* Common void argument and result helpers
*/
struct nfsd_voidargs { };
struct nfsd_voidres { };
bool nfssvc_decode_voidarg(struct svc_rqst *rqstp,
struct xdr_stream *xdr);
bool nfssvc_encode_voidres(struct svc_rqst *rqstp,
struct xdr_stream *xdr);
/* /*
* Function prototypes. * Function prototypes.
*/ */
@ -96,6 +87,8 @@ int nfsd_pool_stats_open(struct inode *, struct file *);
int nfsd_pool_stats_release(struct inode *, struct file *); int nfsd_pool_stats_release(struct inode *, struct file *);
void nfsd_shutdown_threads(struct net *net); void nfsd_shutdown_threads(struct net *net);
void nfsd_destroy(struct net *net);
bool i_am_nfsd(void); bool i_am_nfsd(void);
struct nfsdfs_client { struct nfsdfs_client {
@ -105,9 +98,7 @@ struct nfsdfs_client {
struct nfsdfs_client *get_nfsdfs_client(struct inode *); struct nfsdfs_client *get_nfsdfs_client(struct inode *);
struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
struct nfsdfs_client *ncl, u32 id, struct nfsdfs_client *ncl, u32 id, const struct tree_descr *);
const struct tree_descr *,
struct dentry **fdentries);
void nfsd_client_rmdir(struct dentry *dentry); void nfsd_client_rmdir(struct dentry *dentry);
@ -131,7 +122,6 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change); int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
void nfsd_reset_versions(struct nfsd_net *nn); void nfsd_reset_versions(struct nfsd_net *nn);
int nfsd_create_serv(struct net *net); int nfsd_create_serv(struct net *net);
void nfsd_last_thread(struct net *net);
extern int nfsd_max_blksize; extern int nfsd_max_blksize;
@ -160,9 +150,6 @@ void nfs4_state_shutdown_net(struct net *net);
int nfs4_reset_recoverydir(char *recdir); int nfs4_reset_recoverydir(char *recdir);
char * nfs4_recoverydir(void); char * nfs4_recoverydir(void);
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp); bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
int nfsd4_create_laundry_wq(void);
void nfsd4_destroy_laundry_wq(void);
bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode);
#else #else
static inline int nfsd4_init_slabs(void) { return 0; } static inline int nfsd4_init_slabs(void) { return 0; }
static inline void nfsd4_free_slabs(void) { } static inline void nfsd4_free_slabs(void) { }
@ -176,13 +163,6 @@ static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
{ {
return false; return false;
} }
static inline int nfsd4_create_laundry_wq(void) { return 0; };
static inline void nfsd4_destroy_laundry_wq(void) {};
static inline bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp,
struct inode *inode)
{
return false;
}
#endif #endif
/* /*
@ -344,10 +324,6 @@ void nfsd_lockd_shutdown(void);
#define COMPOUND_ERR_SLACK_SPACE 16 /* OP_SETATTR */ #define COMPOUND_ERR_SLACK_SPACE 16 /* OP_SETATTR */
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */ #define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
#define NFSD_COURTESY_CLIENT_TIMEOUT (24 * 60 * 60) /* seconds */
#define NFSD_CLIENT_MAX_TRIM_PER_RUN 128
#define NFS4_CLIENTS_PER_GB 1024
#define NFSD_DELEGRETURN_TIMEOUT (HZ / 34) /* 30ms */
/* /*
* The following attributes are currently not supported by the NFSv4 server: * The following attributes are currently not supported by the NFSv4 server:
@ -376,7 +352,7 @@ void nfsd_lockd_shutdown(void);
| FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \ | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \
| FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \ | FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \
| FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \ | FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \
| FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_CREATE \ | FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA \
| FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID) | FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
#define NFSD4_SUPPORTED_ATTRS_WORD2 0 #define NFSD4_SUPPORTED_ATTRS_WORD2 0
@ -410,6 +386,7 @@ void nfsd_lockd_shutdown(void);
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \ #define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \ (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_CHANGE_ATTR_TYPE | \
FATTR4_WORD2_MODE_UMASK | \ FATTR4_WORD2_MODE_UMASK | \
NFSD4_2_SECURITY_ATTRS | \ NFSD4_2_SECURITY_ATTRS | \
FATTR4_WORD2_XATTR_SUPPORT) FATTR4_WORD2_XATTR_SUPPORT)
@ -472,8 +449,7 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL) (FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
#define NFSD_WRITEABLE_ATTRS_WORD1 \ #define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_CREATE \ | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
| FATTR4_WORD1_TIME_MODIFY_SET)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL \ #define MAYBE_FATTR4_WORD2_SECURITY_LABEL \
FATTR4_WORD2_SECURITY_LABEL FATTR4_WORD2_SECURITY_LABEL
@ -499,20 +475,12 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
extern int nfsd4_is_junction(struct dentry *dentry); extern int nfsd4_is_junction(struct dentry *dentry);
extern int register_cld_notifier(void); extern int register_cld_notifier(void);
extern void unregister_cld_notifier(void); extern void unregister_cld_notifier(void);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn);
#endif
extern void nfsd4_init_leases_net(struct nfsd_net *nn);
#else /* CONFIG_NFSD_V4 */ #else /* CONFIG_NFSD_V4 */
static inline int nfsd4_is_junction(struct dentry *dentry) static inline int nfsd4_is_junction(struct dentry *dentry)
{ {
return 0; return 0;
} }
static inline void nfsd4_init_leases_net(struct nfsd_net *nn) { };
#define register_cld_notifier() 0 #define register_cld_notifier() 0
#define unregister_cld_notifier() do { } while(0) #define unregister_cld_notifier() do { } while(0)

View File

@ -153,12 +153,11 @@ static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
{ {
struct knfsd_fh *fh = &fhp->fh_handle; struct knfsd_fh *fh = &fhp->fh_handle;
struct fid *fid = NULL; struct fid *fid = NULL, sfid;
struct svc_export *exp; struct svc_export *exp;
struct dentry *dentry; struct dentry *dentry;
int fileid_type; int fileid_type;
int data_left = fh->fh_size/4; int data_left = fh->fh_size/4;
int len;
__be32 error; __be32 error;
error = nfserr_stale; error = nfserr_stale;
@ -167,35 +166,48 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
if (rqstp->rq_vers == 4 && fh->fh_size == 0) if (rqstp->rq_vers == 4 && fh->fh_size == 0)
return nfserr_nofilehandle; return nfserr_nofilehandle;
if (fh->fh_version != 1) if (fh->fh_version == 1) {
return error; int len;
if (--data_left < 0) if (--data_left < 0)
return error; return error;
if (fh->fh_auth_type != 0) if (fh->fh_auth_type != 0)
return error; return error;
len = key_len(fh->fh_fsid_type) / 4; len = key_len(fh->fh_fsid_type) / 4;
if (len == 0) if (len == 0)
return error; return error;
if (fh->fh_fsid_type == FSID_MAJOR_MINOR) { if (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
/* deprecated, convert to type 3 */ /* deprecated, convert to type 3 */
len = key_len(FSID_ENCODE_DEV)/4; len = key_len(FSID_ENCODE_DEV)/4;
fh->fh_fsid_type = FSID_ENCODE_DEV; fh->fh_fsid_type = FSID_ENCODE_DEV;
/* /*
* struct knfsd_fh uses host-endian fields, which are * struct knfsd_fh uses host-endian fields, which are
* sometimes used to hold net-endian values. This * sometimes used to hold net-endian values. This
* confuses sparse, so we must use __force here to * confuses sparse, so we must use __force here to
* keep it from complaining. * keep it from complaining.
*/ */
fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl((__force __be32)fh->fh_fsid[0]), fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl((__force __be32)fh->fh_fsid[0]),
ntohl((__force __be32)fh->fh_fsid[1]))); ntohl((__force __be32)fh->fh_fsid[1])));
fh->fh_fsid[1] = fh->fh_fsid[2]; fh->fh_fsid[1] = fh->fh_fsid[2];
}
data_left -= len;
if (data_left < 0)
return error;
exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid);
fid = (struct fid *)(fh->fh_fsid + len);
} else {
__u32 tfh[2];
dev_t xdev;
ino_t xino;
if (fh->fh_size != NFS_FHSIZE)
return error;
/* assume old filehandle format */
xdev = old_decode_dev(fh->ofh_xdev);
xino = u32_to_ino_t(fh->ofh_xino);
mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
} }
data_left -= len;
if (data_left < 0)
return error;
exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid);
fid = (struct fid *)(fh->fh_fsid + len);
error = nfserr_stale; error = nfserr_stale;
if (IS_ERR(exp)) { if (IS_ERR(exp)) {
@ -240,25 +252,28 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
if (rqstp->rq_vers > 2) if (rqstp->rq_vers > 2)
error = nfserr_badhandle; error = nfserr_badhandle;
fileid_type = fh->fh_fileid_type; if (fh->fh_version != 1) {
sfid.i32.ino = fh->ofh_ino;
sfid.i32.gen = fh->ofh_generation;
sfid.i32.parent_ino = fh->ofh_dirino;
fid = &sfid;
data_left = 3;
if (fh->ofh_dirino == 0)
fileid_type = FILEID_INO32_GEN;
else
fileid_type = FILEID_INO32_GEN_PARENT;
} else
fileid_type = fh->fh_fileid_type;
if (fileid_type == FILEID_ROOT) if (fileid_type == FILEID_ROOT)
dentry = dget(exp->ex_path.dentry); dentry = dget(exp->ex_path.dentry);
else { else {
dentry = exportfs_decode_fh_raw(exp->ex_path.mnt, fid, dentry = exportfs_decode_fh(exp->ex_path.mnt, fid,
data_left, fileid_type, data_left, fileid_type,
nfsd_acceptable, exp); nfsd_acceptable, exp);
if (IS_ERR_OR_NULL(dentry)) { if (IS_ERR_OR_NULL(dentry))
trace_nfsd_set_fh_dentry_badhandle(rqstp, fhp, trace_nfsd_set_fh_dentry_badhandle(rqstp, fhp,
dentry ? PTR_ERR(dentry) : -ESTALE); dentry ? PTR_ERR(dentry) : -ESTALE);
switch (PTR_ERR(dentry)) {
case -ENOMEM:
case -ETIMEDOUT:
break;
default:
dentry = ERR_PTR(-ESTALE);
}
}
} }
if (dentry == NULL) if (dentry == NULL)
goto out; goto out;
@ -276,20 +291,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
fhp->fh_dentry = dentry; fhp->fh_dentry = dentry;
fhp->fh_export = exp; fhp->fh_export = exp;
switch (rqstp->rq_vers) {
case 4:
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
fhp->fh_no_atomic_attr = true;
break;
case 3:
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC)
fhp->fh_no_wcc = true;
break;
case 2:
fhp->fh_no_wcc = true;
}
return 0; return 0;
out: out:
exp_put(exp); exp_put(exp);
@ -326,7 +327,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
__be32 __be32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access) fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
{ {
struct svc_export *exp = NULL; struct svc_export *exp;
struct dentry *dentry; struct dentry *dentry;
__be32 error; __be32 error;
@ -399,7 +400,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
} }
out: out:
if (error == nfserr_stale) if (error == nfserr_stale)
nfsd_stats_fh_stale_inc(exp); nfsdstats.fh_stale++;
return error; return error;
} }
@ -428,6 +429,20 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
} }
} }
/*
* for composing old style file handles
*/
static inline void _fh_update_old(struct dentry *dentry,
struct svc_export *exp,
struct knfsd_fh *fh)
{
fh->ofh_ino = ino_t_to_u32(d_inode(dentry)->i_ino);
fh->ofh_generation = d_inode(dentry)->i_generation;
if (d_is_dir(dentry) ||
(exp->ex_flags & NFSEXP_NOSUBTREECHECK))
fh->ofh_dirino = 0;
}
static bool is_root_export(struct svc_export *exp) static bool is_root_export(struct svc_export *exp)
{ {
return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root; return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root;
@ -524,6 +539,9 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
/* ref_fh is a reference file handle. /* ref_fh is a reference file handle.
* if it is non-null and for the same filesystem, then we should compose * if it is non-null and for the same filesystem, then we should compose
* a filehandle which is of the same version, where possible. * a filehandle which is of the same version, where possible.
* Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
* Then create a 32byte filehandle using nfs_fhbase_old
*
*/ */
struct inode * inode = d_inode(dentry); struct inode * inode = d_inode(dentry);
@ -541,13 +559,10 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
*/ */
set_version_and_fsid_type(fhp, exp, ref_fh); set_version_and_fsid_type(fhp, exp, ref_fh);
/* If we have a ref_fh, then copy the fh_no_wcc setting from it. */
fhp->fh_no_wcc = ref_fh ? ref_fh->fh_no_wcc : false;
if (ref_fh == fhp) if (ref_fh == fhp)
fh_put(ref_fh); fh_put(ref_fh);
if (fhp->fh_dentry) { if (fhp->fh_locked || fhp->fh_dentry) {
printk(KERN_ERR "fh_compose: fh %pd2 not initialized!\n", printk(KERN_ERR "fh_compose: fh %pd2 not initialized!\n",
dentry); dentry);
} }
@ -559,21 +574,35 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
fhp->fh_dentry = dget(dentry); /* our internal copy */ fhp->fh_dentry = dget(dentry); /* our internal copy */
fhp->fh_export = exp_get(exp); fhp->fh_export = exp_get(exp);
fhp->fh_handle.fh_size = if (fhp->fh_handle.fh_version == 0xca) {
key_len(fhp->fh_handle.fh_fsid_type) + 4; /* old style filehandle please */
fhp->fh_handle.fh_auth_type = 0; memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
fhp->fh_handle.fh_size = NFS_FHSIZE;
fhp->fh_handle.ofh_dcookie = 0xfeebbaca;
fhp->fh_handle.ofh_dev = old_encode_dev(ex_dev);
fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
fhp->fh_handle.ofh_xino =
ino_t_to_u32(d_inode(exp->ex_path.dentry)->i_ino);
fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
if (inode)
_fh_update_old(dentry, exp, &fhp->fh_handle);
} else {
fhp->fh_handle.fh_size =
key_len(fhp->fh_handle.fh_fsid_type) + 4;
fhp->fh_handle.fh_auth_type = 0;
mk_fsid(fhp->fh_handle.fh_fsid_type, mk_fsid(fhp->fh_handle.fh_fsid_type,
fhp->fh_handle.fh_fsid, fhp->fh_handle.fh_fsid,
ex_dev, ex_dev,
d_inode(exp->ex_path.dentry)->i_ino, d_inode(exp->ex_path.dentry)->i_ino,
exp->ex_fsid, exp->ex_uuid); exp->ex_fsid, exp->ex_uuid);
if (inode) if (inode)
_fh_update(fhp, exp, dentry); _fh_update(fhp, exp, dentry);
if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) { if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) {
fh_put(fhp); fh_put(fhp);
return nfserr_opnotsupp; return nfserr_opnotsupp;
}
} }
return 0; return 0;
@ -594,12 +623,16 @@ fh_update(struct svc_fh *fhp)
dentry = fhp->fh_dentry; dentry = fhp->fh_dentry;
if (d_really_is_negative(dentry)) if (d_really_is_negative(dentry))
goto out_negative; goto out_negative;
if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT) if (fhp->fh_handle.fh_version != 1) {
return 0; _fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
} else {
if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT)
return 0;
_fh_update(fhp, fhp->fh_export, dentry); _fh_update(fhp, fhp->fh_export, dentry);
if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID)
return nfserr_opnotsupp; return nfserr_opnotsupp;
}
return 0; return 0;
out_bad: out_bad:
printk(KERN_ERR "fh_update: fh not verified!\n"); printk(KERN_ERR "fh_update: fh not verified!\n");
@ -610,85 +643,6 @@ fh_update(struct svc_fh *fhp)
return nfserr_serverfault; return nfserr_serverfault;
} }
/**
* fh_fill_pre_attrs - Fill in pre-op attributes
* @fhp: file handle to be updated
*
*/
void fh_fill_pre_attrs(struct svc_fh *fhp)
{
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
struct inode *inode;
struct kstat stat;
__be32 err;
if (fhp->fh_no_wcc || fhp->fh_pre_saved)
return;
inode = d_inode(fhp->fh_dentry);
err = fh_getattr(fhp, &stat);
if (err) {
/* Grab the times from inode anyway */
stat.mtime = inode->i_mtime;
stat.ctime = inode->i_ctime;
stat.size = inode->i_size;
}
if (v4)
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
fhp->fh_pre_mtime = stat.mtime;
fhp->fh_pre_ctime = stat.ctime;
fhp->fh_pre_size = stat.size;
fhp->fh_pre_saved = true;
}
/**
* fh_fill_post_attrs - Fill in post-op attributes
* @fhp: file handle to be updated
*
*/
void fh_fill_post_attrs(struct svc_fh *fhp)
{
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
struct inode *inode = d_inode(fhp->fh_dentry);
__be32 err;
if (fhp->fh_no_wcc)
return;
if (fhp->fh_post_saved)
printk("nfsd: inode locked twice during operation.\n");
err = fh_getattr(fhp, &fhp->fh_post_attr);
if (err) {
fhp->fh_post_saved = false;
fhp->fh_post_attr.ctime = inode->i_ctime;
} else
fhp->fh_post_saved = true;
if (v4)
fhp->fh_post_change =
nfsd4_change_attribute(&fhp->fh_post_attr, inode);
}
/**
* fh_fill_both_attrs - Fill pre-op and post-op attributes
* @fhp: file handle to be updated
*
* This is used when the directory wasn't changed, but wcc attributes
* are needed anyway.
*/
void fh_fill_both_attrs(struct svc_fh *fhp)
{
fh_fill_post_attrs(fhp);
if (!fhp->fh_post_saved)
return;
fhp->fh_pre_change = fhp->fh_post_change;
fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
fhp->fh_pre_size = fhp->fh_post_attr.size;
fhp->fh_pre_saved = true;
}
/* /*
* Release a file handle. * Release a file handle.
*/ */
@ -698,16 +652,16 @@ fh_put(struct svc_fh *fhp)
struct dentry * dentry = fhp->fh_dentry; struct dentry * dentry = fhp->fh_dentry;
struct svc_export * exp = fhp->fh_export; struct svc_export * exp = fhp->fh_export;
if (dentry) { if (dentry) {
fh_unlock(fhp);
fhp->fh_dentry = NULL; fhp->fh_dentry = NULL;
dput(dentry); dput(dentry);
fh_clear_pre_post_attrs(fhp); fh_clear_wcc(fhp);
} }
fh_drop_write(fhp); fh_drop_write(fhp);
if (exp) { if (exp) {
exp_put(exp); exp_put(exp);
fhp->fh_export = NULL; fhp->fh_export = NULL;
} }
fhp->fh_no_wcc = false;
return; return;
} }
@ -717,15 +671,20 @@ fh_put(struct svc_fh *fhp)
char * SVCFH_fmt(struct svc_fh *fhp) char * SVCFH_fmt(struct svc_fh *fhp)
{ {
struct knfsd_fh *fh = &fhp->fh_handle; struct knfsd_fh *fh = &fhp->fh_handle;
static char buf[2+1+1+64*3+1];
if (fh->fh_size < 0 || fh->fh_size> 64) static char buf[80];
return "bad-fh"; sprintf(buf, "%d: %08x %08x %08x %08x %08x %08x",
sprintf(buf, "%d: %*ph", fh->fh_size, fh->fh_size, fh->fh_raw); fh->fh_size,
fh->fh_base.fh_pad[0],
fh->fh_base.fh_pad[1],
fh->fh_base.fh_pad[2],
fh->fh_base.fh_pad[3],
fh->fh_base.fh_pad[4],
fh->fh_base.fh_pad[5]);
return buf; return buf;
} }
enum fsid_source fsid_source(const struct svc_fh *fhp) enum fsid_source fsid_source(struct svc_fh *fhp)
{ {
if (fhp->fh_handle.fh_version != 1) if (fhp->fh_handle.fh_version != 1)
return FSIDSOURCE_DEV; return FSIDSOURCE_DEV;

View File

@ -10,56 +10,8 @@
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <uapi/linux/nfsd/nfsfh.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include <linux/exportfs.h>
#include <linux/nfs4.h>
/*
* The file handle starts with a sequence of four-byte words.
* The first word contains a version number (1) and three descriptor bytes
* that tell how the remaining 3 variable length fields should be handled.
* These three bytes are auth_type, fsid_type and fileid_type.
*
* All four-byte values are in host-byte-order.
*
* The auth_type field is deprecated and must be set to 0.
*
* The fsid_type identifies how the filesystem (or export point) is
* encoded.
* Current values:
* 0 - 4 byte device id (ms-2-bytes major, ls-2-bytes minor), 4byte inode number
* NOTE: we cannot use the kdev_t device id value, because kdev_t.h
* says we mustn't. We must break it up and reassemble.
* 1 - 4 byte user specified identifier
* 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED
* 3 - 4 byte device id, encoded for user-space, 4 byte inode number
* 4 - 4 byte inode number and 4 byte uuid
* 5 - 8 byte uuid
* 6 - 16 byte uuid
* 7 - 8 byte inode number and 16 byte uuid
*
* The fileid_type identifies how the file within the filesystem is encoded.
* The values for this field are filesystem specific, exccept that
* filesystems must not use the values '0' or '0xff'. 'See enum fid_type'
* in include/linux/exportfs.h for currently registered values.
*/
struct knfsd_fh {
unsigned int fh_size; /*
* Points to the current size while
* building a new file handle.
*/
union {
char fh_raw[NFS4_FHSIZE];
struct {
u8 fh_version; /* == 1 */
u8 fh_auth_type; /* deprecated */
u8 fh_fsid_type;
u8 fh_fileid_type;
u32 fh_fsid[]; /* flexible-array member */
};
};
};
static inline __u32 ino_t_to_u32(ino_t ino) static inline __u32 ino_t_to_u32(ino_t ino)
{ {
@ -81,18 +33,14 @@ typedef struct svc_fh {
struct dentry * fh_dentry; /* validated dentry */ struct dentry * fh_dentry; /* validated dentry */
struct svc_export * fh_export; /* export pointer */ struct svc_export * fh_export; /* export pointer */
bool fh_locked; /* inode locked by us */
bool fh_want_write; /* remount protection taken */ bool fh_want_write; /* remount protection taken */
bool fh_no_wcc; /* no wcc data needed */
bool fh_no_atomic_attr;
/*
* wcc data is not atomic with
* operation
*/
int fh_flags; /* FH flags */ int fh_flags; /* FH flags */
#ifdef CONFIG_NFSD_V3
bool fh_post_saved; /* post-op attrs saved */ bool fh_post_saved; /* post-op attrs saved */
bool fh_pre_saved; /* pre-op attrs saved */ bool fh_pre_saved; /* pre-op attrs saved */
/* Pre-op attributes saved when inode is locked */ /* Pre-op attributes saved during fh_lock */
__u64 fh_pre_size; /* size before operation */ __u64 fh_pre_size; /* size before operation */
struct timespec64 fh_pre_mtime; /* mtime before oper */ struct timespec64 fh_pre_mtime; /* mtime before oper */
struct timespec64 fh_pre_ctime; /* ctime before oper */ struct timespec64 fh_pre_ctime; /* ctime before oper */
@ -102,9 +50,11 @@ typedef struct svc_fh {
*/ */
u64 fh_pre_change; u64 fh_pre_change;
/* Post-op attributes saved in fh_fill_post_attrs() */ /* Post-op attributes saved in fh_unlock */
struct kstat fh_post_attr; /* full attrs after operation */ struct kstat fh_post_attr; /* full attrs after operation */
u64 fh_post_change; /* nfsv4 change; see above */ u64 fh_post_change; /* nfsv4 change; see above */
#endif /* CONFIG_NFSD_V3 */
} svc_fh; } svc_fh;
#define NFSD4_FH_FOREIGN (1<<0) #define NFSD4_FH_FOREIGN (1<<0)
#define SET_FH_FLAG(c, f) ((c)->fh_flags |= (f)) #define SET_FH_FLAG(c, f) ((c)->fh_flags |= (f))
@ -126,7 +76,7 @@ enum fsid_source {
FSIDSOURCE_FSID, FSIDSOURCE_FSID,
FSIDSOURCE_UUID, FSIDSOURCE_UUID,
}; };
extern enum fsid_source fsid_source(const struct svc_fh *fhp); extern enum fsid_source fsid_source(struct svc_fh *fhp);
/* /*
@ -220,19 +170,19 @@ __be32 fh_update(struct svc_fh *);
void fh_put(struct svc_fh *); void fh_put(struct svc_fh *);
static __inline__ struct svc_fh * static __inline__ struct svc_fh *
fh_copy(struct svc_fh *dst, const struct svc_fh *src) fh_copy(struct svc_fh *dst, struct svc_fh *src)
{ {
WARN_ON(src->fh_dentry); WARN_ON(src->fh_dentry || src->fh_locked);
*dst = *src; *dst = *src;
return dst; return dst;
} }
static inline void static inline void
fh_copy_shallow(struct knfsd_fh *dst, const struct knfsd_fh *src) fh_copy_shallow(struct knfsd_fh *dst, struct knfsd_fh *src)
{ {
dst->fh_size = src->fh_size; dst->fh_size = src->fh_size;
memcpy(&dst->fh_raw, &src->fh_raw, src->fh_size); memcpy(&dst->fh_base, &src->fh_base, src->fh_size);
} }
static __inline__ struct svc_fh * static __inline__ struct svc_fh *
@ -243,18 +193,16 @@ fh_init(struct svc_fh *fhp, int maxsize)
return fhp; return fhp;
} }
static inline bool fh_match(const struct knfsd_fh *fh1, static inline bool fh_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
const struct knfsd_fh *fh2)
{ {
if (fh1->fh_size != fh2->fh_size) if (fh1->fh_size != fh2->fh_size)
return false; return false;
if (memcmp(fh1->fh_raw, fh2->fh_raw, fh1->fh_size) != 0) if (memcmp(fh1->fh_base.fh_pad, fh2->fh_base.fh_pad, fh1->fh_size) != 0)
return false; return false;
return true; return true;
} }
static inline bool fh_fsid_match(const struct knfsd_fh *fh1, static inline bool fh_fsid_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
const struct knfsd_fh *fh2)
{ {
if (fh1->fh_fsid_type != fh2->fh_fsid_type) if (fh1->fh_fsid_type != fh2->fh_fsid_type)
return false; return false;
@ -271,23 +219,27 @@ static inline bool fh_fsid_match(const struct knfsd_fh *fh1,
* returns a crc32 hash for the filehandle that is compatible with * returns a crc32 hash for the filehandle that is compatible with
* the one displayed by "wireshark". * the one displayed by "wireshark".
*/ */
static inline u32 knfsd_fh_hash(const struct knfsd_fh *fh)
static inline u32
knfsd_fh_hash(struct knfsd_fh *fh)
{ {
return ~crc32_le(0xFFFFFFFF, fh->fh_raw, fh->fh_size); return ~crc32_le(0xFFFFFFFF, (unsigned char *)&fh->fh_base, fh->fh_size);
} }
#else #else
static inline u32 knfsd_fh_hash(const struct knfsd_fh *fh) static inline u32
knfsd_fh_hash(struct knfsd_fh *fh)
{ {
return 0; return 0;
} }
#endif #endif
/** #ifdef CONFIG_NFSD_V3
* fh_clear_pre_post_attrs - Reset pre/post attributes /*
* @fhp: file handle to be updated * The wcc data stored in current_fh should be cleared
* * between compound ops.
*/ */
static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp) static inline void
fh_clear_wcc(struct svc_fh *fhp)
{ {
fhp->fh_post_saved = false; fhp->fh_post_saved = false;
fhp->fh_pre_saved = false; fhp->fh_pre_saved = false;
@ -307,21 +259,68 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
static inline u64 nfsd4_change_attribute(struct kstat *stat, static inline u64 nfsd4_change_attribute(struct kstat *stat,
struct inode *inode) struct inode *inode)
{ {
if (inode->i_sb->s_export_op->fetch_iversion) u64 chattr;
return inode->i_sb->s_export_op->fetch_iversion(inode);
else if (IS_I_VERSION(inode)) {
u64 chattr;
chattr = stat->ctime.tv_sec; chattr = stat->ctime.tv_sec;
chattr <<= 30; chattr <<= 30;
chattr += stat->ctime.tv_nsec; chattr += stat->ctime.tv_nsec;
chattr += inode_query_iversion(inode); chattr += inode_query_iversion(inode);
return chattr; return chattr;
} else }
return time_to_chattr(&stat->ctime);
extern void fill_pre_wcc(struct svc_fh *fhp);
extern void fill_post_wcc(struct svc_fh *fhp);
#else
#define fh_clear_wcc(ignored)
#define fill_pre_wcc(ignored)
#define fill_post_wcc(notused)
#endif /* CONFIG_NFSD_V3 */
/*
* Lock a file handle/inode
* NOTE: both fh_lock and fh_unlock are done "by hand" in
* vfs.c:nfsd_rename as it needs to grab 2 i_mutex's at once
* so, any changes here should be reflected there.
*/
static inline void
fh_lock_nested(struct svc_fh *fhp, unsigned int subclass)
{
struct dentry *dentry = fhp->fh_dentry;
struct inode *inode;
BUG_ON(!dentry);
if (fhp->fh_locked) {
printk(KERN_WARNING "fh_lock: %pd2 already locked!\n",
dentry);
return;
}
inode = d_inode(dentry);
inode_lock_nested(inode, subclass);
fill_pre_wcc(fhp);
fhp->fh_locked = true;
}
static inline void
fh_lock(struct svc_fh *fhp)
{
fh_lock_nested(fhp, I_MUTEX_NORMAL);
}
/*
* Unlock a file handle/inode
*/
static inline void
fh_unlock(struct svc_fh *fhp)
{
if (fhp->fh_locked) {
fill_post_wcc(fhp);
inode_unlock(d_inode(fhp->fh_dentry));
fhp->fh_locked = false;
}
} }
extern void fh_fill_pre_attrs(struct svc_fh *fhp);
extern void fh_fill_post_attrs(struct svc_fh *fhp);
extern void fh_fill_both_attrs(struct svc_fh *fhp);
#endif /* _LINUX_NFSD_NFSFH_H */ #endif /* _LINUX_NFSD_NFSFH_H */

View File

@ -51,9 +51,6 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
struct nfsd_sattrargs *argp = rqstp->rq_argp; struct nfsd_sattrargs *argp = rqstp->rq_argp;
struct nfsd_attrstat *resp = rqstp->rq_resp; struct nfsd_attrstat *resp = rqstp->rq_resp;
struct iattr *iap = &argp->attrs; struct iattr *iap = &argp->attrs;
struct nfsd_attrs attrs = {
.na_iattr = iap,
};
struct svc_fh *fhp; struct svc_fh *fhp;
dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n", dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
@ -103,7 +100,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
} }
} }
resp->status = nfsd_setattr(rqstp, fhp, &attrs, 0, (time64_t)0); resp->status = nfsd_setattr(rqstp, fhp, iap, 0, (time64_t)0);
if (resp->status != nfs_ok) if (resp->status != nfs_ok)
goto out; goto out;
@ -152,16 +149,14 @@ nfsd_proc_lookup(struct svc_rqst *rqstp)
static __be32 static __be32
nfsd_proc_readlink(struct svc_rqst *rqstp) nfsd_proc_readlink(struct svc_rqst *rqstp)
{ {
struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd_readlinkargs *argp = rqstp->rq_argp;
struct nfsd_readlinkres *resp = rqstp->rq_resp; struct nfsd_readlinkres *resp = rqstp->rq_resp;
dprintk("nfsd: READLINK %s\n", SVCFH_fmt(&argp->fh)); dprintk("nfsd: READLINK %s\n", SVCFH_fmt(&argp->fh));
/* Read the symlink. */ /* Read the symlink. */
resp->len = NFS_MAXPATHLEN; resp->len = NFS_MAXPATHLEN;
resp->page = *(rqstp->rq_next_page++); resp->status = nfsd_readlink(rqstp, &argp->fh, argp->buffer, &resp->len);
resp->status = nfsd_readlink(rqstp, &argp->fh,
page_address(resp->page), &resp->len);
fh_put(&argp->fh); fh_put(&argp->fh);
return rpc_success; return rpc_success;
@ -176,42 +171,36 @@ nfsd_proc_read(struct svc_rqst *rqstp)
{ {
struct nfsd_readargs *argp = rqstp->rq_argp; struct nfsd_readargs *argp = rqstp->rq_argp;
struct nfsd_readres *resp = rqstp->rq_resp; struct nfsd_readres *resp = rqstp->rq_resp;
unsigned int len;
u32 eof; u32 eof;
int v;
dprintk("nfsd: READ %s %d bytes at %d\n", dprintk("nfsd: READ %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->count, argp->offset); argp->count, argp->offset);
argp->count = min_t(u32, argp->count, NFSSVC_MAXBLKSIZE_V2);
argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen);
v = 0;
len = argp->count;
resp->pages = rqstp->rq_next_page;
while (len > 0) {
struct page *page = *(rqstp->rq_next_page++);
rqstp->rq_vec[v].iov_base = page_address(page);
rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
len -= rqstp->rq_vec[v].iov_len;
v++;
}
/* Obtain buffer pointer for payload. 19 is 1 word for /* Obtain buffer pointer for payload. 19 is 1 word for
* status, 17 words for fattr, and 1 word for the byte count. * status, 17 words for fattr, and 1 word for the byte count.
*/ */
if (NFSSVC_MAXBLKSIZE_V2 < argp->count) {
char buf[RPC_MAX_ADDRBUFLEN];
printk(KERN_NOTICE
"oversized read request from %s (%d bytes)\n",
svc_print_addr(rqstp, buf, sizeof(buf)),
argp->count);
argp->count = NFSSVC_MAXBLKSIZE_V2;
}
svc_reserve_auth(rqstp, (19<<2) + argp->count + 4); svc_reserve_auth(rqstp, (19<<2) + argp->count + 4);
resp->count = argp->count; resp->count = argp->count;
fh_copy(&resp->fh, &argp->fh); resp->status = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
resp->status = nfsd_read(rqstp, &resp->fh, argp->offset, argp->offset,
rqstp->rq_vec, v, &resp->count, &eof); rqstp->rq_vec, argp->vlen,
&resp->count,
&eof);
if (resp->status == nfs_ok) if (resp->status == nfs_ok)
resp->status = fh_getattr(&resp->fh, &resp->stat); resp->status = fh_getattr(&resp->fh, &resp->stat);
else if (resp->status == nfserr_jukebox) else if (resp->status == nfserr_jukebox)
set_bit(RQ_DROPME, &rqstp->rq_flags); return rpc_drop_reply;
return rpc_success; return rpc_success;
} }
@ -238,7 +227,12 @@ nfsd_proc_write(struct svc_rqst *rqstp)
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->len, argp->offset); argp->len, argp->offset);
nvecs = svc_fill_write_vector(rqstp, &argp->payload); nvecs = svc_fill_write_vector(rqstp, rqstp->rq_arg.pages,
&argp->first, cnt);
if (!nvecs) {
resp->status = nfserr_io;
goto out;
}
resp->status = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), resp->status = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
argp->offset, rqstp->rq_vec, nvecs, argp->offset, rqstp->rq_vec, nvecs,
@ -246,7 +240,8 @@ nfsd_proc_write(struct svc_rqst *rqstp)
if (resp->status == nfs_ok) if (resp->status == nfs_ok)
resp->status = fh_getattr(&resp->fh, &resp->stat); resp->status = fh_getattr(&resp->fh, &resp->stat);
else if (resp->status == nfserr_jukebox) else if (resp->status == nfserr_jukebox)
set_bit(RQ_DROPME, &rqstp->rq_flags); return rpc_drop_reply;
out:
return rpc_success; return rpc_success;
} }
@ -264,9 +259,6 @@ nfsd_proc_create(struct svc_rqst *rqstp)
svc_fh *dirfhp = &argp->fh; svc_fh *dirfhp = &argp->fh;
svc_fh *newfhp = &resp->fh; svc_fh *newfhp = &resp->fh;
struct iattr *attr = &argp->attrs; struct iattr *attr = &argp->attrs;
struct nfsd_attrs attrs = {
.na_iattr = attr,
};
struct inode *inode; struct inode *inode;
struct dentry *dchild; struct dentry *dchild;
int type, mode; int type, mode;
@ -292,7 +284,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
goto done; goto done;
} }
inode_lock_nested(dirfhp->fh_dentry->d_inode, I_MUTEX_PARENT); fh_lock_nested(dirfhp, I_MUTEX_PARENT);
dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len); dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
if (IS_ERR(dchild)) { if (IS_ERR(dchild)) {
resp->status = nfserrno(PTR_ERR(dchild)); resp->status = nfserrno(PTR_ERR(dchild));
@ -391,8 +383,9 @@ nfsd_proc_create(struct svc_rqst *rqstp)
resp->status = nfs_ok; resp->status = nfs_ok;
if (!inode) { if (!inode) {
/* File doesn't exist. Create it and set attrs */ /* File doesn't exist. Create it and set attrs */
resp->status = nfsd_create_locked(rqstp, dirfhp, &attrs, type, resp->status = nfsd_create_locked(rqstp, dirfhp, argp->name,
rdev, newfhp); argp->len, attr, type, rdev,
newfhp);
} else if (type == S_IFREG) { } else if (type == S_IFREG) {
dprintk("nfsd: existing %s, valid=%x, size=%ld\n", dprintk("nfsd: existing %s, valid=%x, size=%ld\n",
argp->name, attr->ia_valid, (long) attr->ia_size); argp->name, attr->ia_valid, (long) attr->ia_size);
@ -402,12 +395,13 @@ nfsd_proc_create(struct svc_rqst *rqstp)
*/ */
attr->ia_valid &= ATTR_SIZE; attr->ia_valid &= ATTR_SIZE;
if (attr->ia_valid) if (attr->ia_valid)
resp->status = nfsd_setattr(rqstp, newfhp, &attrs, 0, resp->status = nfsd_setattr(rqstp, newfhp, attr, 0,
(time64_t)0); (time64_t)0);
} }
out_unlock: out_unlock:
inode_unlock(dirfhp->fh_dentry->d_inode); /* We don't really need to unlock, as fh_put does it. */
fh_unlock(dirfhp);
fh_drop_write(dirfhp); fh_drop_write(dirfhp);
done: done:
fh_put(dirfhp); fh_put(dirfhp);
@ -477,9 +471,6 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
{ {
struct nfsd_symlinkargs *argp = rqstp->rq_argp; struct nfsd_symlinkargs *argp = rqstp->rq_argp;
struct nfsd_stat *resp = rqstp->rq_resp; struct nfsd_stat *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
struct svc_fh newfh; struct svc_fh newfh;
if (argp->tlen > NFS_MAXPATHLEN) { if (argp->tlen > NFS_MAXPATHLEN) {
@ -501,7 +492,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
fh_init(&newfh, NFS_FHSIZE); fh_init(&newfh, NFS_FHSIZE);
resp->status = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, resp->status = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
argp->tname, &attrs, &newfh); argp->tname, &newfh);
kfree(argp->tname); kfree(argp->tname);
fh_put(&argp->ffh); fh_put(&argp->ffh);
@ -519,9 +510,6 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp)
{ {
struct nfsd_createargs *argp = rqstp->rq_argp; struct nfsd_createargs *argp = rqstp->rq_argp;
struct nfsd_diropres *resp = rqstp->rq_resp; struct nfsd_diropres *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name); dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
@ -533,7 +521,7 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp)
argp->attrs.ia_valid &= ~ATTR_SIZE; argp->attrs.ia_valid &= ~ATTR_SIZE;
fh_init(&resp->fh, NFS_FHSIZE); fh_init(&resp->fh, NFS_FHSIZE);
resp->status = nfsd_create(rqstp, &argp->fh, argp->name, argp->len, resp->status = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
&attrs, S_IFDIR, 0, &resp->fh); &argp->attrs, S_IFDIR, 0, &resp->fh);
fh_put(&argp->fh); fh_put(&argp->fh);
if (resp->status != nfs_ok) if (resp->status != nfs_ok)
goto out; goto out;
@ -560,24 +548,6 @@ nfsd_proc_rmdir(struct svc_rqst *rqstp)
return rpc_success; return rpc_success;
} }
static void nfsd_init_dirlist_pages(struct svc_rqst *rqstp,
struct nfsd_readdirres *resp,
u32 count)
{
struct xdr_buf *buf = &resp->dirlist;
struct xdr_stream *xdr = &resp->xdr;
memset(buf, 0, sizeof(*buf));
/* Reserve room for the NULL ptr & eof flag (-2 words) */
buf->buflen = clamp(count, (u32)(XDR_UNIT * 2), (u32)PAGE_SIZE);
buf->buflen -= XDR_UNIT * 2;
buf->pages = rqstp->rq_next_page;
rqstp->rq_next_page++;
xdr_init_encode_pages(xdr, buf, buf->pages, NULL);
}
/* /*
* Read a portion of a directory. * Read a portion of a directory.
*/ */
@ -586,20 +556,33 @@ nfsd_proc_readdir(struct svc_rqst *rqstp)
{ {
struct nfsd_readdirargs *argp = rqstp->rq_argp; struct nfsd_readdirargs *argp = rqstp->rq_argp;
struct nfsd_readdirres *resp = rqstp->rq_resp; struct nfsd_readdirres *resp = rqstp->rq_resp;
int count;
loff_t offset; loff_t offset;
dprintk("nfsd: READDIR %s %d bytes at %d\n", dprintk("nfsd: READDIR %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->count, argp->cookie); argp->count, argp->cookie);
nfsd_init_dirlist_pages(rqstp, resp, argp->count); /* Shrink to the client read size */
count = (argp->count >> 2) - 2;
/* Make sure we've room for the NULL ptr & eof flag */
count -= 2;
if (count < 0)
count = 0;
resp->buffer = argp->buffer;
resp->offset = NULL;
resp->buflen = count;
resp->common.err = nfs_ok; resp->common.err = nfs_ok;
resp->cookie_offset = 0; /* Read directory and encode entries on the fly */
offset = argp->cookie; offset = argp->cookie;
resp->status = nfsd_readdir(rqstp, &argp->fh, &offset, resp->status = nfsd_readdir(rqstp, &argp->fh, &offset,
&resp->common, nfssvc_encode_entry); &resp->common, nfssvc_encode_entry);
nfssvc_encode_nfscookie(resp, offset);
resp->count = resp->buffer - argp->buffer;
if (resp->offset)
*resp->offset = htonl(offset);
fh_put(&argp->fh); fh_put(&argp->fh);
return rpc_success; return rpc_success;
@ -626,6 +609,7 @@ nfsd_proc_statfs(struct svc_rqst *rqstp)
* NFSv2 Server procedures. * NFSv2 Server procedures.
* Only the results of non-idempotent operations are cached. * Only the results of non-idempotent operations are cached.
*/ */
struct nfsd_void { int dummy; };
#define ST 1 /* status */ #define ST 1 /* status */
#define FH 8 /* filehandle */ #define FH 8 /* filehandle */
@ -634,49 +618,41 @@ nfsd_proc_statfs(struct svc_rqst *rqstp)
static const struct svc_procedure nfsd_procedures2[18] = { static const struct svc_procedure nfsd_procedures2[18] = {
[NFSPROC_NULL] = { [NFSPROC_NULL] = {
.pc_func = nfsd_proc_null, .pc_func = nfsd_proc_null,
.pc_decode = nfssvc_decode_voidarg, .pc_decode = nfssvc_decode_void,
.pc_encode = nfssvc_encode_voidres, .pc_encode = nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_voidargs), .pc_argsize = sizeof(struct nfsd_void),
.pc_argzero = sizeof(struct nfsd_voidargs), .pc_ressize = sizeof(struct nfsd_void),
.pc_ressize = sizeof(struct nfsd_voidres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = 0, .pc_xdrressize = 0,
.pc_name = "NULL",
}, },
[NFSPROC_GETATTR] = { [NFSPROC_GETATTR] = {
.pc_func = nfsd_proc_getattr, .pc_func = nfsd_proc_getattr,
.pc_decode = nfssvc_decode_fhandleargs, .pc_decode = nfssvc_decode_fhandle,
.pc_encode = nfssvc_encode_attrstatres, .pc_encode = nfssvc_encode_attrstat,
.pc_release = nfssvc_release_attrstat, .pc_release = nfssvc_release_attrstat,
.pc_argsize = sizeof(struct nfsd_fhandle), .pc_argsize = sizeof(struct nfsd_fhandle),
.pc_argzero = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd_attrstat), .pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT, .pc_xdrressize = ST+AT,
.pc_name = "GETATTR",
}, },
[NFSPROC_SETATTR] = { [NFSPROC_SETATTR] = {
.pc_func = nfsd_proc_setattr, .pc_func = nfsd_proc_setattr,
.pc_decode = nfssvc_decode_sattrargs, .pc_decode = nfssvc_decode_sattrargs,
.pc_encode = nfssvc_encode_attrstatres, .pc_encode = nfssvc_encode_attrstat,
.pc_release = nfssvc_release_attrstat, .pc_release = nfssvc_release_attrstat,
.pc_argsize = sizeof(struct nfsd_sattrargs), .pc_argsize = sizeof(struct nfsd_sattrargs),
.pc_argzero = sizeof(struct nfsd_sattrargs),
.pc_ressize = sizeof(struct nfsd_attrstat), .pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+AT, .pc_xdrressize = ST+AT,
.pc_name = "SETATTR",
}, },
[NFSPROC_ROOT] = { [NFSPROC_ROOT] = {
.pc_func = nfsd_proc_root, .pc_func = nfsd_proc_root,
.pc_decode = nfssvc_decode_voidarg, .pc_decode = nfssvc_decode_void,
.pc_encode = nfssvc_encode_voidres, .pc_encode = nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_voidargs), .pc_argsize = sizeof(struct nfsd_void),
.pc_argzero = sizeof(struct nfsd_voidargs), .pc_ressize = sizeof(struct nfsd_void),
.pc_ressize = sizeof(struct nfsd_voidres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = 0, .pc_xdrressize = 0,
.pc_name = "ROOT",
}, },
[NFSPROC_LOOKUP] = { [NFSPROC_LOOKUP] = {
.pc_func = nfsd_proc_lookup, .pc_func = nfsd_proc_lookup,
@ -684,22 +660,18 @@ static const struct svc_procedure nfsd_procedures2[18] = {
.pc_encode = nfssvc_encode_diropres, .pc_encode = nfssvc_encode_diropres,
.pc_release = nfssvc_release_diropres, .pc_release = nfssvc_release_diropres,
.pc_argsize = sizeof(struct nfsd_diropargs), .pc_argsize = sizeof(struct nfsd_diropargs),
.pc_argzero = sizeof(struct nfsd_diropargs),
.pc_ressize = sizeof(struct nfsd_diropres), .pc_ressize = sizeof(struct nfsd_diropres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+FH+AT, .pc_xdrressize = ST+FH+AT,
.pc_name = "LOOKUP",
}, },
[NFSPROC_READLINK] = { [NFSPROC_READLINK] = {
.pc_func = nfsd_proc_readlink, .pc_func = nfsd_proc_readlink,
.pc_decode = nfssvc_decode_fhandleargs, .pc_decode = nfssvc_decode_readlinkargs,
.pc_encode = nfssvc_encode_readlinkres, .pc_encode = nfssvc_encode_readlinkres,
.pc_argsize = sizeof(struct nfsd_fhandle), .pc_argsize = sizeof(struct nfsd_readlinkargs),
.pc_argzero = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd_readlinkres), .pc_ressize = sizeof(struct nfsd_readlinkres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+1+NFS_MAXPATHLEN/4, .pc_xdrressize = ST+1+NFS_MAXPATHLEN/4,
.pc_name = "READLINK",
}, },
[NFSPROC_READ] = { [NFSPROC_READ] = {
.pc_func = nfsd_proc_read, .pc_func = nfsd_proc_read,
@ -707,34 +679,28 @@ static const struct svc_procedure nfsd_procedures2[18] = {
.pc_encode = nfssvc_encode_readres, .pc_encode = nfssvc_encode_readres,
.pc_release = nfssvc_release_readres, .pc_release = nfssvc_release_readres,
.pc_argsize = sizeof(struct nfsd_readargs), .pc_argsize = sizeof(struct nfsd_readargs),
.pc_argzero = sizeof(struct nfsd_readargs),
.pc_ressize = sizeof(struct nfsd_readres), .pc_ressize = sizeof(struct nfsd_readres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4, .pc_xdrressize = ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4,
.pc_name = "READ",
}, },
[NFSPROC_WRITECACHE] = { [NFSPROC_WRITECACHE] = {
.pc_func = nfsd_proc_writecache, .pc_func = nfsd_proc_writecache,
.pc_decode = nfssvc_decode_voidarg, .pc_decode = nfssvc_decode_void,
.pc_encode = nfssvc_encode_voidres, .pc_encode = nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_voidargs), .pc_argsize = sizeof(struct nfsd_void),
.pc_argzero = sizeof(struct nfsd_voidargs), .pc_ressize = sizeof(struct nfsd_void),
.pc_ressize = sizeof(struct nfsd_voidres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = 0, .pc_xdrressize = 0,
.pc_name = "WRITECACHE",
}, },
[NFSPROC_WRITE] = { [NFSPROC_WRITE] = {
.pc_func = nfsd_proc_write, .pc_func = nfsd_proc_write,
.pc_decode = nfssvc_decode_writeargs, .pc_decode = nfssvc_decode_writeargs,
.pc_encode = nfssvc_encode_attrstatres, .pc_encode = nfssvc_encode_attrstat,
.pc_release = nfssvc_release_attrstat, .pc_release = nfssvc_release_attrstat,
.pc_argsize = sizeof(struct nfsd_writeargs), .pc_argsize = sizeof(struct nfsd_writeargs),
.pc_argzero = sizeof(struct nfsd_writeargs),
.pc_ressize = sizeof(struct nfsd_attrstat), .pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+AT, .pc_xdrressize = ST+AT,
.pc_name = "WRITE",
}, },
[NFSPROC_CREATE] = { [NFSPROC_CREATE] = {
.pc_func = nfsd_proc_create, .pc_func = nfsd_proc_create,
@ -742,55 +708,45 @@ static const struct svc_procedure nfsd_procedures2[18] = {
.pc_encode = nfssvc_encode_diropres, .pc_encode = nfssvc_encode_diropres,
.pc_release = nfssvc_release_diropres, .pc_release = nfssvc_release_diropres,
.pc_argsize = sizeof(struct nfsd_createargs), .pc_argsize = sizeof(struct nfsd_createargs),
.pc_argzero = sizeof(struct nfsd_createargs),
.pc_ressize = sizeof(struct nfsd_diropres), .pc_ressize = sizeof(struct nfsd_diropres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+FH+AT, .pc_xdrressize = ST+FH+AT,
.pc_name = "CREATE",
}, },
[NFSPROC_REMOVE] = { [NFSPROC_REMOVE] = {
.pc_func = nfsd_proc_remove, .pc_func = nfsd_proc_remove,
.pc_decode = nfssvc_decode_diropargs, .pc_decode = nfssvc_decode_diropargs,
.pc_encode = nfssvc_encode_statres, .pc_encode = nfssvc_encode_stat,
.pc_argsize = sizeof(struct nfsd_diropargs), .pc_argsize = sizeof(struct nfsd_diropargs),
.pc_argzero = sizeof(struct nfsd_diropargs),
.pc_ressize = sizeof(struct nfsd_stat), .pc_ressize = sizeof(struct nfsd_stat),
.pc_cachetype = RC_REPLSTAT, .pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "REMOVE",
}, },
[NFSPROC_RENAME] = { [NFSPROC_RENAME] = {
.pc_func = nfsd_proc_rename, .pc_func = nfsd_proc_rename,
.pc_decode = nfssvc_decode_renameargs, .pc_decode = nfssvc_decode_renameargs,
.pc_encode = nfssvc_encode_statres, .pc_encode = nfssvc_encode_stat,
.pc_argsize = sizeof(struct nfsd_renameargs), .pc_argsize = sizeof(struct nfsd_renameargs),
.pc_argzero = sizeof(struct nfsd_renameargs),
.pc_ressize = sizeof(struct nfsd_stat), .pc_ressize = sizeof(struct nfsd_stat),
.pc_cachetype = RC_REPLSTAT, .pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "RENAME",
}, },
[NFSPROC_LINK] = { [NFSPROC_LINK] = {
.pc_func = nfsd_proc_link, .pc_func = nfsd_proc_link,
.pc_decode = nfssvc_decode_linkargs, .pc_decode = nfssvc_decode_linkargs,
.pc_encode = nfssvc_encode_statres, .pc_encode = nfssvc_encode_stat,
.pc_argsize = sizeof(struct nfsd_linkargs), .pc_argsize = sizeof(struct nfsd_linkargs),
.pc_argzero = sizeof(struct nfsd_linkargs),
.pc_ressize = sizeof(struct nfsd_stat), .pc_ressize = sizeof(struct nfsd_stat),
.pc_cachetype = RC_REPLSTAT, .pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "LINK",
}, },
[NFSPROC_SYMLINK] = { [NFSPROC_SYMLINK] = {
.pc_func = nfsd_proc_symlink, .pc_func = nfsd_proc_symlink,
.pc_decode = nfssvc_decode_symlinkargs, .pc_decode = nfssvc_decode_symlinkargs,
.pc_encode = nfssvc_encode_statres, .pc_encode = nfssvc_encode_stat,
.pc_argsize = sizeof(struct nfsd_symlinkargs), .pc_argsize = sizeof(struct nfsd_symlinkargs),
.pc_argzero = sizeof(struct nfsd_symlinkargs),
.pc_ressize = sizeof(struct nfsd_stat), .pc_ressize = sizeof(struct nfsd_stat),
.pc_cachetype = RC_REPLSTAT, .pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "SYMLINK",
}, },
[NFSPROC_MKDIR] = { [NFSPROC_MKDIR] = {
.pc_func = nfsd_proc_mkdir, .pc_func = nfsd_proc_mkdir,
@ -798,43 +754,35 @@ static const struct svc_procedure nfsd_procedures2[18] = {
.pc_encode = nfssvc_encode_diropres, .pc_encode = nfssvc_encode_diropres,
.pc_release = nfssvc_release_diropres, .pc_release = nfssvc_release_diropres,
.pc_argsize = sizeof(struct nfsd_createargs), .pc_argsize = sizeof(struct nfsd_createargs),
.pc_argzero = sizeof(struct nfsd_createargs),
.pc_ressize = sizeof(struct nfsd_diropres), .pc_ressize = sizeof(struct nfsd_diropres),
.pc_cachetype = RC_REPLBUFF, .pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+FH+AT, .pc_xdrressize = ST+FH+AT,
.pc_name = "MKDIR",
}, },
[NFSPROC_RMDIR] = { [NFSPROC_RMDIR] = {
.pc_func = nfsd_proc_rmdir, .pc_func = nfsd_proc_rmdir,
.pc_decode = nfssvc_decode_diropargs, .pc_decode = nfssvc_decode_diropargs,
.pc_encode = nfssvc_encode_statres, .pc_encode = nfssvc_encode_stat,
.pc_argsize = sizeof(struct nfsd_diropargs), .pc_argsize = sizeof(struct nfsd_diropargs),
.pc_argzero = sizeof(struct nfsd_diropargs),
.pc_ressize = sizeof(struct nfsd_stat), .pc_ressize = sizeof(struct nfsd_stat),
.pc_cachetype = RC_REPLSTAT, .pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST, .pc_xdrressize = ST,
.pc_name = "RMDIR",
}, },
[NFSPROC_READDIR] = { [NFSPROC_READDIR] = {
.pc_func = nfsd_proc_readdir, .pc_func = nfsd_proc_readdir,
.pc_decode = nfssvc_decode_readdirargs, .pc_decode = nfssvc_decode_readdirargs,
.pc_encode = nfssvc_encode_readdirres, .pc_encode = nfssvc_encode_readdirres,
.pc_argsize = sizeof(struct nfsd_readdirargs), .pc_argsize = sizeof(struct nfsd_readdirargs),
.pc_argzero = sizeof(struct nfsd_readdirargs),
.pc_ressize = sizeof(struct nfsd_readdirres), .pc_ressize = sizeof(struct nfsd_readdirres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_name = "READDIR",
}, },
[NFSPROC_STATFS] = { [NFSPROC_STATFS] = {
.pc_func = nfsd_proc_statfs, .pc_func = nfsd_proc_statfs,
.pc_decode = nfssvc_decode_fhandleargs, .pc_decode = nfssvc_decode_fhandle,
.pc_encode = nfssvc_encode_statfsres, .pc_encode = nfssvc_encode_statfsres,
.pc_argsize = sizeof(struct nfsd_fhandle), .pc_argsize = sizeof(struct nfsd_fhandle),
.pc_argzero = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd_statfsres), .pc_ressize = sizeof(struct nfsd_statfsres),
.pc_cachetype = RC_NOCACHE, .pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+5, .pc_xdrressize = ST+5,
.pc_name = "STATFS",
}, },
}; };
@ -848,3 +796,61 @@ const struct svc_version nfsd_version2 = {
.vs_dispatch = nfsd_dispatch, .vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS2_SVC_XDRSIZE, .vs_xdrsize = NFS2_SVC_XDRSIZE,
}; };
/*
* Map errnos to NFS errnos.
*/
__be32
nfserrno (int errno)
{
static struct {
__be32 nfserr;
int syserr;
} nfs_errtbl[] = {
{ nfs_ok, 0 },
{ nfserr_perm, -EPERM },
{ nfserr_noent, -ENOENT },
{ nfserr_io, -EIO },
{ nfserr_nxio, -ENXIO },
{ nfserr_fbig, -E2BIG },
{ nfserr_acces, -EACCES },
{ nfserr_exist, -EEXIST },
{ nfserr_xdev, -EXDEV },
{ nfserr_mlink, -EMLINK },
{ nfserr_nodev, -ENODEV },
{ nfserr_notdir, -ENOTDIR },
{ nfserr_isdir, -EISDIR },
{ nfserr_inval, -EINVAL },
{ nfserr_fbig, -EFBIG },
{ nfserr_nospc, -ENOSPC },
{ nfserr_rofs, -EROFS },
{ nfserr_mlink, -EMLINK },
{ nfserr_nametoolong, -ENAMETOOLONG },
{ nfserr_notempty, -ENOTEMPTY },
#ifdef EDQUOT
{ nfserr_dquot, -EDQUOT },
#endif
{ nfserr_stale, -ESTALE },
{ nfserr_jukebox, -ETIMEDOUT },
{ nfserr_jukebox, -ERESTARTSYS },
{ nfserr_jukebox, -EAGAIN },
{ nfserr_jukebox, -EWOULDBLOCK },
{ nfserr_jukebox, -ENOMEM },
{ nfserr_io, -ETXTBSY },
{ nfserr_notsupp, -EOPNOTSUPP },
{ nfserr_toosmall, -ETOOSMALL },
{ nfserr_serverfault, -ESERVERFAULT },
{ nfserr_serverfault, -ENFILE },
{ nfserr_io, -EUCLEAN },
{ nfserr_perm, -ENOKEY },
};
int i;
for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
if (nfs_errtbl[i].syserr == errno)
return nfs_errtbl[i].nfserr;
}
WARN_ONCE(1, "nfsd: non-standard errno: %d\n", errno);
return nfserr_io;
}

View File

@ -12,7 +12,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/fs_struct.h> #include <linux/fs_struct.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/siphash.h>
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
@ -30,10 +29,14 @@
#include "netns.h" #include "netns.h"
#include "filecache.h" #include "filecache.h"
#include "trace.h"
#define NFSDDBG_FACILITY NFSDDBG_SVC #define NFSDDBG_FACILITY NFSDDBG_SVC
bool inter_copy_offload_enable;
EXPORT_SYMBOL_GPL(inter_copy_offload_enable);
module_param(inter_copy_offload_enable, bool, 0644);
MODULE_PARM_DESC(inter_copy_offload_enable,
"Enable inter server to server copy offload. Default: false");
extern struct svc_program nfsd_program; extern struct svc_program nfsd_program;
static int nfsd(void *vrqstp); static int nfsd(void *vrqstp);
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
@ -56,17 +59,18 @@ static __be32 nfsd_init_request(struct svc_rqst *,
struct svc_process_info *); struct svc_process_info *);
/* /*
* nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members
* of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks. * of the svc_serv struct. In particular, ->sv_nrthreads but also to some
* extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt
* *
* If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a * If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a
* properly initialised 'struct svc_serv' with ->sv_nrthreads > 0 (unless * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number
* nn->keep_active is set). That number of nfsd threads must * of nfsd threads must exist and each must listed in ->sp_all_threads in each
* exist and each must be listed in ->sp_all_threads in some entry of * entry of ->sv_pools[].
* ->sv_pools[].
* *
* Each active thread holds a counted reference on nn->nfsd_serv, as does * Transitions of the thread count between zero and non-zero are of particular
* the nn->keep_active flag and various transient calls to svc_get(). * interest since the svc_serv needs to be created and initialized at that
* point, or freed.
* *
* Finally, the nfsd_mutex also protects some of the global variables that are * Finally, the nfsd_mutex also protects some of the global variables that are
* accessed when nfsd starts and that are settable via the write_* routines in * accessed when nfsd starts and that are settable via the write_* routines in
@ -84,19 +88,15 @@ DEFINE_MUTEX(nfsd_mutex);
* version 4.1 DRC caches. * version 4.1 DRC caches.
* nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage. * nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
*/ */
DEFINE_SPINLOCK(nfsd_drc_lock); spinlock_t nfsd_drc_lock;
unsigned long nfsd_drc_max_mem; unsigned long nfsd_drc_max_mem;
unsigned long nfsd_drc_mem_used; unsigned long nfsd_drc_mem_used;
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
static struct svc_stat nfsd_acl_svcstats; static struct svc_stat nfsd_acl_svcstats;
static const struct svc_version *nfsd_acl_version[] = { static const struct svc_version *nfsd_acl_version[] = {
# if defined(CONFIG_NFSD_V2_ACL)
[2] = &nfsd_acl_version2, [2] = &nfsd_acl_version2,
# endif
# if defined(CONFIG_NFSD_V3_ACL)
[3] = &nfsd_acl_version3, [3] = &nfsd_acl_version3,
# endif
}; };
#define NFSD_ACL_MINVERS 2 #define NFSD_ACL_MINVERS 2
@ -120,10 +120,10 @@ static struct svc_stat nfsd_acl_svcstats = {
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */ #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
static const struct svc_version *nfsd_version[] = { static const struct svc_version *nfsd_version[] = {
#if defined(CONFIG_NFSD_V2)
[2] = &nfsd_version2, [2] = &nfsd_version2,
#endif #if defined(CONFIG_NFSD_V3)
[3] = &nfsd_version3, [3] = &nfsd_version3,
#endif
#if defined(CONFIG_NFSD_V4) #if defined(CONFIG_NFSD_V4)
[4] = &nfsd_version4, [4] = &nfsd_version4,
#endif #endif
@ -297,13 +297,13 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
if (!list_empty(&nn->nfsd_serv->sv_permsocks)) if (!list_empty(&nn->nfsd_serv->sv_permsocks))
return 0; return 0;
error = svc_xprt_create(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT, error = svc_create_xprt(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
SVC_SOCK_DEFAULTS, cred); SVC_SOCK_DEFAULTS, cred);
if (error < 0) if (error < 0)
return error; return error;
error = svc_xprt_create(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT, error = svc_create_xprt(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
SVC_SOCK_DEFAULTS, cred); SVC_SOCK_DEFAULTS, cred);
if (error < 0) if (error < 0)
return error; return error;
@ -312,7 +312,7 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
static int nfsd_users = 0; static int nfsd_users = 0;
static int nfsd_startup_generic(void) static int nfsd_startup_generic(int nrservs)
{ {
int ret; int ret;
@ -349,60 +349,36 @@ static bool nfsd_needs_lockd(struct nfsd_net *nn)
return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST); return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST);
} }
/** void nfsd_copy_boot_verifier(__be32 verf[2], struct nfsd_net *nn)
* nfsd_copy_write_verifier - Atomically copy a write verifier
* @verf: buffer in which to receive the verifier cookie
* @nn: NFS net namespace
*
* This function provides a wait-free mechanism for copying the
* namespace's write verifier without tearing it.
*/
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn)
{ {
int seq = 0; int seq = 0;
do { do {
read_seqbegin_or_lock(&nn->writeverf_lock, &seq); read_seqbegin_or_lock(&nn->boot_lock, &seq);
memcpy(verf, nn->writeverf, sizeof(nn->writeverf)); /*
} while (need_seqretry(&nn->writeverf_lock, seq)); * This is opaque to client, so no need to byte-swap. Use
done_seqretry(&nn->writeverf_lock, seq); * __force to keep sparse happy. y2038 time_t overflow is
* irrelevant in this usage
*/
verf[0] = (__force __be32)nn->nfssvc_boot.tv_sec;
verf[1] = (__force __be32)nn->nfssvc_boot.tv_nsec;
} while (need_seqretry(&nn->boot_lock, seq));
done_seqretry(&nn->boot_lock, seq);
} }
static void nfsd_reset_write_verifier_locked(struct nfsd_net *nn) static void nfsd_reset_boot_verifier_locked(struct nfsd_net *nn)
{ {
struct timespec64 now; ktime_get_real_ts64(&nn->nfssvc_boot);
u64 verf;
/*
* Because the time value is hashed, y2038 time_t overflow
* is irrelevant in this usage.
*/
ktime_get_raw_ts64(&now);
verf = siphash_2u64(now.tv_sec, now.tv_nsec, &nn->siphash_key);
memcpy(nn->writeverf, &verf, sizeof(nn->writeverf));
} }
/** void nfsd_reset_boot_verifier(struct nfsd_net *nn)
* nfsd_reset_write_verifier - Generate a new write verifier
* @nn: NFS net namespace
*
* This function updates the ->writeverf field of @nn. This field
* contains an opaque cookie that, according to Section 18.32.3 of
* RFC 8881, "the client can use to determine whether a server has
* changed instance state (e.g., server restart) between a call to
* WRITE and a subsequent call to either WRITE or COMMIT. This
* cookie MUST be unchanged during a single instance of the NFSv4.1
* server and MUST be unique between instances of the NFSv4.1
* server."
*/
void nfsd_reset_write_verifier(struct nfsd_net *nn)
{ {
write_seqlock(&nn->writeverf_lock); write_seqlock(&nn->boot_lock);
nfsd_reset_write_verifier_locked(nn); nfsd_reset_boot_verifier_locked(nn);
write_sequnlock(&nn->writeverf_lock); write_sequnlock(&nn->boot_lock);
} }
static int nfsd_startup_net(struct net *net, const struct cred *cred) static int nfsd_startup_net(int nrservs, struct net *net, const struct cred *cred)
{ {
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
int ret; int ret;
@ -410,7 +386,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
if (nn->nfsd_net_up) if (nn->nfsd_net_up)
return 0; return 0;
ret = nfsd_startup_generic(); ret = nfsd_startup_generic(nrservs);
if (ret) if (ret)
return ret; return ret;
ret = nfsd_init_socks(net, cred); ret = nfsd_init_socks(net, cred);
@ -431,9 +407,6 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
if (ret) if (ret)
goto out_filecache; goto out_filecache;
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
nfsd4_ssc_init_umount_work(nn);
#endif
nn->nfsd_net_up = true; nn->nfsd_net_up = true;
return 0; return 0;
@ -463,7 +436,6 @@ static void nfsd_shutdown_net(struct net *net)
nfsd_shutdown_generic(); nfsd_shutdown_generic();
} }
static DEFINE_SPINLOCK(nfsd_notifier_lock);
static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event, static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
void *ptr) void *ptr)
{ {
@ -473,17 +445,18 @@ static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct sockaddr_in sin; struct sockaddr_in sin;
if (event != NETDEV_DOWN || !nn->nfsd_serv) if ((event != NETDEV_DOWN) ||
!atomic_inc_not_zero(&nn->ntf_refcnt))
goto out; goto out;
spin_lock(&nfsd_notifier_lock);
if (nn->nfsd_serv) { if (nn->nfsd_serv) {
dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local); dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local);
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ifa->ifa_local; sin.sin_addr.s_addr = ifa->ifa_local;
svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin); svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin);
} }
spin_unlock(&nfsd_notifier_lock); atomic_dec(&nn->ntf_refcnt);
wake_up(&nn->ntf_wq);
out: out:
return NOTIFY_DONE; return NOTIFY_DONE;
@ -503,10 +476,10 @@ static int nfsd_inet6addr_event(struct notifier_block *this,
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
if (event != NETDEV_DOWN || !nn->nfsd_serv) if ((event != NETDEV_DOWN) ||
!atomic_inc_not_zero(&nn->ntf_refcnt))
goto out; goto out;
spin_lock(&nfsd_notifier_lock);
if (nn->nfsd_serv) { if (nn->nfsd_serv) {
dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr); dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr);
sin6.sin6_family = AF_INET6; sin6.sin6_family = AF_INET6;
@ -515,8 +488,8 @@ static int nfsd_inet6addr_event(struct notifier_block *this,
sin6.sin6_scope_id = ifa->idev->dev->ifindex; sin6.sin6_scope_id = ifa->idev->dev->ifindex;
svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6); svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6);
} }
spin_unlock(&nfsd_notifier_lock); atomic_dec(&nn->ntf_refcnt);
wake_up(&nn->ntf_wq);
out: out:
return NOTIFY_DONE; return NOTIFY_DONE;
} }
@ -529,15 +502,11 @@ static struct notifier_block nfsd_inet6addr_notifier = {
/* Only used under nfsd_mutex, so this atomic may be overkill: */ /* Only used under nfsd_mutex, so this atomic may be overkill: */
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0); static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
void nfsd_last_thread(struct net *net) static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
{ {
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv = nn->nfsd_serv;
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = NULL;
spin_unlock(&nfsd_notifier_lock);
atomic_dec(&nn->ntf_refcnt);
/* check if the notifier still has clients */ /* check if the notifier still has clients */
if (atomic_dec_return(&nfsd_notifier_refcount) == 0) { if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
unregister_inetaddr_notifier(&nfsd_inetaddr_notifier); unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
@ -545,8 +514,7 @@ void nfsd_last_thread(struct net *net)
unregister_inet6addr_notifier(&nfsd_inet6addr_notifier); unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
#endif #endif
} }
wait_event(nn->ntf_wq, atomic_read(&nn->ntf_refcnt) == 0);
svc_xprt_destroy_all(serv, net);
/* /*
* write_ports can create the server without actually starting * write_ports can create the server without actually starting
@ -599,6 +567,7 @@ static void set_max_drc(void)
nfsd_drc_max_mem = (nr_free_buffer_pages() nfsd_drc_max_mem = (nr_free_buffer_pages()
>> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE; >> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
nfsd_drc_mem_used = 0; nfsd_drc_mem_used = 0;
spin_lock_init(&nfsd_drc_lock);
dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem); dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem);
} }
@ -623,6 +592,24 @@ static int nfsd_get_default_max_blksize(void)
return ret; return ret;
} }
static const struct svc_serv_ops nfsd_thread_sv_ops = {
.svo_shutdown = nfsd_last_thread,
.svo_function = nfsd,
.svo_enqueue_xprt = svc_xprt_do_enqueue,
.svo_setup = svc_set_num_threads,
.svo_module = THIS_MODULE,
};
static void nfsd_complete_shutdown(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
WARN_ON(!mutex_is_locked(&nfsd_mutex));
nn->nfsd_serv = NULL;
complete(&nn->nfsd_shutdown_complete);
}
void nfsd_shutdown_threads(struct net *net) void nfsd_shutdown_threads(struct net *net)
{ {
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
@ -637,10 +624,11 @@ void nfsd_shutdown_threads(struct net *net)
svc_get(serv); svc_get(serv);
/* Kill outstanding nfsd threads */ /* Kill outstanding nfsd threads */
svc_set_num_threads(serv, NULL, 0); serv->sv_ops->svo_setup(serv, NULL, 0);
nfsd_last_thread(net); nfsd_destroy(net);
svc_put(serv);
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
/* Wait for shutdown of nfsd_serv to complete */
wait_for_completion(&nn->nfsd_shutdown_complete);
} }
bool i_am_nfsd(void) bool i_am_nfsd(void)
@ -652,7 +640,6 @@ int nfsd_create_serv(struct net *net)
{ {
int error; int error;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
WARN_ON(!mutex_is_locked(&nfsd_mutex)); WARN_ON(!mutex_is_locked(&nfsd_mutex));
if (nn->nfsd_serv) { if (nn->nfsd_serv) {
@ -662,19 +649,19 @@ int nfsd_create_serv(struct net *net)
if (nfsd_max_blksize == 0) if (nfsd_max_blksize == 0)
nfsd_max_blksize = nfsd_get_default_max_blksize(); nfsd_max_blksize = nfsd_get_default_max_blksize();
nfsd_reset_versions(nn); nfsd_reset_versions(nn);
serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, nfsd); nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
if (serv == NULL) &nfsd_thread_sv_ops);
if (nn->nfsd_serv == NULL)
return -ENOMEM; return -ENOMEM;
init_completion(&nn->nfsd_shutdown_complete);
serv->sv_maxconn = nn->max_connections; nn->nfsd_serv->sv_maxconn = nn->max_connections;
error = svc_bind(serv, net); error = svc_bind(nn->nfsd_serv, net);
if (error < 0) { if (error < 0) {
svc_put(serv); svc_destroy(nn->nfsd_serv);
nfsd_complete_shutdown(net);
return error; return error;
} }
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = serv;
spin_unlock(&nfsd_notifier_lock);
set_max_drc(); set_max_drc();
/* check if the notifier is already set */ /* check if the notifier is already set */
@ -684,7 +671,8 @@ int nfsd_create_serv(struct net *net)
register_inet6addr_notifier(&nfsd_inet6addr_notifier); register_inet6addr_notifier(&nfsd_inet6addr_notifier);
#endif #endif
} }
nfsd_reset_write_verifier(nn); atomic_inc(&nn->ntf_refcnt);
nfsd_reset_boot_verifier(nn);
return 0; return 0;
} }
@ -711,6 +699,18 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
return 0; return 0;
} }
void nfsd_destroy(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
int destroy = (nn->nfsd_serv->sv_nrthreads == 1);
if (destroy)
svc_shutdown_net(nn->nfsd_serv, net);
svc_destroy(nn->nfsd_serv);
if (destroy)
nfsd_complete_shutdown(net);
}
int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
{ {
int i = 0; int i = 0;
@ -735,7 +735,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
if (tot > NFSD_MAXSERVS) { if (tot > NFSD_MAXSERVS) {
/* total too large: scale down requested numbers */ /* total too large: scale down requested numbers */
for (i = 0; i < n && tot > 0; i++) { for (i = 0; i < n && tot > 0; i++) {
int new = nthreads[i] * NFSD_MAXSERVS / tot; int new = nthreads[i] * NFSD_MAXSERVS / tot;
tot -= (nthreads[i] - new); tot -= (nthreads[i] - new);
nthreads[i] = new; nthreads[i] = new;
} }
@ -755,13 +755,12 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
/* apply the new numbers */ /* apply the new numbers */
svc_get(nn->nfsd_serv); svc_get(nn->nfsd_serv);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
err = svc_set_num_threads(nn->nfsd_serv, err = nn->nfsd_serv->sv_ops->svo_setup(nn->nfsd_serv,
&nn->nfsd_serv->sv_pools[i], &nn->nfsd_serv->sv_pools[i], nthreads[i]);
nthreads[i]);
if (err) if (err)
break; break;
} }
svc_put(nn->nfsd_serv); nfsd_destroy(net);
return err; return err;
} }
@ -776,7 +775,6 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
int error; int error;
bool nfsd_up_before; bool nfsd_up_before;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
mutex_lock(&nfsd_mutex); mutex_lock(&nfsd_mutex);
dprintk("nfsd: creating service\n"); dprintk("nfsd: creating service\n");
@ -788,7 +786,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
if (nrservs == 0 && nn->nfsd_serv == NULL) if (nrservs == 0 && nn->nfsd_serv == NULL)
goto out; goto out;
strscpy(nn->nfsd_name, utsname()->nodename, strlcpy(nn->nfsd_name, utsname()->nodename,
sizeof(nn->nfsd_name)); sizeof(nn->nfsd_name));
error = nfsd_create_serv(net); error = nfsd_create_serv(net);
@ -796,25 +794,24 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
goto out; goto out;
nfsd_up_before = nn->nfsd_net_up; nfsd_up_before = nn->nfsd_net_up;
serv = nn->nfsd_serv;
error = nfsd_startup_net(net, cred); error = nfsd_startup_net(nrservs, net, cred);
if (error) if (error)
goto out_put; goto out_destroy;
error = svc_set_num_threads(serv, NULL, nrservs); error = nn->nfsd_serv->sv_ops->svo_setup(nn->nfsd_serv,
NULL, nrservs);
if (error) if (error)
goto out_shutdown; goto out_shutdown;
error = serv->sv_nrthreads; /* We are holding a reference to nn->nfsd_serv which
if (error == 0) * we don't want to count in the return value,
nfsd_last_thread(net); * so subtract 1
*/
error = nn->nfsd_serv->sv_nrthreads - 1;
out_shutdown: out_shutdown:
if (error < 0 && !nfsd_up_before) if (error < 0 && !nfsd_up_before)
nfsd_shutdown_net(net); nfsd_shutdown_net(net);
out_put: out_destroy:
/* Threads now hold service active */ nfsd_destroy(net); /* Release server */
if (xchg(&nn->keep_active, 0))
svc_put(serv);
svc_put(serv);
out: out:
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
return error; return error;
@ -928,6 +925,9 @@ nfsd(void *vrqstp)
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
int err; int err;
/* Lock module and set up kernel thread */
mutex_lock(&nfsd_mutex);
/* At this point, the thread shares current->fs /* At this point, the thread shares current->fs
* with the init process. We need to create files with the * with the init process. We need to create files with the
* umask as defined by the client instead of init's umask. */ * umask as defined by the client instead of init's umask. */
@ -938,7 +938,17 @@ nfsd(void *vrqstp)
current->fs->umask = 0; current->fs->umask = 0;
atomic_inc(&nfsdstats.th_cnt); /*
* thread is spawned with all signals set to SIG_IGN, re-enable
* the ones that will bring down the thread
*/
allow_signal(SIGKILL);
allow_signal(SIGHUP);
allow_signal(SIGINT);
allow_signal(SIGQUIT);
nfsdstats.th_cnt++;
mutex_unlock(&nfsd_mutex);
set_freezable(); set_freezable();
@ -962,14 +972,57 @@ nfsd(void *vrqstp)
validate_process_creds(); validate_process_creds();
} }
atomic_dec(&nfsdstats.th_cnt); /* Clear signals before calling svc_exit_thread() */
flush_signals(current);
mutex_lock(&nfsd_mutex);
nfsdstats.th_cnt --;
out: out:
rqstp->rq_server = NULL;
/* Release the thread */ /* Release the thread */
svc_exit_thread(rqstp); svc_exit_thread(rqstp);
nfsd_destroy(net);
/* Release module */
mutex_unlock(&nfsd_mutex);
module_put_and_exit(0);
return 0; return 0;
} }
/*
* A write procedure can have a large argument, and a read procedure can
* have a large reply, but no NFSv2 or NFSv3 procedure has argument and
* reply that can both be larger than a page. The xdr code has taken
* advantage of this assumption to be a sloppy about bounds checking in
* some cases. Pending a rewrite of the NFSv2/v3 xdr code to fix that
* problem, we enforce these assumptions here:
*/
static bool nfs_request_too_big(struct svc_rqst *rqstp,
const struct svc_procedure *proc)
{
/*
* The ACL code has more careful bounds-checking and is not
* susceptible to this problem:
*/
if (rqstp->rq_prog != NFS_PROGRAM)
return false;
/*
* Ditto NFSv4 (which can in theory have argument and reply both
* more than a page):
*/
if (rqstp->rq_vers >= 4)
return false;
/* The reply will be small, we're OK: */
if (proc->pc_xdrressize > 0 &&
proc->pc_xdrressize < XDR_QUADLEN(PAGE_SIZE))
return false;
return rqstp->rq_arg.len > PAGE_SIZE;
}
/** /**
* nfsd_dispatch - Process an NFS or NFSACL Request * nfsd_dispatch - Process an NFS or NFSACL Request
* @rqstp: incoming request * @rqstp: incoming request
@ -984,15 +1037,22 @@ nfsd(void *vrqstp)
int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
{ {
const struct svc_procedure *proc = rqstp->rq_procinfo; const struct svc_procedure *proc = rqstp->rq_procinfo;
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
__be32 *p;
dprintk("nfsd_dispatch: vers %d proc %d\n",
rqstp->rq_vers, rqstp->rq_proc);
if (nfs_request_too_big(rqstp, proc))
goto out_too_large;
/* /*
* Give the xdr decoder a chance to change this if it wants * Give the xdr decoder a chance to change this if it wants
* (necessary in the NFSv4.0 compound case) * (necessary in the NFSv4.0 compound case)
*/ */
rqstp->rq_cachetype = proc->pc_cachetype; rqstp->rq_cachetype = proc->pc_cachetype;
if (!proc->pc_decode(rqstp, argv->iov_base))
svcxdr_init_decode(rqstp);
if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
goto out_decode_err; goto out_decode_err;
switch (nfsd_cache_lookup(rqstp)) { switch (nfsd_cache_lookup(rqstp)) {
@ -1008,64 +1068,43 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
* Need to grab the location to store the status, as * Need to grab the location to store the status, as
* NFSv4 does some encoding while processing * NFSv4 does some encoding while processing
*/ */
svcxdr_init_encode(rqstp); p = resv->iov_base + resv->iov_len;
resv->iov_len += sizeof(__be32);
*statp = proc->pc_func(rqstp); *statp = proc->pc_func(rqstp);
if (test_bit(RQ_DROPME, &rqstp->rq_flags)) if (*statp == rpc_drop_reply || test_bit(RQ_DROPME, &rqstp->rq_flags))
goto out_update_drop; goto out_update_drop;
if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream)) if (!proc->pc_encode(rqstp, p))
goto out_encode_err; goto out_encode_err;
nfsd_cache_update(rqstp, rqstp->rq_cachetype, statp + 1); nfsd_cache_update(rqstp, rqstp->rq_cachetype, statp + 1);
out_cached_reply: out_cached_reply:
return 1; return 1;
out_too_large:
dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers);
*statp = rpc_garbage_args;
return 1;
out_decode_err: out_decode_err:
trace_nfsd_garbage_args_err(rqstp); dprintk("nfsd: failed to decode arguments!\n");
*statp = rpc_garbage_args; *statp = rpc_garbage_args;
return 1; return 1;
out_update_drop: out_update_drop:
dprintk("nfsd: Dropping request; may be revisited later\n");
nfsd_cache_update(rqstp, RC_NOCACHE, NULL); nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
out_dropit: out_dropit:
return 0; return 0;
out_encode_err: out_encode_err:
trace_nfsd_cant_encode_err(rqstp); dprintk("nfsd: failed to encode result!\n");
nfsd_cache_update(rqstp, RC_NOCACHE, NULL); nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
*statp = rpc_system_err; *statp = rpc_system_err;
return 1; return 1;
} }
/**
* nfssvc_decode_voidarg - Decode void arguments
* @rqstp: Server RPC transaction context
* @xdr: XDR stream positioned at arguments to decode
*
* Return values:
* %false: Arguments were not valid
* %true: Decoding was successful
*/
bool nfssvc_decode_voidarg(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return true;
}
/**
* nfssvc_encode_voidres - Encode void results
* @rqstp: Server RPC transaction context
* @xdr: XDR stream into which to encode results
*
* Return values:
* %false: Local error while encoding
* %true: Encoding was successful
*/
bool nfssvc_encode_voidres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
return true;
}
int nfsd_pool_stats_open(struct inode *inode, struct file *file) int nfsd_pool_stats_open(struct inode *inode, struct file *file)
{ {
int ret; int ret;
@ -1076,6 +1115,7 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
return -ENODEV; return -ENODEV;
} }
/* bump up the psudo refcount while traversing */
svc_get(nn->nfsd_serv); svc_get(nn->nfsd_serv);
ret = svc_pool_stats_open(nn->nfsd_serv, file); ret = svc_pool_stats_open(nn->nfsd_serv, file);
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
@ -1084,12 +1124,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
int nfsd_pool_stats_release(struct inode *inode, struct file *file) int nfsd_pool_stats_release(struct inode *inode, struct file *file)
{ {
struct seq_file *seq = file->private_data;
struct svc_serv *serv = seq->private;
int ret = seq_release(inode, file); int ret = seq_release(inode, file);
struct net *net = inode->i_sb->s_fs_info;
mutex_lock(&nfsd_mutex); mutex_lock(&nfsd_mutex);
svc_put(serv); /* this function really, really should have been called svc_put() */
nfsd_destroy(net);
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
return ret; return ret;
} }

File diff suppressed because it is too large Load Diff

View File

@ -57,11 +57,11 @@ typedef struct {
} stateid_t; } stateid_t;
typedef struct { typedef struct {
stateid_t cs_stid; stateid_t stid;
#define NFS4_COPY_STID 1 #define NFS4_COPY_STID 1
#define NFS4_COPYNOTIFY_STID 2 #define NFS4_COPYNOTIFY_STID 2
unsigned char cs_type; unsigned char sc_type;
refcount_t cs_count; refcount_t sc_count;
} copy_stateid_t; } copy_stateid_t;
struct nfsd4_callback { struct nfsd4_callback {
@ -149,7 +149,6 @@ struct nfs4_delegation {
/* For recall: */ /* For recall: */
int dl_retries; int dl_retries;
struct nfsd4_callback dl_recall; struct nfsd4_callback dl_recall;
bool dl_recalled;
}; };
#define cb_to_delegation(cb) \ #define cb_to_delegation(cb) \
@ -175,7 +174,7 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
/* Maximum number of slots per session. 160 is useful for long haul TCP */ /* Maximum number of slots per session. 160 is useful for long haul TCP */
#define NFSD_MAX_SLOTS_PER_SESSION 160 #define NFSD_MAX_SLOTS_PER_SESSION 160
/* Maximum number of operations per session compound */ /* Maximum number of operations per session compound */
#define NFSD_MAX_OPS_PER_COMPOUND 50 #define NFSD_MAX_OPS_PER_COMPOUND 16
/* Maximum session per slot cache size */ /* Maximum session per slot cache size */
#define NFSD_SLOT_CACHE_SIZE 2048 #define NFSD_SLOT_CACHE_SIZE 2048
/* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */ /* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */
@ -283,28 +282,6 @@ struct nfsd4_sessionid {
#define HEXDIR_LEN 33 /* hex version of 16 byte md5 of cl_name plus '\0' */ #define HEXDIR_LEN 33 /* hex version of 16 byte md5 of cl_name plus '\0' */
/*
* State Meaning Where set
* --------------------------------------------------------------------------
* | NFSD4_ACTIVE | Confirmed, active | Default |
* |------------------- ----------------------------------------------------|
* | NFSD4_COURTESY | Courtesy state. | nfs4_get_client_reaplist |
* | | Lease/lock/share | |
* | | reservation conflict | |
* | | can cause Courtesy | |
* | | client to be expired | |
* |------------------------------------------------------------------------|
* | NFSD4_EXPIRABLE | Courtesy client to be| nfs4_laundromat |
* | | expired by Laundromat| try_to_expire_client |
* | | due to conflict | |
* |------------------------------------------------------------------------|
*/
enum {
NFSD4_ACTIVE = 0,
NFSD4_COURTESY,
NFSD4_EXPIRABLE,
};
/* /*
* struct nfs4_client - one per client. Clientids live here. * struct nfs4_client - one per client. Clientids live here.
* *
@ -368,7 +345,6 @@ struct nfs4_client {
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */ #define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \ #define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
1 << NFSD4_CLIENT_CB_KILL) 1 << NFSD4_CLIENT_CB_KILL)
#define NFSD4_CLIENT_CB_RECALL_ANY (6)
unsigned long cl_flags; unsigned long cl_flags;
const struct cred *cl_cb_cred; const struct cred *cl_cb_cred;
struct rpc_clnt *cl_cb_client; struct rpc_clnt *cl_cb_client;
@ -395,10 +371,6 @@ struct nfs4_client {
/* debugging info directory under nfsd/clients/ : */ /* debugging info directory under nfsd/clients/ : */
struct dentry *cl_nfsd_dentry; struct dentry *cl_nfsd_dentry;
/* 'info' file within that directory. Ref is not counted,
* but will remain valid iff cl_nfsd_dentry != NULL
*/
struct dentry *cl_nfsd_info_dentry;
/* for nfs41 callbacks */ /* for nfs41 callbacks */
/* We currently support a single back channel with a single slot */ /* We currently support a single back channel with a single slot */
@ -409,13 +381,6 @@ struct nfs4_client {
struct list_head async_copies; /* list of async copies */ struct list_head async_copies; /* list of async copies */
spinlock_t async_lock; /* lock for async copies */ spinlock_t async_lock; /* lock for async copies */
atomic_t cl_cb_inflight; /* Outstanding callbacks */ atomic_t cl_cb_inflight; /* Outstanding callbacks */
unsigned int cl_state;
atomic_t cl_delegs_in_recall;
struct nfsd4_cb_recall_any *cl_ra;
time64_t cl_ra_time;
struct list_head cl_ra_cblist;
}; };
/* struct nfs4_client_reset /* struct nfs4_client_reset
@ -541,13 +506,14 @@ struct nfs4_clnt_odstate {
* inode can have multiple filehandles associated with it, so there is * inode can have multiple filehandles associated with it, so there is
* (potentially) a many to one relationship between this struct and struct * (potentially) a many to one relationship between this struct and struct
* inode. * inode.
*
* These are hashed by filehandle in the file_hashtbl, which is protected by
* the global state_lock spinlock.
*/ */
struct nfs4_file { struct nfs4_file {
refcount_t fi_ref; refcount_t fi_ref;
struct inode * fi_inode;
bool fi_aliased;
spinlock_t fi_lock; spinlock_t fi_lock;
struct rhlist_head fi_rlist; struct hlist_node fi_hash; /* hash on fi_fhandle */
struct list_head fi_stateids; struct list_head fi_stateids;
union { union {
struct list_head fi_delegations; struct list_head fi_delegations;
@ -596,10 +562,6 @@ struct nfs4_ol_stateid {
struct list_head st_locks; struct list_head st_locks;
struct nfs4_stateowner *st_stateowner; struct nfs4_stateowner *st_stateowner;
struct nfs4_clnt_odstate *st_clnt_odstate; struct nfs4_clnt_odstate *st_clnt_odstate;
/*
* These bitmasks use 3 separate bits for READ, ALLOW, and BOTH; see the
* comment above bmap_to_share_mode() for explanation:
*/
unsigned char st_access_bmap; unsigned char st_access_bmap;
unsigned char st_deny_bmap; unsigned char st_deny_bmap;
struct nfs4_ol_stateid *st_openstp; struct nfs4_ol_stateid *st_openstp;
@ -641,7 +603,6 @@ enum nfsd4_cb_op {
NFSPROC4_CLNT_CB_OFFLOAD, NFSPROC4_CLNT_CB_OFFLOAD,
NFSPROC4_CLNT_CB_SEQUENCE, NFSPROC4_CLNT_CB_SEQUENCE,
NFSPROC4_CLNT_CB_NOTIFY_LOCK, NFSPROC4_CLNT_CB_NOTIFY_LOCK,
NFSPROC4_CLNT_CB_RECALL_ANY,
}; };
/* Returns true iff a is later than b: */ /* Returns true iff a is later than b: */
@ -662,7 +623,6 @@ struct nfsd4_blocked_lock {
struct file_lock nbl_lock; struct file_lock nbl_lock;
struct knfsd_fh nbl_fh; struct knfsd_fh nbl_fh;
struct nfsd4_callback nbl_cb; struct nfsd4_callback nbl_cb;
struct kref nbl_kref;
}; };
struct nfsd4_compound_state; struct nfsd4_compound_state;
@ -689,22 +649,26 @@ void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *)
extern void nfs4_release_reclaim(struct nfsd_net *); extern void nfs4_release_reclaim(struct nfsd_net *);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct xdr_netobj name, extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct xdr_netobj name,
struct nfsd_net *nn); struct nfsd_net *nn);
extern __be32 nfs4_check_open_reclaim(struct nfs4_client *); extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_probe_callback(struct nfs4_client *clp);
extern void nfsd4_probe_callback_sync(struct nfs4_client *clp); extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op); const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op);
extern bool nfsd4_run_cb(struct nfsd4_callback *cb); extern void nfsd4_run_cb(struct nfsd4_callback *cb);
extern int nfsd4_create_callback_queue(void); extern int nfsd4_create_callback_queue(void);
extern void nfsd4_destroy_callback_queue(void); extern void nfsd4_destroy_callback_queue(void);
extern void nfsd4_shutdown_callback(struct nfs4_client *); extern void nfsd4_shutdown_callback(struct nfs4_client *);
extern void nfsd4_shutdown_copy(struct nfs4_client *clp); extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
struct xdr_netobj princhash, struct nfsd_net *nn); struct xdr_netobj princhash, struct nfsd_net *nn);
extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn); extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
struct nfs4_file *find_file(struct knfsd_fh *fh);
void put_nfs4_file(struct nfs4_file *fi); void put_nfs4_file(struct nfs4_file *fi);
extern void nfs4_put_copy(struct nfsd4_copy *copy);
extern struct nfsd4_copy * extern struct nfsd4_copy *
find_async_copy(struct nfs4_client *clp, stateid_t *staetid); find_async_copy(struct nfs4_client *clp, stateid_t *staetid);
extern void nfs4_put_cpntf_state(struct nfsd_net *nn, extern void nfs4_put_cpntf_state(struct nfsd_net *nn,
@ -729,9 +693,4 @@ extern void nfsd4_client_record_remove(struct nfs4_client *clp);
extern int nfsd4_client_record_check(struct nfs4_client *clp); extern int nfsd4_client_record_check(struct nfs4_client *clp);
extern void nfsd4_record_grace_done(struct nfsd_net *nn); extern void nfsd4_record_grace_done(struct nfsd_net *nn);
static inline bool try_to_expire_client(struct nfs4_client *clp)
{
cmpxchg(&clp->cl_state, NFSD4_COURTESY, NFSD4_EXPIRABLE);
return clp->cl_state == NFSD4_EXPIRABLE;
}
#endif /* NFSD4_STATE_H */ #endif /* NFSD4_STATE_H */

View File

@ -7,14 +7,16 @@
* Format: * Format:
* rc <hits> <misses> <nocache> * rc <hits> <misses> <nocache>
* Statistsics for the reply cache * Statistsics for the reply cache
* fh <stale> <deprecated filehandle cache stats> * fh <stale> <total-lookups> <anonlookups> <dir-not-in-dcache> <nondir-not-in-dcache>
* statistics for filehandle lookup * statistics for filehandle lookup
* io <bytes-read> <bytes-written> * io <bytes-read> <bytes-written>
* statistics for IO throughput * statistics for IO throughput
* th <threads> <deprecated thread usage histogram stats> * th <threads> <fullcnt> <10%-20%> <20%-30%> ... <90%-100%> <100%>
* number of threads * time (seconds) when nfsd thread usage above thresholds
* ra <deprecated ra-cache stats> * and number of times that all threads were in use
* * ra cache-size <10% <20% <30% ... <100% not-found
* number of times that read-ahead entry was found that deep in
* the cache.
* plus generic RPC stats (see net/sunrpc/stats.c) * plus generic RPC stats (see net/sunrpc/stats.c)
* *
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
@ -32,28 +34,35 @@ struct svc_stat nfsd_svcstats = {
.program = &nfsd_program, .program = &nfsd_program,
}; };
static int nfsd_show(struct seq_file *seq, void *v) static int nfsd_proc_show(struct seq_file *seq, void *v)
{ {
int i; int i;
seq_printf(seq, "rc %lld %lld %lld\nfh %lld 0 0 0 0\nio %lld %lld\n", seq_printf(seq, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n",
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_HITS]), nfsdstats.rchits,
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_MISSES]), nfsdstats.rcmisses,
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_NOCACHE]), nfsdstats.rcnocache,
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_FH_STALE]), nfsdstats.fh_stale,
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_IO_READ]), nfsdstats.fh_lookup,
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_IO_WRITE])); nfsdstats.fh_anon,
nfsdstats.fh_nocache_dir,
nfsdstats.fh_nocache_nondir,
nfsdstats.io_read,
nfsdstats.io_write);
/* thread usage: */ /* thread usage: */
seq_printf(seq, "th %u 0", atomic_read(&nfsdstats.th_cnt)); seq_printf(seq, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt);
for (i=0; i<10; i++) {
/* deprecated thread usage histogram stats */ unsigned int jifs = nfsdstats.th_usage[i];
for (i = 0; i < 10; i++) unsigned int sec = jifs / HZ, msec = (jifs % HZ)*1000/HZ;
seq_puts(seq, " 0.000"); seq_printf(seq, " %u.%03u", sec, msec);
}
/* deprecated ra-cache stats */
seq_puts(seq, "\nra 0 0 0 0 0 0 0 0 0 0 0 0\n");
/* newline and ra-cache */
seq_printf(seq, "\nra %u", nfsdstats.ra_size);
for (i=0; i<11; i++)
seq_printf(seq, " %u", nfsdstats.ra_depth[i]);
seq_putc(seq, '\n');
/* show my rpc info */ /* show my rpc info */
svc_seq_show(seq, &nfsd_svcstats); svc_seq_show(seq, &nfsd_svcstats);
@ -61,10 +70,8 @@ static int nfsd_show(struct seq_file *seq, void *v)
/* Show count for individual nfsv4 operations */ /* Show count for individual nfsv4 operations */
/* Writing operation numbers 0 1 2 also for maintaining uniformity */ /* Writing operation numbers 0 1 2 also for maintaining uniformity */
seq_printf(seq,"proc4ops %u", LAST_NFS4_OP + 1); seq_printf(seq,"proc4ops %u", LAST_NFS4_OP + 1);
for (i = 0; i <= LAST_NFS4_OP; i++) { for (i = 0; i <= LAST_NFS4_OP; i++)
seq_printf(seq, " %lld", seq_printf(seq, " %u", nfsdstats.nfs4_opcount[i]);
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_NFS4_OP(i)]));
}
seq_putc(seq, '\n'); seq_putc(seq, '\n');
#endif #endif
@ -72,65 +79,26 @@ static int nfsd_show(struct seq_file *seq, void *v)
return 0; return 0;
} }
DEFINE_PROC_SHOW_ATTRIBUTE(nfsd); static int nfsd_proc_open(struct inode *inode, struct file *file)
int nfsd_percpu_counters_init(struct percpu_counter counters[], int num)
{ {
int i, err = 0; return single_open(file, nfsd_proc_show, NULL);
for (i = 0; !err && i < num; i++)
err = percpu_counter_init(&counters[i], 0, GFP_KERNEL);
if (!err)
return 0;
for (; i > 0; i--)
percpu_counter_destroy(&counters[i-1]);
return err;
} }
void nfsd_percpu_counters_reset(struct percpu_counter counters[], int num) static const struct proc_ops nfsd_proc_ops = {
.proc_open = nfsd_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
void
nfsd_stat_init(void)
{ {
int i;
for (i = 0; i < num; i++)
percpu_counter_set(&counters[i], 0);
}
void nfsd_percpu_counters_destroy(struct percpu_counter counters[], int num)
{
int i;
for (i = 0; i < num; i++)
percpu_counter_destroy(&counters[i]);
}
static int nfsd_stat_counters_init(void)
{
return nfsd_percpu_counters_init(nfsdstats.counter, NFSD_STATS_COUNTERS_NUM);
}
static void nfsd_stat_counters_destroy(void)
{
nfsd_percpu_counters_destroy(nfsdstats.counter, NFSD_STATS_COUNTERS_NUM);
}
int nfsd_stat_init(void)
{
int err;
err = nfsd_stat_counters_init();
if (err)
return err;
svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_ops); svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_ops);
return 0;
} }
void nfsd_stat_shutdown(void) void
nfsd_stat_shutdown(void)
{ {
nfsd_stat_counters_destroy();
svc_proc_unregister(&init_net, "nfsd"); svc_proc_unregister(&init_net, "nfsd");
} }

View File

@ -8,89 +8,37 @@
#define _NFSD_STATS_H #define _NFSD_STATS_H
#include <uapi/linux/nfsd/stats.h> #include <uapi/linux/nfsd/stats.h>
#include <linux/percpu_counter.h>
enum {
NFSD_STATS_RC_HITS, /* repcache hits */
NFSD_STATS_RC_MISSES, /* repcache misses */
NFSD_STATS_RC_NOCACHE, /* uncached reqs */
NFSD_STATS_FH_STALE, /* FH stale error */
NFSD_STATS_IO_READ, /* bytes returned to read requests */
NFSD_STATS_IO_WRITE, /* bytes passed in write requests */
#ifdef CONFIG_NFSD_V4
NFSD_STATS_FIRST_NFS4_OP, /* count of individual nfsv4 operations */
NFSD_STATS_LAST_NFS4_OP = NFSD_STATS_FIRST_NFS4_OP + LAST_NFS4_OP,
#define NFSD_STATS_NFS4_OP(op) (NFSD_STATS_FIRST_NFS4_OP + (op))
#endif
NFSD_STATS_COUNTERS_NUM
};
struct nfsd_stats { struct nfsd_stats {
struct percpu_counter counter[NFSD_STATS_COUNTERS_NUM]; unsigned int rchits; /* repcache hits */
unsigned int rcmisses; /* repcache hits */
unsigned int rcnocache; /* uncached reqs */
unsigned int fh_stale; /* FH stale error */
unsigned int fh_lookup; /* dentry cached */
unsigned int fh_anon; /* anon file dentry returned */
unsigned int fh_nocache_dir; /* filehandle not found in dcache */
unsigned int fh_nocache_nondir; /* filehandle not found in dcache */
unsigned int io_read; /* bytes returned to read requests */
unsigned int io_write; /* bytes passed in write requests */
unsigned int th_cnt; /* number of available threads */
unsigned int th_usage[10]; /* number of ticks during which n perdeciles
* of available threads were in use */
unsigned int th_fullcnt; /* number of times last free thread was used */
unsigned int ra_size; /* size of ra cache */
unsigned int ra_depth[11]; /* number of times ra entry was found that deep
* in the cache (10percentiles). [10] = not found */
#ifdef CONFIG_NFSD_V4
unsigned int nfs4_opcount[LAST_NFS4_OP + 1]; /* count of individual nfsv4 operations */
#endif
atomic_t th_cnt; /* number of available threads */
}; };
extern struct nfsd_stats nfsdstats;
extern struct nfsd_stats nfsdstats;
extern struct svc_stat nfsd_svcstats; extern struct svc_stat nfsd_svcstats;
int nfsd_percpu_counters_init(struct percpu_counter counters[], int num); void nfsd_stat_init(void);
void nfsd_percpu_counters_reset(struct percpu_counter counters[], int num); void nfsd_stat_shutdown(void);
void nfsd_percpu_counters_destroy(struct percpu_counter counters[], int num);
int nfsd_stat_init(void);
void nfsd_stat_shutdown(void);
static inline void nfsd_stats_rc_hits_inc(void)
{
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_RC_HITS]);
}
static inline void nfsd_stats_rc_misses_inc(void)
{
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_RC_MISSES]);
}
static inline void nfsd_stats_rc_nocache_inc(void)
{
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_RC_NOCACHE]);
}
static inline void nfsd_stats_fh_stale_inc(struct svc_export *exp)
{
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_FH_STALE]);
if (exp)
percpu_counter_inc(&exp->ex_stats.counter[EXP_STATS_FH_STALE]);
}
static inline void nfsd_stats_io_read_add(struct svc_export *exp, s64 amount)
{
percpu_counter_add(&nfsdstats.counter[NFSD_STATS_IO_READ], amount);
if (exp)
percpu_counter_add(&exp->ex_stats.counter[EXP_STATS_IO_READ], amount);
}
static inline void nfsd_stats_io_write_add(struct svc_export *exp, s64 amount)
{
percpu_counter_add(&nfsdstats.counter[NFSD_STATS_IO_WRITE], amount);
if (exp)
percpu_counter_add(&exp->ex_stats.counter[EXP_STATS_IO_WRITE], amount);
}
static inline void nfsd_stats_payload_misses_inc(struct nfsd_net *nn)
{
percpu_counter_inc(&nn->counter[NFSD_NET_PAYLOAD_MISSES]);
}
static inline void nfsd_stats_drc_mem_usage_add(struct nfsd_net *nn, s64 amount)
{
percpu_counter_add(&nn->counter[NFSD_NET_DRC_MEM_USAGE], amount);
}
static inline void nfsd_stats_drc_mem_usage_sub(struct nfsd_net *nn, s64 amount)
{
percpu_counter_sub(&nn->counter[NFSD_NET_DRC_MEM_USAGE], amount);
}
#endif /* _NFSD_STATS_H */ #endif /* _NFSD_STATS_H */

View File

@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include "trace.h" #include "trace.h"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,6 @@
#ifndef LINUX_NFSD_VFS_H #ifndef LINUX_NFSD_VFS_H
#define LINUX_NFSD_VFS_H #define LINUX_NFSD_VFS_H
#include <linux/fs.h>
#include <linux/posix_acl.h>
#include "nfsfh.h" #include "nfsfh.h"
#include "nfsd.h" #include "nfsd.h"
@ -44,23 +42,6 @@ struct nfsd_file;
typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned); typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
/* nfsd/vfs.c */ /* nfsd/vfs.c */
struct nfsd_attrs {
struct iattr *na_iattr; /* input */
struct xdr_netobj *na_seclabel; /* input */
struct posix_acl *na_pacl; /* input */
struct posix_acl *na_dpacl; /* input */
int na_labelerr; /* output */
int na_aclerr; /* output */
};
static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
{
posix_acl_release(attrs->na_pacl);
posix_acl_release(attrs->na_dpacl);
}
__be32 nfserrno (int errno);
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp); struct svc_export **expp);
__be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *, __be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *,
@ -69,28 +50,32 @@ __be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
const char *, unsigned int, const char *, unsigned int,
struct svc_export **, struct dentry **); struct svc_export **, struct dentry **);
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *, __be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
struct nfsd_attrs *, int, time64_t); struct iattr *, int, time64_t);
int nfsd_mountpoint(struct dentry *, struct svc_export *); int nfsd_mountpoint(struct dentry *, struct svc_export *);
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
struct xdr_netobj *);
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *, __be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
struct file *, loff_t, loff_t, int); struct file *, loff_t, loff_t, int);
__be32 nfsd4_clone_file_range(struct svc_rqst *rqstp, __be32 nfsd4_clone_file_range(struct nfsd_file *nf_src, u64 src_pos,
struct nfsd_file *nf_src, u64 src_pos,
struct nfsd_file *nf_dst, u64 dst_pos, struct nfsd_file *nf_dst, u64 dst_pos,
u64 count, bool sync); u64 count, bool sync);
#endif /* CONFIG_NFSD_V4 */ #endif /* CONFIG_NFSD_V4 */
__be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *, __be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *,
struct nfsd_attrs *attrs, int type, dev_t rdev, char *name, int len, struct iattr *attrs,
struct svc_fh *res);
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct nfsd_attrs *attrs,
int type, dev_t rdev, struct svc_fh *res); int type, dev_t rdev, struct svc_fh *res);
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct iattr *attrs,
int type, dev_t rdev, struct svc_fh *res);
#ifdef CONFIG_NFSD_V3
__be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *); __be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *);
__be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *,
struct svc_fh *resfhp, struct nfsd_attrs *iap); char *name, int len, struct iattr *attrs,
__be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp, struct svc_fh *res, int createmode,
struct nfsd_file *nf, u64 offset, u32 count, u32 *verifier, bool *truncp, bool *created);
__be32 *verf); __be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
loff_t, unsigned long, __be32 *verf);
#endif /* CONFIG_NFSD_V3 */
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
__be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *name, void **bufp, int *lenp); char *name, void **bufp, int *lenp);
@ -104,7 +89,7 @@ __be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
int nfsd_open_break_lease(struct inode *, int); int nfsd_open_break_lease(struct inode *, int);
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t, __be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **); int, struct file **);
__be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *, __be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **); int, struct file **);
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset, struct file *file, loff_t offset,
@ -128,9 +113,8 @@ __be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *, __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
char *, int *); char *, int *);
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *, __be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
char *name, int len, char *path, char *name, int len, char *path,
struct nfsd_attrs *attrs, struct svc_fh *res);
struct svc_fh *res);
__be32 nfsd_link(struct svc_rqst *, struct svc_fh *, __be32 nfsd_link(struct svc_rqst *, struct svc_fh *,
char *, int, struct svc_fh *); char *, int, struct svc_fh *);
ssize_t nfsd_copy_file_range(struct file *, u64, ssize_t nfsd_copy_file_range(struct file *, u64,
@ -168,7 +152,7 @@ static inline void fh_drop_write(struct svc_fh *fh)
} }
} }
static inline __be32 fh_getattr(const struct svc_fh *fh, struct kstat *stat) static inline __be32 fh_getattr(struct svc_fh *fh, struct kstat *stat)
{ {
struct path p = {.mnt = fh->fh_export->ex_path.mnt, struct path p = {.mnt = fh->fh_export->ex_path.mnt,
.dentry = fh->fh_dentry}; .dentry = fh->fh_dentry};
@ -176,4 +160,10 @@ static inline __be32 fh_getattr(const struct svc_fh *fh, struct kstat *stat)
AT_STATX_SYNC_AS_STAT)); AT_STATX_SYNC_AS_STAT));
} }
static inline int nfsd_create_is_exclusive(int createmode)
{
return createmode == NFS3_CREATE_EXCLUSIVE
|| createmode == NFS4_CREATE_EXCLUSIVE4_1;
}
#endif /* LINUX_NFSD_VFS_H */ #endif /* LINUX_NFSD_VFS_H */

View File

@ -27,13 +27,14 @@ struct nfsd_readargs {
struct svc_fh fh; struct svc_fh fh;
__u32 offset; __u32 offset;
__u32 count; __u32 count;
int vlen;
}; };
struct nfsd_writeargs { struct nfsd_writeargs {
svc_fh fh; svc_fh fh;
__u32 offset; __u32 offset;
__u32 len; __u32 len;
struct xdr_buf payload; struct kvec first;
}; };
struct nfsd_createargs { struct nfsd_createargs {
@ -52,6 +53,11 @@ struct nfsd_renameargs {
unsigned int tlen; unsigned int tlen;
}; };
struct nfsd_readlinkargs {
struct svc_fh fh;
char * buffer;
};
struct nfsd_linkargs { struct nfsd_linkargs {
struct svc_fh ffh; struct svc_fh ffh;
struct svc_fh tfh; struct svc_fh tfh;
@ -73,6 +79,7 @@ struct nfsd_readdirargs {
struct svc_fh fh; struct svc_fh fh;
__u32 cookie; __u32 cookie;
__u32 count; __u32 count;
__be32 * buffer;
}; };
struct nfsd_stat { struct nfsd_stat {
@ -94,7 +101,6 @@ struct nfsd_diropres {
struct nfsd_readlinkres { struct nfsd_readlinkres {
__be32 status; __be32 status;
int len; int len;
struct page *page;
}; };
struct nfsd_readres { struct nfsd_readres {
@ -102,20 +108,17 @@ struct nfsd_readres {
struct svc_fh fh; struct svc_fh fh;
unsigned long count; unsigned long count;
struct kstat stat; struct kstat stat;
struct page **pages;
}; };
struct nfsd_readdirres { struct nfsd_readdirres {
/* Components of the reply */
__be32 status; __be32 status;
int count; int count;
/* Used to encode the reply's entry list */
struct xdr_stream xdr;
struct xdr_buf dirlist;
struct readdir_cd common; struct readdir_cd common;
unsigned int cookie_offset; __be32 * buffer;
int buflen;
__be32 * offset;
}; };
struct nfsd_statfsres { struct nfsd_statfsres {
@ -141,37 +144,36 @@ union nfsd_xdrstore {
#define NFS2_SVC_XDRSIZE sizeof(union nfsd_xdrstore) #define NFS2_SVC_XDRSIZE sizeof(union nfsd_xdrstore)
bool nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_void(struct svc_rqst *, __be32 *);
bool nfssvc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_fhandle(struct svc_rqst *, __be32 *);
bool nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_sattrargs(struct svc_rqst *, __be32 *);
bool nfssvc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_diropargs(struct svc_rqst *, __be32 *);
bool nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_readargs(struct svc_rqst *, __be32 *);
bool nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_writeargs(struct svc_rqst *, __be32 *);
bool nfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_createargs(struct svc_rqst *, __be32 *);
bool nfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_renameargs(struct svc_rqst *, __be32 *);
bool nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_readlinkargs(struct svc_rqst *, __be32 *);
bool nfssvc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_decode_linkargs(struct svc_rqst *, __be32 *);
int nfssvc_decode_symlinkargs(struct svc_rqst *, __be32 *);
int nfssvc_decode_readdirargs(struct svc_rqst *, __be32 *);
int nfssvc_encode_void(struct svc_rqst *, __be32 *);
int nfssvc_encode_stat(struct svc_rqst *, __be32 *);
int nfssvc_encode_attrstat(struct svc_rqst *, __be32 *);
int nfssvc_encode_diropres(struct svc_rqst *, __be32 *);
int nfssvc_encode_readlinkres(struct svc_rqst *, __be32 *);
int nfssvc_encode_readres(struct svc_rqst *, __be32 *);
int nfssvc_encode_statfsres(struct svc_rqst *, __be32 *);
int nfssvc_encode_readdirres(struct svc_rqst *, __be32 *);
bool nfssvc_encode_statres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfssvc_encode_entry(void *, const char *name,
bool nfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int namlen, loff_t offset, u64 ino, unsigned int);
bool nfssvc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset);
int nfssvc_encode_entry(void *data, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type);
void nfssvc_release_attrstat(struct svc_rqst *rqstp); void nfssvc_release_attrstat(struct svc_rqst *rqstp);
void nfssvc_release_diropres(struct svc_rqst *rqstp); void nfssvc_release_diropres(struct svc_rqst *rqstp);
void nfssvc_release_readres(struct svc_rqst *rqstp); void nfssvc_release_readres(struct svc_rqst *rqstp);
/* Helper functions for NFSv2 ACL code */ /* Helper functions for NFSv2 ACL code */
bool svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp); __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat);
bool svcxdr_encode_stat(struct xdr_stream *xdr, __be32 status); __be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp);
bool svcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
const struct svc_fh *fhp, const struct kstat *stat);
#endif /* LINUX_NFSD_H */ #endif /* LINUX_NFSD_H */

View File

@ -25,13 +25,14 @@ struct nfsd3_diropargs {
struct nfsd3_accessargs { struct nfsd3_accessargs {
struct svc_fh fh; struct svc_fh fh;
__u32 access; unsigned int access;
}; };
struct nfsd3_readargs { struct nfsd3_readargs {
struct svc_fh fh; struct svc_fh fh;
__u64 offset; __u64 offset;
__u32 count; __u32 count;
int vlen;
}; };
struct nfsd3_writeargs { struct nfsd3_writeargs {
@ -40,7 +41,7 @@ struct nfsd3_writeargs {
__u32 count; __u32 count;
int stable; int stable;
__u32 len; __u32 len;
struct xdr_buf payload; struct kvec first;
}; };
struct nfsd3_createargs { struct nfsd3_createargs {
@ -70,6 +71,11 @@ struct nfsd3_renameargs {
unsigned int tlen; unsigned int tlen;
}; };
struct nfsd3_readlinkargs {
struct svc_fh fh;
char * buffer;
};
struct nfsd3_linkargs { struct nfsd3_linkargs {
struct svc_fh ffh; struct svc_fh ffh;
struct svc_fh tfh; struct svc_fh tfh;
@ -90,8 +96,10 @@ struct nfsd3_symlinkargs {
struct nfsd3_readdirargs { struct nfsd3_readdirargs {
struct svc_fh fh; struct svc_fh fh;
__u64 cookie; __u64 cookie;
__u32 dircount;
__u32 count; __u32 count;
__be32 * verf; __be32 * verf;
__be32 * buffer;
}; };
struct nfsd3_commitargs { struct nfsd3_commitargs {
@ -102,13 +110,13 @@ struct nfsd3_commitargs {
struct nfsd3_getaclargs { struct nfsd3_getaclargs {
struct svc_fh fh; struct svc_fh fh;
__u32 mask; int mask;
}; };
struct posix_acl; struct posix_acl;
struct nfsd3_setaclargs { struct nfsd3_setaclargs {
struct svc_fh fh; struct svc_fh fh;
__u32 mask; int mask;
struct posix_acl *acl_access; struct posix_acl *acl_access;
struct posix_acl *acl_default; struct posix_acl *acl_default;
}; };
@ -137,7 +145,6 @@ struct nfsd3_readlinkres {
__be32 status; __be32 status;
struct svc_fh fh; struct svc_fh fh;
__u32 len; __u32 len;
struct page **pages;
}; };
struct nfsd3_readres { struct nfsd3_readres {
@ -145,7 +152,6 @@ struct nfsd3_readres {
struct svc_fh fh; struct svc_fh fh;
unsigned long count; unsigned long count;
__u32 eof; __u32 eof;
struct page **pages;
}; };
struct nfsd3_writeres { struct nfsd3_writeres {
@ -169,17 +175,19 @@ struct nfsd3_linkres {
}; };
struct nfsd3_readdirres { struct nfsd3_readdirres {
/* Components of the reply */
__be32 status; __be32 status;
struct svc_fh fh; struct svc_fh fh;
/* Just to save kmalloc on every readdirplus entry (svc_fh is a
* little large for the stack): */
struct svc_fh scratch;
int count;
__be32 verf[2]; __be32 verf[2];
/* Used to encode the reply's entry list */
struct xdr_stream xdr;
struct xdr_buf dirlist;
struct svc_fh scratch;
struct readdir_cd common; struct readdir_cd common;
unsigned int cookie_offset; __be32 * buffer;
int buflen;
__be32 * offset;
__be32 * offset1;
struct svc_rqst * rqstp; struct svc_rqst * rqstp;
}; };
@ -265,50 +273,52 @@ union nfsd3_xdrstore {
#define NFS3_SVC_XDRSIZE sizeof(union nfsd3_xdrstore) #define NFS3_SVC_XDRSIZE sizeof(union nfsd3_xdrstore)
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_voidarg(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_fhandle(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_sattrargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_diropargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_accessargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_readargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_writeargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_createargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_mkdirargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_mknodargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_renameargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_readlinkargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_linkargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_symlinkargs(struct svc_rqst *, __be32 *);
bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_readdirargs(struct svc_rqst *, __be32 *);
int nfs3svc_decode_readdirplusargs(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_getattrres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_decode_commitargs(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_voidres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_attrstat(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_wccstat(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_diropres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_accessres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_readlinkres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_readres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_writeres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_createres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_renameres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_linkres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_readdirres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_fsstatres(struct svc_rqst *, __be32 *);
bool nfs3svc_encode_commitres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs3svc_encode_fsinfores(struct svc_rqst *, __be32 *);
int nfs3svc_encode_pathconfres(struct svc_rqst *, __be32 *);
int nfs3svc_encode_commitres(struct svc_rqst *, __be32 *);
void nfs3svc_release_fhandle(struct svc_rqst *); void nfs3svc_release_fhandle(struct svc_rqst *);
void nfs3svc_release_fhandle2(struct svc_rqst *); void nfs3svc_release_fhandle2(struct svc_rqst *);
int nfs3svc_encode_entry(void *, const char *name,
void nfs3svc_encode_cookie3(struct nfsd3_readdirres *resp, u64 offset); int namlen, loff_t offset, u64 ino,
int nfs3svc_encode_entry3(void *data, const char *name, int namlen, unsigned int);
loff_t offset, u64 ino, unsigned int d_type); int nfs3svc_encode_entry_plus(void *, const char *name,
int nfs3svc_encode_entryplus3(void *data, const char *name, int namlen, int namlen, loff_t offset, u64 ino,
loff_t offset, u64 ino, unsigned int d_type); unsigned int);
/* Helper functions for NFSv3 ACL code */ /* Helper functions for NFSv3 ACL code */
bool svcxdr_decode_nfs_fh3(struct xdr_stream *xdr, struct svc_fh *fhp); __be32 *nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p,
bool svcxdr_encode_nfsstat3(struct xdr_stream *xdr, __be32 status); struct svc_fh *fhp);
bool svcxdr_encode_post_op_attr(struct svc_rqst *rqstp, struct xdr_stream *xdr, __be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp);
const struct svc_fh *fhp);
#endif /* _LINUX_NFSD_XDR3_H */ #endif /* _LINUX_NFSD_XDR3_H */

View File

@ -76,7 +76,12 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
struct nfsd4_change_info { struct nfsd4_change_info {
u32 atomic; u32 atomic;
bool change_supported;
u32 before_ctime_sec;
u32 before_ctime_nsec;
u64 before_change; u64 before_change;
u32 after_ctime_sec;
u32 after_ctime_nsec;
u64 after_change; u64 after_change;
}; };
@ -247,8 +252,7 @@ struct nfsd4_listxattrs {
struct nfsd4_open { struct nfsd4_open {
u32 op_claim_type; /* request */ u32 op_claim_type; /* request */
u32 op_fnamelen; struct xdr_netobj op_fname; /* request - everything but CLAIM_PREV */
char * op_fname; /* request - everything but CLAIM_PREV */
u32 op_delegate_type; /* request - CLAIM_PREV only */ u32 op_delegate_type; /* request - CLAIM_PREV only */
stateid_t op_delegate_stateid; /* request - response */ stateid_t op_delegate_stateid; /* request - response */
u32 op_why_no_deleg; /* response - DELEG_NONE_EXT only */ u32 op_why_no_deleg; /* response - DELEG_NONE_EXT only */
@ -273,13 +277,11 @@ struct nfsd4_open {
bool op_truncate; /* used during processing */ bool op_truncate; /* used during processing */
bool op_created; /* used during processing */ bool op_created; /* used during processing */
struct nfs4_openowner *op_openowner; /* used during processing */ struct nfs4_openowner *op_openowner; /* used during processing */
struct file *op_filp; /* used during processing */
struct nfs4_file *op_file; /* used during processing */ struct nfs4_file *op_file; /* used during processing */
struct nfs4_ol_stateid *op_stp; /* used during processing */ struct nfs4_ol_stateid *op_stp; /* used during processing */
struct nfs4_clnt_odstate *op_odstate; /* used during processing */ struct nfs4_clnt_odstate *op_odstate; /* used during processing */
struct nfs4_acl *op_acl; struct nfs4_acl *op_acl;
struct xdr_netobj op_label; struct xdr_netobj op_label;
struct svc_rqst *op_rqstp;
}; };
struct nfsd4_open_confirm { struct nfsd4_open_confirm {
@ -303,10 +305,9 @@ struct nfsd4_read {
u32 rd_length; /* request */ u32 rd_length; /* request */
int rd_vlen; int rd_vlen;
struct nfsd_file *rd_nf; struct nfsd_file *rd_nf;
struct svc_rqst *rd_rqstp; /* response */ struct svc_rqst *rd_rqstp; /* response */
struct svc_fh *rd_fhp; /* response */ struct svc_fh *rd_fhp; /* response */
u32 rd_eof; /* response */
}; };
struct nfsd4_readdir { struct nfsd4_readdir {
@ -384,6 +385,13 @@ struct nfsd4_setclientid_confirm {
nfs4_verifier sc_confirm; nfs4_verifier sc_confirm;
}; };
struct nfsd4_saved_compoundargs {
__be32 *p;
__be32 *end;
int pagelen;
struct page **pagelist;
};
struct nfsd4_test_stateid_id { struct nfsd4_test_stateid_id {
__be32 ts_id_status; __be32 ts_id_status;
stateid_t ts_id_stateid; stateid_t ts_id_stateid;
@ -411,7 +419,8 @@ struct nfsd4_write {
u64 wr_offset; /* request */ u64 wr_offset; /* request */
u32 wr_stable_how; /* request */ u32 wr_stable_how; /* request */
u32 wr_buflen; /* request */ u32 wr_buflen; /* request */
struct xdr_buf wr_payload; /* request */ struct kvec wr_head;
struct page ** wr_pagelist; /* request */
u32 wr_bytes_written; /* response */ u32 wr_bytes_written; /* response */
u32 wr_how_written; /* response */ u32 wr_how_written; /* response */
@ -424,7 +433,7 @@ struct nfsd4_exchange_id {
u32 flags; u32 flags;
clientid_t clientid; clientid_t clientid;
u32 seqid; u32 seqid;
u32 spa_how; int spa_how;
u32 spo_must_enforce[3]; u32 spo_must_enforce[3];
u32 spo_must_allow[3]; u32 spo_must_allow[3];
struct xdr_netobj nii_domain; struct xdr_netobj nii_domain;
@ -534,13 +543,6 @@ struct nfsd42_write_res {
stateid_t cb_stateid; stateid_t cb_stateid;
}; };
struct nfsd4_cb_offload {
struct nfsd4_callback co_cb;
struct nfsd42_write_res co_res;
__be32 co_nfserr;
struct knfsd_fh co_fh;
};
struct nfsd4_copy { struct nfsd4_copy {
/* request */ /* request */
stateid_t cp_src_stateid; stateid_t cp_src_stateid;
@ -548,16 +550,18 @@ struct nfsd4_copy {
u64 cp_src_pos; u64 cp_src_pos;
u64 cp_dst_pos; u64 cp_dst_pos;
u64 cp_count; u64 cp_count;
struct nl4_server *cp_src; struct nl4_server cp_src;
bool cp_intra;
unsigned long cp_flags; /* both */
#define NFSD4_COPY_F_STOPPED (0) bool cp_synchronous;
#define NFSD4_COPY_F_INTRA (1)
#define NFSD4_COPY_F_SYNCHRONOUS (2)
#define NFSD4_COPY_F_COMMITTED (3)
/* response */ /* response */
struct nfsd42_write_res cp_res; struct nfsd42_write_res cp_res;
/* for cb_offload */
struct nfsd4_callback cp_cb;
__be32 nfserr;
struct knfsd_fh fh; struct knfsd_fh fh;
struct nfs4_client *cp_clp; struct nfs4_client *cp_clp;
@ -570,34 +574,13 @@ struct nfsd4_copy {
struct list_head copies; struct list_head copies;
struct task_struct *copy_task; struct task_struct *copy_task;
refcount_t refcount; refcount_t refcount;
bool stopped;
struct nfsd4_ssc_umount_item *ss_nsui; struct vfsmount *ss_mnt;
struct nfs_fh c_fh; struct nfs_fh c_fh;
nfs4_stateid stateid; nfs4_stateid stateid;
}; };
extern bool inter_copy_offload_enable;
static inline void nfsd4_copy_set_sync(struct nfsd4_copy *copy, bool sync)
{
if (sync)
set_bit(NFSD4_COPY_F_SYNCHRONOUS, &copy->cp_flags);
else
clear_bit(NFSD4_COPY_F_SYNCHRONOUS, &copy->cp_flags);
}
static inline bool nfsd4_copy_is_sync(const struct nfsd4_copy *copy)
{
return test_bit(NFSD4_COPY_F_SYNCHRONOUS, &copy->cp_flags);
}
static inline bool nfsd4_copy_is_async(const struct nfsd4_copy *copy)
{
return !test_bit(NFSD4_COPY_F_SYNCHRONOUS, &copy->cp_flags);
}
static inline bool nfsd4_ssc_is_inter(const struct nfsd4_copy *copy)
{
return !test_bit(NFSD4_COPY_F_INTRA, &copy->cp_flags);
}
struct nfsd4_seek { struct nfsd4_seek {
/* request */ /* request */
@ -622,20 +605,19 @@ struct nfsd4_offload_status {
struct nfsd4_copy_notify { struct nfsd4_copy_notify {
/* request */ /* request */
stateid_t cpn_src_stateid; stateid_t cpn_src_stateid;
struct nl4_server *cpn_dst; struct nl4_server cpn_dst;
/* response */ /* response */
stateid_t cpn_cnr_stateid; stateid_t cpn_cnr_stateid;
u64 cpn_sec; u64 cpn_sec;
u32 cpn_nsec; u32 cpn_nsec;
struct nl4_server *cpn_src; struct nl4_server cpn_src;
}; };
struct nfsd4_op { struct nfsd4_op {
u32 opnum; int opnum;
const struct nfsd4_operation * opdesc;
__be32 status; __be32 status;
const struct nfsd4_operation *opdesc;
struct nfs4_replay *replay;
union nfsd4_op_u { union nfsd4_op_u {
struct nfsd4_access access; struct nfsd4_access access;
struct nfsd4_close close; struct nfsd4_close close;
@ -699,6 +681,7 @@ struct nfsd4_op {
struct nfsd4_listxattrs listxattrs; struct nfsd4_listxattrs listxattrs;
struct nfsd4_removexattr removexattr; struct nfsd4_removexattr removexattr;
} u; } u;
struct nfs4_replay * replay;
}; };
bool nfsd4_cache_this_op(struct nfsd4_op *); bool nfsd4_cache_this_op(struct nfsd4_op *);
@ -713,29 +696,35 @@ struct svcxdr_tmpbuf {
struct nfsd4_compoundargs { struct nfsd4_compoundargs {
/* scratch variables for XDR decode */ /* scratch variables for XDR decode */
struct xdr_stream *xdr; __be32 * p;
__be32 * end;
struct page ** pagelist;
int pagelen;
bool tail;
__be32 tmp[8];
__be32 * tmpp;
struct svcxdr_tmpbuf *to_free; struct svcxdr_tmpbuf *to_free;
struct svc_rqst *rqstp; struct svc_rqst *rqstp;
char * tag;
u32 taglen; u32 taglen;
char * tag;
u32 minorversion; u32 minorversion;
u32 client_opcnt;
u32 opcnt; u32 opcnt;
struct nfsd4_op *ops; struct nfsd4_op *ops;
struct nfsd4_op iops[8]; struct nfsd4_op iops[8];
int cachetype;
}; };
struct nfsd4_compoundres { struct nfsd4_compoundres {
/* scratch variables for XDR encode */ /* scratch variables for XDR encode */
struct xdr_stream *xdr; struct xdr_stream xdr;
struct svc_rqst * rqstp; struct svc_rqst * rqstp;
__be32 *statusp;
char * tag;
u32 taglen; u32 taglen;
char * tag;
u32 opcnt; u32 opcnt;
__be32 * tagp; /* tag, opcount encode location */
struct nfsd4_compound_state cstate; struct nfsd4_compound_state cstate;
}; };
@ -778,16 +767,24 @@ static inline void
set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp) set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp)
{ {
BUG_ON(!fhp->fh_pre_saved); BUG_ON(!fhp->fh_pre_saved);
cinfo->atomic = (u32)(fhp->fh_post_saved && !fhp->fh_no_atomic_attr); cinfo->atomic = (u32)fhp->fh_post_saved;
cinfo->change_supported = IS_I_VERSION(d_inode(fhp->fh_dentry));
cinfo->before_change = fhp->fh_pre_change; cinfo->before_change = fhp->fh_pre_change;
cinfo->after_change = fhp->fh_post_change; cinfo->after_change = fhp->fh_post_change;
cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec;
cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec;
cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec;
cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec;
} }
bool nfsd4_mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp); bool nfsd4_mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp);
bool nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs4svc_decode_voidarg(struct svc_rqst *, __be32 *);
bool nfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr); int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *);
int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *);
int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *);
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32); __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *); void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op); void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op);
@ -888,19 +885,13 @@ struct nfsd4_operation {
u32 op_flags; u32 op_flags;
char *op_name; char *op_name;
/* Try to get response size before operation */ /* Try to get response size before operation */
u32 (*op_rsize_bop)(const struct svc_rqst *rqstp, u32 (*op_rsize_bop)(struct svc_rqst *, struct nfsd4_op *);
const struct nfsd4_op *op);
void (*op_get_currentstateid)(struct nfsd4_compound_state *, void (*op_get_currentstateid)(struct nfsd4_compound_state *,
union nfsd4_op_u *); union nfsd4_op_u *);
void (*op_set_currentstateid)(struct nfsd4_compound_state *, void (*op_set_currentstateid)(struct nfsd4_compound_state *,
union nfsd4_op_u *); union nfsd4_op_u *);
}; };
struct nfsd4_cb_recall_any {
struct nfsd4_callback ra_cb;
u32 ra_keep;
u32 ra_bmval[1];
};
#endif #endif

View File

@ -48,9 +48,3 @@
#define NFS4_dec_cb_offload_sz (cb_compound_dec_hdr_sz + \ #define NFS4_dec_cb_offload_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \ cb_sequence_dec_sz + \
op_dec_sz) op_dec_sz)
#define NFS4_enc_cb_recall_any_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + 1 + 1)
#define NFS4_dec_cb_recall_any_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \
op_dec_sz)

View File

@ -150,7 +150,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
return; return;
dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
fsnotify_group_lock(dnotify_group); mutex_lock(&dnotify_group->mark_mutex);
spin_lock(&fsn_mark->lock); spin_lock(&fsn_mark->lock);
prev = &dn_mark->dn; prev = &dn_mark->dn;
@ -173,7 +173,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
free = true; free = true;
} }
fsnotify_group_unlock(dnotify_group); mutex_unlock(&dnotify_group->mark_mutex);
if (free) if (free)
fsnotify_free_mark(fsn_mark); fsnotify_free_mark(fsn_mark);
@ -196,7 +196,7 @@ static __u32 convert_arg(unsigned long arg)
if (arg & DN_ATTRIB) if (arg & DN_ATTRIB)
new_mask |= FS_ATTRIB; new_mask |= FS_ATTRIB;
if (arg & DN_RENAME) if (arg & DN_RENAME)
new_mask |= FS_RENAME; new_mask |= FS_DN_RENAME;
if (arg & DN_CREATE) if (arg & DN_CREATE)
new_mask |= (FS_CREATE | FS_MOVED_TO); new_mask |= (FS_CREATE | FS_MOVED_TO);
@ -306,7 +306,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
new_dn_mark->dn = NULL; new_dn_mark->dn = NULL;
/* this is needed to prevent the fcntl/close race described below */ /* this is needed to prevent the fcntl/close race described below */
fsnotify_group_lock(dnotify_group); mutex_lock(&dnotify_group->mark_mutex);
/* add the new_fsn_mark or find an old one. */ /* add the new_fsn_mark or find an old one. */
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group); fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group);
@ -316,7 +316,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
} else { } else {
error = fsnotify_add_inode_mark_locked(new_fsn_mark, inode, 0); error = fsnotify_add_inode_mark_locked(new_fsn_mark, inode, 0);
if (error) { if (error) {
fsnotify_group_unlock(dnotify_group); mutex_unlock(&dnotify_group->mark_mutex);
goto out_err; goto out_err;
} }
spin_lock(&new_fsn_mark->lock); spin_lock(&new_fsn_mark->lock);
@ -327,7 +327,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
} }
rcu_read_lock(); rcu_read_lock();
f = lookup_fd_rcu(fd); f = fcheck(fd);
rcu_read_unlock(); rcu_read_unlock();
/* if (f != filp) means that we lost a race and another task/thread /* if (f != filp) means that we lost a race and another task/thread
@ -365,7 +365,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
if (destroy) if (destroy)
fsnotify_detach_mark(fsn_mark); fsnotify_detach_mark(fsn_mark);
fsnotify_group_unlock(dnotify_group); mutex_unlock(&dnotify_group->mark_mutex);
if (destroy) if (destroy)
fsnotify_free_mark(fsn_mark); fsnotify_free_mark(fsn_mark);
fsnotify_put_mark(fsn_mark); fsnotify_put_mark(fsn_mark);
@ -383,8 +383,7 @@ static int __init dnotify_init(void)
SLAB_PANIC|SLAB_ACCOUNT); SLAB_PANIC|SLAB_ACCOUNT);
dnotify_mark_cache = KMEM_CACHE(dnotify_mark, SLAB_PANIC|SLAB_ACCOUNT); dnotify_mark_cache = KMEM_CACHE(dnotify_mark, SLAB_PANIC|SLAB_ACCOUNT);
dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops, dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops);
FSNOTIFY_GROUP_NOFS);
if (IS_ERR(dnotify_group)) if (IS_ERR(dnotify_group))
panic("unable to allocate fsnotify group for dnotify\n"); panic("unable to allocate fsnotify group for dnotify\n");
return 0; return 0;

View File

@ -14,33 +14,20 @@
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/stringhash.h>
#include "fanotify.h" #include "fanotify.h"
static bool fanotify_path_equal(const struct path *p1, const struct path *p2) static bool fanotify_path_equal(struct path *p1, struct path *p2)
{ {
return p1->mnt == p2->mnt && p1->dentry == p2->dentry; return p1->mnt == p2->mnt && p1->dentry == p2->dentry;
} }
static unsigned int fanotify_hash_path(const struct path *path)
{
return hash_ptr(path->dentry, FANOTIFY_EVENT_HASH_BITS) ^
hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
}
static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1, static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
__kernel_fsid_t *fsid2) __kernel_fsid_t *fsid2)
{ {
return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1]; return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
} }
static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
{
return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^
hash_32(fsid->val[1], FANOTIFY_EVENT_HASH_BITS);
}
static bool fanotify_fh_equal(struct fanotify_fh *fh1, static bool fanotify_fh_equal(struct fanotify_fh *fh1,
struct fanotify_fh *fh2) struct fanotify_fh *fh2)
{ {
@ -51,16 +38,6 @@ static bool fanotify_fh_equal(struct fanotify_fh *fh1,
!memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len); !memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len);
} }
static unsigned int fanotify_hash_fh(struct fanotify_fh *fh)
{
long salt = (long)fh->type | (long)fh->len << 8;
/*
* full_name_hash() works long by long, so it handles fh buf optimally.
*/
return full_name_hash((void *)salt, fanotify_fh_buf(fh), fh->len);
}
static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1, static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1,
struct fanotify_fid_event *ffe2) struct fanotify_fid_event *ffe2)
{ {
@ -76,10 +53,8 @@ static bool fanotify_info_equal(struct fanotify_info *info1,
struct fanotify_info *info2) struct fanotify_info *info2)
{ {
if (info1->dir_fh_totlen != info2->dir_fh_totlen || if (info1->dir_fh_totlen != info2->dir_fh_totlen ||
info1->dir2_fh_totlen != info2->dir2_fh_totlen ||
info1->file_fh_totlen != info2->file_fh_totlen || info1->file_fh_totlen != info2->file_fh_totlen ||
info1->name_len != info2->name_len || info1->name_len != info2->name_len)
info1->name2_len != info2->name2_len)
return false; return false;
if (info1->dir_fh_totlen && if (info1->dir_fh_totlen &&
@ -87,24 +62,14 @@ static bool fanotify_info_equal(struct fanotify_info *info1,
fanotify_info_dir_fh(info2))) fanotify_info_dir_fh(info2)))
return false; return false;
if (info1->dir2_fh_totlen &&
!fanotify_fh_equal(fanotify_info_dir2_fh(info1),
fanotify_info_dir2_fh(info2)))
return false;
if (info1->file_fh_totlen && if (info1->file_fh_totlen &&
!fanotify_fh_equal(fanotify_info_file_fh(info1), !fanotify_fh_equal(fanotify_info_file_fh(info1),
fanotify_info_file_fh(info2))) fanotify_info_file_fh(info2)))
return false; return false;
if (info1->name_len && return !info1->name_len ||
memcmp(fanotify_info_name(info1), fanotify_info_name(info2), !memcmp(fanotify_info_name(info1), fanotify_info_name(info2),
info1->name_len)) info1->name_len);
return false;
return !info1->name2_len ||
!memcmp(fanotify_info_name2(info1), fanotify_info_name2(info2),
info1->name2_len);
} }
static bool fanotify_name_event_equal(struct fanotify_name_event *fne1, static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
@ -123,22 +88,16 @@ static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
return fanotify_info_equal(info1, info2); return fanotify_info_equal(info1, info2);
} }
static bool fanotify_error_event_equal(struct fanotify_error_event *fee1, static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
struct fanotify_error_event *fee2) struct fsnotify_event *new_fsn)
{ {
/* Error events against the same file system are always merged. */ struct fanotify_event *old, *new;
if (!fanotify_fsid_equal(&fee1->fsid, &fee2->fsid))
return false;
return true; pr_debug("%s: old=%p new=%p\n", __func__, old_fsn, new_fsn);
} old = FANOTIFY_E(old_fsn);
new = FANOTIFY_E(new_fsn);
static bool fanotify_should_merge(struct fanotify_event *old, if (old_fsn->objectid != new_fsn->objectid ||
struct fanotify_event *new)
{
pr_debug("%s: old=%p new=%p\n", __func__, old, new);
if (old->hash != new->hash ||
old->type != new->type || old->pid != new->pid) old->type != new->type || old->pid != new->pid)
return false; return false;
@ -153,13 +112,6 @@ static bool fanotify_should_merge(struct fanotify_event *old,
if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR)) if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR))
return false; return false;
/*
* FAN_RENAME event is reported with special info record types,
* so we cannot merge it with other events.
*/
if ((old->mask & FAN_RENAME) != (new->mask & FAN_RENAME))
return false;
switch (old->type) { switch (old->type) {
case FANOTIFY_EVENT_TYPE_PATH: case FANOTIFY_EVENT_TYPE_PATH:
return fanotify_path_equal(fanotify_event_path(old), return fanotify_path_equal(fanotify_event_path(old),
@ -170,9 +122,6 @@ static bool fanotify_should_merge(struct fanotify_event *old,
case FANOTIFY_EVENT_TYPE_FID_NAME: case FANOTIFY_EVENT_TYPE_FID_NAME:
return fanotify_name_event_equal(FANOTIFY_NE(old), return fanotify_name_event_equal(FANOTIFY_NE(old),
FANOTIFY_NE(new)); FANOTIFY_NE(new));
case FANOTIFY_EVENT_TYPE_FS_ERROR:
return fanotify_error_event_equal(FANOTIFY_EE(old),
FANOTIFY_EE(new));
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
@ -184,16 +133,14 @@ static bool fanotify_should_merge(struct fanotify_event *old,
#define FANOTIFY_MAX_MERGE_EVENTS 128 #define FANOTIFY_MAX_MERGE_EVENTS 128
/* and the list better be locked by something too! */ /* and the list better be locked by something too! */
static int fanotify_merge(struct fsnotify_group *group, static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
struct fsnotify_event *event)
{ {
struct fanotify_event *old, *new = FANOTIFY_E(event); struct fsnotify_event *test_event;
unsigned int bucket = fanotify_event_hash_bucket(group, new); struct fanotify_event *new;
struct hlist_head *hlist = &group->fanotify_data.merge_hash[bucket];
int i = 0; int i = 0;
pr_debug("%s: group=%p event=%p bucket=%u\n", __func__, pr_debug("%s: list=%p event=%p\n", __func__, list, event);
group, event, bucket); new = FANOTIFY_E(event);
/* /*
* Don't merge a permission event with any other event so that we know * Don't merge a permission event with any other event so that we know
@ -203,15 +150,11 @@ static int fanotify_merge(struct fsnotify_group *group,
if (fanotify_is_perm_event(new->mask)) if (fanotify_is_perm_event(new->mask))
return 0; return 0;
hlist_for_each_entry(old, hlist, merge_list) { list_for_each_entry_reverse(test_event, list, list) {
if (++i > FANOTIFY_MAX_MERGE_EVENTS) if (++i > FANOTIFY_MAX_MERGE_EVENTS)
break; break;
if (fanotify_should_merge(old, new)) { if (fanotify_should_merge(test_event, event)) {
old->mask |= new->mask; FANOTIFY_E(test_event)->mask |= new->mask;
if (fanotify_is_error_event(old->mask))
FANOTIFY_EE(old)->err_count++;
return 1; return 1;
} }
} }
@ -247,11 +190,8 @@ static int fanotify_get_response(struct fsnotify_group *group,
return ret; return ret;
} }
/* Event not yet reported? Just remove it. */ /* Event not yet reported? Just remove it. */
if (event->state == FAN_EVENT_INIT) { if (event->state == FAN_EVENT_INIT)
fsnotify_remove_queued_event(group, &event->fae.fse); fsnotify_remove_queued_event(group, &event->fae.fse);
/* Permission events are not supposed to be hashed */
WARN_ON_ONCE(!hlist_unhashed(&event->fae.merge_list));
}
/* /*
* Event may be also answered in case signal delivery raced * Event may be also answered in case signal delivery raced
* with wakeup. In that case we have nothing to do besides * with wakeup. In that case we have nothing to do besides
@ -291,17 +231,15 @@ static int fanotify_get_response(struct fsnotify_group *group,
*/ */
static u32 fanotify_group_event_mask(struct fsnotify_group *group, static u32 fanotify_group_event_mask(struct fsnotify_group *group,
struct fsnotify_iter_info *iter_info, struct fsnotify_iter_info *iter_info,
u32 *match_mask, u32 event_mask, u32 event_mask, const void *data,
const void *data, int data_type, int data_type, struct inode *dir)
struct inode *dir)
{ {
__u32 marks_mask = 0, marks_ignore_mask = 0; __u32 marks_mask = 0, marks_ignored_mask = 0;
__u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS | __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS |
FANOTIFY_EVENT_FLAGS; FANOTIFY_EVENT_FLAGS;
const struct path *path = fsnotify_data_path(data, data_type); const struct path *path = fsnotify_data_path(data, data_type);
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
struct fsnotify_mark *mark; struct fsnotify_mark *mark;
bool ondir = event_mask & FAN_ONDIR;
int type; int type;
pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n",
@ -316,30 +254,37 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
return 0; return 0;
} else if (!(fid_mode & FAN_REPORT_FID)) { } else if (!(fid_mode & FAN_REPORT_FID)) {
/* Do we have a directory inode to report? */ /* Do we have a directory inode to report? */
if (!dir && !ondir) if (!dir && !(event_mask & FS_ISDIR))
return 0; return 0;
} }
fsnotify_foreach_iter_mark_type(iter_info, mark, type) { fsnotify_foreach_obj_type(type) {
/* if (!fsnotify_iter_should_report_type(iter_info, type))
* Apply ignore mask depending on event flags in ignore mask. continue;
*/ mark = iter_info->marks[type];
marks_ignore_mask |=
fsnotify_effective_ignore_mask(mark, ondir, type); /* Apply ignore mask regardless of ISDIR and ON_CHILD flags */
marks_ignored_mask |= mark->ignored_mask;
/* /*
* Send the event depending on event flags in mark mask. * If the event is on dir and this mark doesn't care about
* events on dir, don't send it!
*/ */
if (!fsnotify_mask_applicable(mark->mask, ondir, type)) if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR))
continue;
/*
* If the event is on a child and this mark is on a parent not
* watching children, don't send it!
*/
if (type == FSNOTIFY_OBJ_TYPE_PARENT &&
!(mark->mask & FS_EVENT_ON_CHILD))
continue; continue;
marks_mask |= mark->mask; marks_mask |= mark->mask;
/* Record the mark types of this group that matched the event */
*match_mask |= 1U << type;
} }
test_mask = event_mask & marks_mask & ~marks_ignore_mask; test_mask = event_mask & marks_mask & ~marks_ignored_mask;
/* /*
* For dirent modification events (create/delete/move) that do not carry * For dirent modification events (create/delete/move) that do not carry
@ -374,23 +319,13 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
static int fanotify_encode_fh_len(struct inode *inode) static int fanotify_encode_fh_len(struct inode *inode)
{ {
int dwords = 0; int dwords = 0;
int fh_len;
if (!inode) if (!inode)
return 0; return 0;
exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
fh_len = dwords << 2;
/* return dwords << 2;
* struct fanotify_error_event might be preallocated and is
* limited to MAX_HANDLE_SZ. This should never happen, but
* safeguard by forcing an invalid file handle.
*/
if (WARN_ON_ONCE(fh_len > MAX_HANDLE_SZ))
return 0;
return fh_len;
} }
/* /*
@ -400,8 +335,7 @@ static int fanotify_encode_fh_len(struct inode *inode)
* Return 0 on failure to encode. * Return 0 on failure to encode.
*/ */
static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
unsigned int fh_len, unsigned int *hash, unsigned int fh_len, gfp_t gfp)
gfp_t gfp)
{ {
int dwords, type = 0; int dwords, type = 0;
char *ext_buf = NULL; char *ext_buf = NULL;
@ -411,21 +345,15 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
fh->type = FILEID_ROOT; fh->type = FILEID_ROOT;
fh->len = 0; fh->len = 0;
fh->flags = 0; fh->flags = 0;
/*
* Invalid FHs are used by FAN_FS_ERROR for errors not
* linked to any inode. The f_handle won't be reported
* back to userspace.
*/
if (!inode) if (!inode)
goto out; return 0;
/* /*
* !gpf means preallocated variable size fh, but fh_len could * !gpf means preallocated variable size fh, but fh_len could
* be zero in that case if encoding fh len failed. * be zero in that case if encoding fh len failed.
*/ */
err = -ENOENT; err = -ENOENT;
if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4) || fh_len > MAX_HANDLE_SZ) if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4))
goto out_err; goto out_err;
/* No external buffer in a variable size allocated fh */ /* No external buffer in a variable size allocated fh */
@ -450,14 +378,6 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
fh->type = type; fh->type = type;
fh->len = fh_len; fh->len = fh_len;
out:
/*
* Mix fh into event merge key. Hash might be NULL in case of
* unhashed FID events (i.e. FAN_FS_ERROR).
*/
if (hash)
*hash ^= fanotify_hash_fh(fh);
return FANOTIFY_FH_HDR_LEN + fh_len; return FANOTIFY_FH_HDR_LEN + fh_len;
out_err: out_err:
@ -472,41 +392,17 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
} }
/* /*
* FAN_REPORT_FID is ambiguous in that it reports the fid of the child for * The inode to use as identifier when reporting fid depends on the event.
* some events and the fid of the parent for create/delete/move events. * Report the modified directory inode on dirent modification events.
* * Report the "victim" inode otherwise.
* With the FAN_REPORT_TARGET_FID flag, the fid of the child is reported
* also in create/delete/move events in addition to the fid of the parent
* and the name of the child.
*/
static inline bool fanotify_report_child_fid(unsigned int fid_mode, u32 mask)
{
if (mask & ALL_FSNOTIFY_DIRENT_EVENTS)
return (fid_mode & FAN_REPORT_TARGET_FID);
return (fid_mode & FAN_REPORT_FID) && !(mask & FAN_ONDIR);
}
/*
* The inode to use as identifier when reporting fid depends on the event
* and the group flags.
*
* With the group flag FAN_REPORT_TARGET_FID, always report the child fid.
*
* Without the group flag FAN_REPORT_TARGET_FID, report the modified directory
* fid on dirent events and the child fid otherwise.
*
* For example: * For example:
* FS_ATTRIB reports the child fid even if reported on a watched parent. * FS_ATTRIB reports the child inode even if reported on a watched parent.
* FS_CREATE reports the modified dir fid without FAN_REPORT_TARGET_FID. * FS_CREATE reports the modified dir inode and not the created inode.
* and reports the created child fid with FAN_REPORT_TARGET_FID.
*/ */
static struct inode *fanotify_fid_inode(u32 event_mask, const void *data, static struct inode *fanotify_fid_inode(u32 event_mask, const void *data,
int data_type, struct inode *dir, int data_type, struct inode *dir)
unsigned int fid_mode)
{ {
if ((event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) && if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
!(fid_mode & FAN_REPORT_TARGET_FID))
return dir; return dir;
return fsnotify_data_inode(data, data_type); return fsnotify_data_inode(data, data_type);
@ -528,14 +424,13 @@ static struct inode *fanotify_dfid_inode(u32 event_mask, const void *data,
if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
return dir; return dir;
if (inode && S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
return inode; return inode;
return dir; return dir;
} }
static struct fanotify_event *fanotify_alloc_path_event(const struct path *path, static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
unsigned int *hash,
gfp_t gfp) gfp_t gfp)
{ {
struct fanotify_path_event *pevent; struct fanotify_path_event *pevent;
@ -546,7 +441,6 @@ static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH; pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH;
pevent->path = *path; pevent->path = *path;
*hash ^= fanotify_hash_path(path);
path_get(path); path_get(path);
return &pevent->fae; return &pevent->fae;
@ -572,7 +466,6 @@ static struct fanotify_event *fanotify_alloc_perm_event(const struct path *path,
static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id, static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
__kernel_fsid_t *fsid, __kernel_fsid_t *fsid,
unsigned int *hash,
gfp_t gfp) gfp_t gfp)
{ {
struct fanotify_fid_event *ffe; struct fanotify_fid_event *ffe;
@ -583,153 +476,78 @@ static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
ffe->fae.type = FANOTIFY_EVENT_TYPE_FID; ffe->fae.type = FANOTIFY_EVENT_TYPE_FID;
ffe->fsid = *fsid; ffe->fsid = *fsid;
*hash ^= fanotify_hash_fsid(fsid);
fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id), fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id),
hash, gfp); gfp);
return &ffe->fae; return &ffe->fae;
} }
static struct fanotify_event *fanotify_alloc_name_event(struct inode *dir, static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
__kernel_fsid_t *fsid, __kernel_fsid_t *fsid,
const struct qstr *name, const struct qstr *file_name,
struct inode *child, struct inode *child,
struct dentry *moved,
unsigned int *hash,
gfp_t gfp) gfp_t gfp)
{ {
struct fanotify_name_event *fne; struct fanotify_name_event *fne;
struct fanotify_info *info; struct fanotify_info *info;
struct fanotify_fh *dfh, *ffh; struct fanotify_fh *dfh, *ffh;
struct inode *dir2 = moved ? d_inode(moved->d_parent) : NULL; unsigned int dir_fh_len = fanotify_encode_fh_len(id);
const struct qstr *name2 = moved ? &moved->d_name : NULL;
unsigned int dir_fh_len = fanotify_encode_fh_len(dir);
unsigned int dir2_fh_len = fanotify_encode_fh_len(dir2);
unsigned int child_fh_len = fanotify_encode_fh_len(child); unsigned int child_fh_len = fanotify_encode_fh_len(child);
unsigned long name_len = name ? name->len : 0; unsigned int size;
unsigned long name2_len = name2 ? name2->len : 0;
unsigned int len, size;
/* Reserve terminating null byte even for empty name */ size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len;
size = sizeof(*fne) + name_len + name2_len + 2;
if (dir_fh_len)
size += FANOTIFY_FH_HDR_LEN + dir_fh_len;
if (dir2_fh_len)
size += FANOTIFY_FH_HDR_LEN + dir2_fh_len;
if (child_fh_len) if (child_fh_len)
size += FANOTIFY_FH_HDR_LEN + child_fh_len; size += FANOTIFY_FH_HDR_LEN + child_fh_len;
if (file_name)
size += file_name->len + 1;
fne = kmalloc(size, gfp); fne = kmalloc(size, gfp);
if (!fne) if (!fne)
return NULL; return NULL;
fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME; fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME;
fne->fsid = *fsid; fne->fsid = *fsid;
*hash ^= fanotify_hash_fsid(fsid);
info = &fne->info; info = &fne->info;
fanotify_info_init(info); fanotify_info_init(info);
if (dir_fh_len) { dfh = fanotify_info_dir_fh(info);
dfh = fanotify_info_dir_fh(info); info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, 0);
len = fanotify_encode_fh(dfh, dir, dir_fh_len, hash, 0);
fanotify_info_set_dir_fh(info, len);
}
if (dir2_fh_len) {
dfh = fanotify_info_dir2_fh(info);
len = fanotify_encode_fh(dfh, dir2, dir2_fh_len, hash, 0);
fanotify_info_set_dir2_fh(info, len);
}
if (child_fh_len) { if (child_fh_len) {
ffh = fanotify_info_file_fh(info); ffh = fanotify_info_file_fh(info);
len = fanotify_encode_fh(ffh, child, child_fh_len, hash, 0); info->file_fh_totlen = fanotify_encode_fh(ffh, child, child_fh_len, 0);
fanotify_info_set_file_fh(info, len);
}
if (name_len) {
fanotify_info_copy_name(info, name);
*hash ^= full_name_hash((void *)name_len, name->name, name_len);
}
if (name2_len) {
fanotify_info_copy_name2(info, name2);
*hash ^= full_name_hash((void *)name2_len, name2->name,
name2_len);
} }
if (file_name)
fanotify_info_copy_name(info, file_name);
pr_debug("%s: size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n", pr_debug("%s: ino=%lu size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n",
__func__, size, dir_fh_len, child_fh_len, __func__, id->i_ino, size, dir_fh_len, child_fh_len,
info->name_len, info->name_len, fanotify_info_name(info)); info->name_len, info->name_len, fanotify_info_name(info));
if (dir2_fh_len) {
pr_debug("%s: dir2_fh_len=%u name2_len=%u name2='%.*s'\n",
__func__, dir2_fh_len, info->name2_len,
info->name2_len, fanotify_info_name2(info));
}
return &fne->fae; return &fne->fae;
} }
static struct fanotify_event *fanotify_alloc_error_event( static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
struct fsnotify_group *group, u32 mask, const void *data,
__kernel_fsid_t *fsid, int data_type, struct inode *dir,
const void *data, int data_type, const struct qstr *file_name,
unsigned int *hash) __kernel_fsid_t *fsid)
{
struct fs_error_report *report =
fsnotify_data_error_report(data, data_type);
struct inode *inode;
struct fanotify_error_event *fee;
int fh_len;
if (WARN_ON_ONCE(!report))
return NULL;
fee = mempool_alloc(&group->fanotify_data.error_events_pool, GFP_NOFS);
if (!fee)
return NULL;
fee->fae.type = FANOTIFY_EVENT_TYPE_FS_ERROR;
fee->error = report->error;
fee->err_count = 1;
fee->fsid = *fsid;
inode = report->inode;
fh_len = fanotify_encode_fh_len(inode);
/* Bad fh_len. Fallback to using an invalid fh. Should never happen. */
if (!fh_len && inode)
inode = NULL;
fanotify_encode_fh(&fee->object_fh, inode, fh_len, NULL, 0);
*hash ^= fanotify_hash_fsid(fsid);
return &fee->fae;
}
static struct fanotify_event *fanotify_alloc_event(
struct fsnotify_group *group,
u32 mask, const void *data, int data_type,
struct inode *dir, const struct qstr *file_name,
__kernel_fsid_t *fsid, u32 match_mask)
{ {
struct fanotify_event *event = NULL; struct fanotify_event *event = NULL;
gfp_t gfp = GFP_KERNEL_ACCOUNT; gfp_t gfp = GFP_KERNEL_ACCOUNT;
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); struct inode *id = fanotify_fid_inode(mask, data, data_type, dir);
struct inode *id = fanotify_fid_inode(mask, data, data_type, dir,
fid_mode);
struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir); struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
const struct path *path = fsnotify_data_path(data, data_type); const struct path *path = fsnotify_data_path(data, data_type);
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
struct mem_cgroup *old_memcg; struct mem_cgroup *old_memcg;
struct dentry *moved = NULL;
struct inode *child = NULL; struct inode *child = NULL;
bool name_event = false; bool name_event = false;
unsigned int hash = 0;
bool ondir = mask & FAN_ONDIR;
struct pid *pid;
if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) { if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) {
/* /*
* For certain events and group flags, report the child fid * With both flags FAN_REPORT_DIR_FID and FAN_REPORT_FID, we
* report the child fid for events reported on a non-dir child
* in addition to reporting the parent fid and maybe child name. * in addition to reporting the parent fid and maybe child name.
*/ */
if (fanotify_report_child_fid(fid_mode, mask) && id != dirid) if ((fid_mode & FAN_REPORT_FID) &&
id != dirid && !(mask & FAN_ONDIR))
child = id; child = id;
id = dirid; id = dirid;
@ -750,41 +568,10 @@ static struct fanotify_event *fanotify_alloc_event(
if (!(fid_mode & FAN_REPORT_NAME)) { if (!(fid_mode & FAN_REPORT_NAME)) {
name_event = !!child; name_event = !!child;
file_name = NULL; file_name = NULL;
} else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || !ondir) { } else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) ||
!(mask & FAN_ONDIR)) {
name_event = true; name_event = true;
} }
/*
* In the special case of FAN_RENAME event, use the match_mask
* to determine if we need to report only the old parent+name,
* only the new parent+name or both.
* 'dirid' and 'file_name' are the old parent+name and
* 'moved' has the new parent+name.
*/
if (mask & FAN_RENAME) {
bool report_old, report_new;
if (WARN_ON_ONCE(!match_mask))
return NULL;
/* Report both old and new parent+name if sb watching */
report_old = report_new =
match_mask & (1U << FSNOTIFY_ITER_TYPE_SB);
report_old |=
match_mask & (1U << FSNOTIFY_ITER_TYPE_INODE);
report_new |=
match_mask & (1U << FSNOTIFY_ITER_TYPE_INODE2);
if (!report_old) {
/* Do not report old parent+name */
dirid = NULL;
file_name = NULL;
}
if (report_new) {
/* Report new parent+name */
moved = fsnotify_data_dentry(data, data_type);
}
}
} }
/* /*
@ -803,30 +590,28 @@ static struct fanotify_event *fanotify_alloc_event(
if (fanotify_is_perm_event(mask)) { if (fanotify_is_perm_event(mask)) {
event = fanotify_alloc_perm_event(path, gfp); event = fanotify_alloc_perm_event(path, gfp);
} else if (fanotify_is_error_event(mask)) { } else if (name_event && (file_name || child)) {
event = fanotify_alloc_error_event(group, fsid, data, event = fanotify_alloc_name_event(id, fsid, file_name, child,
data_type, &hash); gfp);
} else if (name_event && (file_name || moved || child)) {
event = fanotify_alloc_name_event(dirid, fsid, file_name, child,
moved, &hash, gfp);
} else if (fid_mode) { } else if (fid_mode) {
event = fanotify_alloc_fid_event(id, fsid, &hash, gfp); event = fanotify_alloc_fid_event(id, fsid, gfp);
} else { } else {
event = fanotify_alloc_path_event(path, &hash, gfp); event = fanotify_alloc_path_event(path, gfp);
} }
if (!event) if (!event)
goto out; goto out;
/*
* Use the victim inode instead of the watching inode as the id for
* event queue, so event reported on parent is merged with event
* reported on child when both directory and child watches exist.
*/
fanotify_init_event(event, (unsigned long)id, mask);
if (FAN_GROUP_FLAG(group, FAN_REPORT_TID)) if (FAN_GROUP_FLAG(group, FAN_REPORT_TID))
pid = get_pid(task_pid(current)); event->pid = get_pid(task_pid(current));
else else
pid = get_pid(task_tgid(current)); event->pid = get_pid(task_tgid(current));
/* Mix event info, FAN_ONDIR flag and pid into event merge key */
hash ^= hash_long((unsigned long)pid | ondir, FANOTIFY_EVENT_HASH_BITS);
fanotify_init_event(event, hash, mask);
event->pid = pid;
out: out:
set_active_memcg(old_memcg); set_active_memcg(old_memcg);
@ -840,14 +625,16 @@ static struct fanotify_event *fanotify_alloc_event(
*/ */
static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info) static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
{ {
struct fsnotify_mark *mark;
int type; int type;
__kernel_fsid_t fsid = {}; __kernel_fsid_t fsid = {};
fsnotify_foreach_iter_mark_type(iter_info, mark, type) { fsnotify_foreach_obj_type(type) {
struct fsnotify_mark_connector *conn; struct fsnotify_mark_connector *conn;
conn = READ_ONCE(mark->connector); if (!fsnotify_iter_should_report_type(iter_info, type))
continue;
conn = READ_ONCE(iter_info->marks[type]->connector);
/* Mark is just getting destroyed or created? */ /* Mark is just getting destroyed or created? */
if (!conn) if (!conn)
continue; continue;
@ -864,27 +651,6 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
return fsid; return fsid;
} }
/*
* Add an event to hash table for faster merge.
*/
static void fanotify_insert_event(struct fsnotify_group *group,
struct fsnotify_event *fsn_event)
{
struct fanotify_event *event = FANOTIFY_E(fsn_event);
unsigned int bucket = fanotify_event_hash_bucket(group, event);
struct hlist_head *hlist = &group->fanotify_data.merge_hash[bucket];
assert_spin_locked(&group->notification_lock);
if (!fanotify_is_hashed_event(event->mask))
return;
pr_debug("%s: group=%p event=%p bucket=%u\n", __func__,
group, event, bucket);
hlist_add_head(&event->merge_list, hlist);
}
static int fanotify_handle_event(struct fsnotify_group *group, u32 mask, static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
const void *data, int data_type, const void *data, int data_type,
struct inode *dir, struct inode *dir,
@ -895,7 +661,6 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
struct fanotify_event *event; struct fanotify_event *event;
struct fsnotify_event *fsn_event; struct fsnotify_event *fsn_event;
__kernel_fsid_t fsid = {}; __kernel_fsid_t fsid = {};
u32 match_mask = 0;
BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
@ -916,18 +681,15 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR); BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);
BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC); BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM); BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
BUILD_BUG_ON(FAN_FS_ERROR != FS_ERROR);
BUILD_BUG_ON(FAN_RENAME != FS_RENAME);
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 21); BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
mask = fanotify_group_event_mask(group, iter_info, &match_mask, mask = fanotify_group_event_mask(group, iter_info, mask, data,
mask, data, data_type, dir); data_type, dir);
if (!mask) if (!mask)
return 0; return 0;
pr_debug("%s: group=%p mask=%x report_mask=%x\n", __func__, pr_debug("%s: group=%p mask=%x\n", __func__, group, mask);
group, mask, match_mask);
if (fanotify_is_perm_event(mask)) { if (fanotify_is_perm_event(mask)) {
/* /*
@ -946,7 +708,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
} }
event = fanotify_alloc_event(group, mask, data, data_type, dir, event = fanotify_alloc_event(group, mask, data, data_type, dir,
file_name, &fsid, match_mask); file_name, &fsid);
ret = -ENOMEM; ret = -ENOMEM;
if (unlikely(!event)) { if (unlikely(!event)) {
/* /*
@ -959,8 +721,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
} }
fsn_event = &event->fse; fsn_event = &event->fse;
ret = fsnotify_insert_event(group, fsn_event, fanotify_merge, ret = fsnotify_add_event(group, fsn_event, fanotify_merge);
fanotify_insert_event);
if (ret) { if (ret) {
/* Permission events shouldn't be merged */ /* Permission events shouldn't be merged */
BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS); BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS);
@ -981,13 +742,11 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
static void fanotify_free_group_priv(struct fsnotify_group *group) static void fanotify_free_group_priv(struct fsnotify_group *group)
{ {
kfree(group->fanotify_data.merge_hash); struct user_struct *user;
if (group->fanotify_data.ucounts)
dec_ucount(group->fanotify_data.ucounts,
UCOUNT_FANOTIFY_GROUPS);
if (mempool_initialized(&group->fanotify_data.error_events_pool)) user = group->fanotify_data.user;
mempool_exit(&group->fanotify_data.error_events_pool); atomic_dec(&user->fanotify_listeners);
free_uid(user);
} }
static void fanotify_free_path_event(struct fanotify_event *event) static void fanotify_free_path_event(struct fanotify_event *event)
@ -1016,16 +775,7 @@ static void fanotify_free_name_event(struct fanotify_event *event)
kfree(FANOTIFY_NE(event)); kfree(FANOTIFY_NE(event));
} }
static void fanotify_free_error_event(struct fsnotify_group *group, static void fanotify_free_event(struct fsnotify_event *fsn_event)
struct fanotify_event *event)
{
struct fanotify_error_event *fee = FANOTIFY_EE(event);
mempool_free(fee, &group->fanotify_data.error_events_pool);
}
static void fanotify_free_event(struct fsnotify_group *group,
struct fsnotify_event *fsn_event)
{ {
struct fanotify_event *event; struct fanotify_event *event;
@ -1047,21 +797,11 @@ static void fanotify_free_event(struct fsnotify_group *group,
case FANOTIFY_EVENT_TYPE_OVERFLOW: case FANOTIFY_EVENT_TYPE_OVERFLOW:
kfree(event); kfree(event);
break; break;
case FANOTIFY_EVENT_TYPE_FS_ERROR:
fanotify_free_error_event(group, event);
break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
} }
static void fanotify_freeing_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group)
{
if (!FAN_GROUP_FLAG(group, FAN_UNLIMITED_MARKS))
dec_ucount(group->fanotify_data.ucounts, UCOUNT_FANOTIFY_MARKS);
}
static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
{ {
kmem_cache_free(fanotify_mark_cache, fsn_mark); kmem_cache_free(fanotify_mark_cache, fsn_mark);
@ -1071,6 +811,5 @@ const struct fsnotify_ops fanotify_fsnotify_ops = {
.handle_event = fanotify_handle_event, .handle_event = fanotify_handle_event,
.free_group_priv = fanotify_free_group_priv, .free_group_priv = fanotify_free_group_priv,
.free_event = fanotify_free_event, .free_event = fanotify_free_event,
.freeing_mark = fanotify_freeing_mark,
.free_mark = fanotify_free_mark, .free_mark = fanotify_free_mark,
}; };

View File

@ -3,7 +3,6 @@
#include <linux/path.h> #include <linux/path.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/hashtable.h>
extern struct kmem_cache *fanotify_mark_cache; extern struct kmem_cache *fanotify_mark_cache;
extern struct kmem_cache *fanotify_fid_event_cachep; extern struct kmem_cache *fanotify_fid_event_cachep;
@ -40,45 +39,15 @@ struct fanotify_fh {
struct fanotify_info { struct fanotify_info {
/* size of dir_fh/file_fh including fanotify_fh hdr size */ /* size of dir_fh/file_fh including fanotify_fh hdr size */
u8 dir_fh_totlen; u8 dir_fh_totlen;
u8 dir2_fh_totlen;
u8 file_fh_totlen; u8 file_fh_totlen;
u8 name_len; u8 name_len;
u8 name2_len; u8 pad;
u8 pad[3];
unsigned char buf[]; unsigned char buf[];
/* /*
* (struct fanotify_fh) dir_fh starts at buf[0] * (struct fanotify_fh) dir_fh starts at buf[0]
* (optional) dir2_fh starts at buf[dir_fh_totlen] * (optional) file_fh starts at buf[dir_fh_totlen]
* (optional) file_fh starts at buf[dir_fh_totlen + dir2_fh_totlen] * name starts at buf[dir_fh_totlen + file_fh_totlen]
* name starts at buf[dir_fh_totlen + dir2_fh_totlen + file_fh_totlen]
* ...
*/ */
#define FANOTIFY_DIR_FH_SIZE(info) ((info)->dir_fh_totlen)
#define FANOTIFY_DIR2_FH_SIZE(info) ((info)->dir2_fh_totlen)
#define FANOTIFY_FILE_FH_SIZE(info) ((info)->file_fh_totlen)
#define FANOTIFY_NAME_SIZE(info) ((info)->name_len + 1)
#define FANOTIFY_NAME2_SIZE(info) ((info)->name2_len + 1)
#define FANOTIFY_DIR_FH_OFFSET(info) 0
#define FANOTIFY_DIR2_FH_OFFSET(info) \
(FANOTIFY_DIR_FH_OFFSET(info) + FANOTIFY_DIR_FH_SIZE(info))
#define FANOTIFY_FILE_FH_OFFSET(info) \
(FANOTIFY_DIR2_FH_OFFSET(info) + FANOTIFY_DIR2_FH_SIZE(info))
#define FANOTIFY_NAME_OFFSET(info) \
(FANOTIFY_FILE_FH_OFFSET(info) + FANOTIFY_FILE_FH_SIZE(info))
#define FANOTIFY_NAME2_OFFSET(info) \
(FANOTIFY_NAME_OFFSET(info) + FANOTIFY_NAME_SIZE(info))
#define FANOTIFY_DIR_FH_BUF(info) \
((info)->buf + FANOTIFY_DIR_FH_OFFSET(info))
#define FANOTIFY_DIR2_FH_BUF(info) \
((info)->buf + FANOTIFY_DIR2_FH_OFFSET(info))
#define FANOTIFY_FILE_FH_BUF(info) \
((info)->buf + FANOTIFY_FILE_FH_OFFSET(info))
#define FANOTIFY_NAME_BUF(info) \
((info)->buf + FANOTIFY_NAME_OFFSET(info))
#define FANOTIFY_NAME2_BUF(info) \
((info)->buf + FANOTIFY_NAME2_OFFSET(info))
} __aligned(4); } __aligned(4);
static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh) static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh)
@ -117,21 +86,7 @@ static inline struct fanotify_fh *fanotify_info_dir_fh(struct fanotify_info *inf
{ {
BUILD_BUG_ON(offsetof(struct fanotify_info, buf) % 4); BUILD_BUG_ON(offsetof(struct fanotify_info, buf) % 4);
return (struct fanotify_fh *)FANOTIFY_DIR_FH_BUF(info); return (struct fanotify_fh *)info->buf;
}
static inline int fanotify_info_dir2_fh_len(struct fanotify_info *info)
{
if (!info->dir2_fh_totlen ||
WARN_ON_ONCE(info->dir2_fh_totlen < FANOTIFY_FH_HDR_LEN))
return 0;
return info->dir2_fh_totlen - FANOTIFY_FH_HDR_LEN;
}
static inline struct fanotify_fh *fanotify_info_dir2_fh(struct fanotify_info *info)
{
return (struct fanotify_fh *)FANOTIFY_DIR2_FH_BUF(info);
} }
static inline int fanotify_info_file_fh_len(struct fanotify_info *info) static inline int fanotify_info_file_fh_len(struct fanotify_info *info)
@ -145,90 +100,27 @@ static inline int fanotify_info_file_fh_len(struct fanotify_info *info)
static inline struct fanotify_fh *fanotify_info_file_fh(struct fanotify_info *info) static inline struct fanotify_fh *fanotify_info_file_fh(struct fanotify_info *info)
{ {
return (struct fanotify_fh *)FANOTIFY_FILE_FH_BUF(info); return (struct fanotify_fh *)(info->buf + info->dir_fh_totlen);
} }
static inline char *fanotify_info_name(struct fanotify_info *info) static inline const char *fanotify_info_name(struct fanotify_info *info)
{ {
if (!info->name_len) return info->buf + info->dir_fh_totlen + info->file_fh_totlen;
return NULL;
return FANOTIFY_NAME_BUF(info);
}
static inline char *fanotify_info_name2(struct fanotify_info *info)
{
if (!info->name2_len)
return NULL;
return FANOTIFY_NAME2_BUF(info);
} }
static inline void fanotify_info_init(struct fanotify_info *info) static inline void fanotify_info_init(struct fanotify_info *info)
{ {
BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN + MAX_HANDLE_SZ > U8_MAX);
BUILD_BUG_ON(NAME_MAX > U8_MAX);
info->dir_fh_totlen = 0; info->dir_fh_totlen = 0;
info->dir2_fh_totlen = 0;
info->file_fh_totlen = 0; info->file_fh_totlen = 0;
info->name_len = 0; info->name_len = 0;
info->name2_len = 0;
}
/* These set/copy helpers MUST be called by order */
static inline void fanotify_info_set_dir_fh(struct fanotify_info *info,
unsigned int totlen)
{
if (WARN_ON_ONCE(info->dir2_fh_totlen > 0) ||
WARN_ON_ONCE(info->file_fh_totlen > 0) ||
WARN_ON_ONCE(info->name_len > 0) ||
WARN_ON_ONCE(info->name2_len > 0))
return;
info->dir_fh_totlen = totlen;
}
static inline void fanotify_info_set_dir2_fh(struct fanotify_info *info,
unsigned int totlen)
{
if (WARN_ON_ONCE(info->file_fh_totlen > 0) ||
WARN_ON_ONCE(info->name_len > 0) ||
WARN_ON_ONCE(info->name2_len > 0))
return;
info->dir2_fh_totlen = totlen;
}
static inline void fanotify_info_set_file_fh(struct fanotify_info *info,
unsigned int totlen)
{
if (WARN_ON_ONCE(info->name_len > 0) ||
WARN_ON_ONCE(info->name2_len > 0))
return;
info->file_fh_totlen = totlen;
} }
static inline void fanotify_info_copy_name(struct fanotify_info *info, static inline void fanotify_info_copy_name(struct fanotify_info *info,
const struct qstr *name) const struct qstr *name)
{ {
if (WARN_ON_ONCE(name->len > NAME_MAX) ||
WARN_ON_ONCE(info->name2_len > 0))
return;
info->name_len = name->len; info->name_len = name->len;
strcpy(fanotify_info_name(info), name->name); strcpy(info->buf + info->dir_fh_totlen + info->file_fh_totlen,
} name->name);
static inline void fanotify_info_copy_name2(struct fanotify_info *info,
const struct qstr *name)
{
if (WARN_ON_ONCE(name->len > NAME_MAX))
return;
info->name2_len = name->len;
strcpy(fanotify_info_name2(info), name->name);
} }
/* /*
@ -243,48 +135,29 @@ enum fanotify_event_type {
FANOTIFY_EVENT_TYPE_PATH, FANOTIFY_EVENT_TYPE_PATH,
FANOTIFY_EVENT_TYPE_PATH_PERM, FANOTIFY_EVENT_TYPE_PATH_PERM,
FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */ FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */
__FANOTIFY_EVENT_TYPE_NUM
}; };
#define FANOTIFY_EVENT_TYPE_BITS \
(ilog2(__FANOTIFY_EVENT_TYPE_NUM - 1) + 1)
#define FANOTIFY_EVENT_HASH_BITS \
(32 - FANOTIFY_EVENT_TYPE_BITS)
struct fanotify_event { struct fanotify_event {
struct fsnotify_event fse; struct fsnotify_event fse;
struct hlist_node merge_list; /* List for hashed merge */
u32 mask; u32 mask;
struct { enum fanotify_event_type type;
unsigned int type : FANOTIFY_EVENT_TYPE_BITS;
unsigned int hash : FANOTIFY_EVENT_HASH_BITS;
};
struct pid *pid; struct pid *pid;
}; };
static inline void fanotify_init_event(struct fanotify_event *event, static inline void fanotify_init_event(struct fanotify_event *event,
unsigned int hash, u32 mask) unsigned long id, u32 mask)
{ {
fsnotify_init_event(&event->fse); fsnotify_init_event(&event->fse, id);
INIT_HLIST_NODE(&event->merge_list);
event->hash = hash;
event->mask = mask; event->mask = mask;
event->pid = NULL; event->pid = NULL;
} }
#define FANOTIFY_INLINE_FH(name, size) \
struct { \
struct fanotify_fh (name); \
/* Space for object_fh.buf[] - access with fanotify_fh_buf() */ \
unsigned char _inline_fh_buf[(size)]; \
}
struct fanotify_fid_event { struct fanotify_fid_event {
struct fanotify_event fae; struct fanotify_event fae;
__kernel_fsid_t fsid; __kernel_fsid_t fsid;
struct fanotify_fh object_fh;
FANOTIFY_INLINE_FH(object_fh, FANOTIFY_INLINE_FH_LEN); /* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */
unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN];
}; };
static inline struct fanotify_fid_event * static inline struct fanotify_fid_event *
@ -305,30 +178,12 @@ FANOTIFY_NE(struct fanotify_event *event)
return container_of(event, struct fanotify_name_event, fae); return container_of(event, struct fanotify_name_event, fae);
} }
struct fanotify_error_event {
struct fanotify_event fae;
s32 error; /* Error reported by the Filesystem. */
u32 err_count; /* Suppressed errors count */
__kernel_fsid_t fsid; /* FSID this error refers to. */
FANOTIFY_INLINE_FH(object_fh, MAX_HANDLE_SZ);
};
static inline struct fanotify_error_event *
FANOTIFY_EE(struct fanotify_event *event)
{
return container_of(event, struct fanotify_error_event, fae);
}
static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event) static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
{ {
if (event->type == FANOTIFY_EVENT_TYPE_FID) if (event->type == FANOTIFY_EVENT_TYPE_FID)
return &FANOTIFY_FE(event)->fsid; return &FANOTIFY_FE(event)->fsid;
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
return &FANOTIFY_NE(event)->fsid; return &FANOTIFY_NE(event)->fsid;
else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
return &FANOTIFY_EE(event)->fsid;
else else
return NULL; return NULL;
} }
@ -340,8 +195,6 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
return &FANOTIFY_FE(event)->object_fh; return &FANOTIFY_FE(event)->object_fh;
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
return fanotify_info_file_fh(&FANOTIFY_NE(event)->info); return fanotify_info_file_fh(&FANOTIFY_NE(event)->info);
else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
return &FANOTIFY_EE(event)->object_fh;
else else
return NULL; return NULL;
} }
@ -373,37 +226,6 @@ static inline int fanotify_event_dir_fh_len(struct fanotify_event *event)
return info ? fanotify_info_dir_fh_len(info) : 0; return info ? fanotify_info_dir_fh_len(info) : 0;
} }
static inline int fanotify_event_dir2_fh_len(struct fanotify_event *event)
{
struct fanotify_info *info = fanotify_event_info(event);
return info ? fanotify_info_dir2_fh_len(info) : 0;
}
static inline bool fanotify_event_has_object_fh(struct fanotify_event *event)
{
/* For error events, even zeroed fh are reported. */
if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
return true;
return fanotify_event_object_fh_len(event) > 0;
}
static inline bool fanotify_event_has_dir_fh(struct fanotify_event *event)
{
return fanotify_event_dir_fh_len(event) > 0;
}
static inline bool fanotify_event_has_dir2_fh(struct fanotify_event *event)
{
return fanotify_event_dir2_fh_len(event) > 0;
}
static inline bool fanotify_event_has_any_dir_fh(struct fanotify_event *event)
{
return fanotify_event_has_dir_fh(event) ||
fanotify_event_has_dir2_fh(event);
}
struct fanotify_path_event { struct fanotify_path_event {
struct fanotify_event fae; struct fanotify_event fae;
struct path path; struct path path;
@ -447,12 +269,13 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
return container_of(fse, struct fanotify_event, fse); return container_of(fse, struct fanotify_event, fse);
} }
static inline bool fanotify_is_error_event(u32 mask) static inline bool fanotify_event_has_path(struct fanotify_event *event)
{ {
return mask & FAN_FS_ERROR; return event->type == FANOTIFY_EVENT_TYPE_PATH ||
event->type == FANOTIFY_EVENT_TYPE_PATH_PERM;
} }
static inline const struct path *fanotify_event_path(struct fanotify_event *event) static inline struct path *fanotify_event_path(struct fanotify_event *event)
{ {
if (event->type == FANOTIFY_EVENT_TYPE_PATH) if (event->type == FANOTIFY_EVENT_TYPE_PATH)
return &FANOTIFY_PE(event)->path; return &FANOTIFY_PE(event)->path;
@ -461,40 +284,3 @@ static inline const struct path *fanotify_event_path(struct fanotify_event *even
else else
return NULL; return NULL;
} }
/*
* Use 128 size hash table to speed up events merge.
*/
#define FANOTIFY_HTABLE_BITS (7)
#define FANOTIFY_HTABLE_SIZE (1 << FANOTIFY_HTABLE_BITS)
#define FANOTIFY_HTABLE_MASK (FANOTIFY_HTABLE_SIZE - 1)
/*
* Permission events and overflow event do not get merged - don't hash them.
*/
static inline bool fanotify_is_hashed_event(u32 mask)
{
return !(fanotify_is_perm_event(mask) ||
fsnotify_is_overflow_event(mask));
}
static inline unsigned int fanotify_event_hash_bucket(
struct fsnotify_group *group,
struct fanotify_event *event)
{
return event->hash & FANOTIFY_HTABLE_MASK;
}
static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
{
unsigned int mflags = 0;
if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
if (mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)
mflags |= FAN_MARK_EVICTABLE;
if (mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
mflags |= FAN_MARK_IGNORE;
return mflags;
}

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include "inotify/inotify.h" #include "inotify/inotify.h"
#include "fanotify/fanotify.h"
#include "fdinfo.h" #include "fdinfo.h"
#include "fsnotify.h" #include "fsnotify.h"
@ -29,13 +28,13 @@ static void show_fdinfo(struct seq_file *m, struct file *f,
struct fsnotify_group *group = f->private_data; struct fsnotify_group *group = f->private_data;
struct fsnotify_mark *mark; struct fsnotify_mark *mark;
fsnotify_group_lock(group); mutex_lock(&group->mark_mutex);
list_for_each_entry(mark, &group->marks_list, g_list) { list_for_each_entry(mark, &group->marks_list, g_list) {
show(m, mark); show(m, mark);
if (seq_has_overflowed(m)) if (seq_has_overflowed(m))
break; break;
} }
fsnotify_group_unlock(group); mutex_unlock(&group->mark_mutex);
} }
#if defined(CONFIG_EXPORTFS) #if defined(CONFIG_EXPORTFS)
@ -104,16 +103,19 @@ void inotify_show_fdinfo(struct seq_file *m, struct file *f)
static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
{ {
unsigned int mflags = fanotify_mark_user_flags(mark); unsigned int mflags = 0;
struct inode *inode; struct inode *inode;
if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) { if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) {
inode = igrab(fsnotify_conn_inode(mark->connector)); inode = igrab(fsnotify_conn_inode(mark->connector));
if (!inode) if (!inode)
return; return;
seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ", seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ",
inode->i_ino, inode->i_sb->s_dev, inode->i_ino, inode->i_sb->s_dev,
mflags, mark->mask, mark->ignore_mask); mflags, mark->mask, mark->ignored_mask);
show_mark_fhandle(m, inode); show_mark_fhandle(m, inode);
seq_putc(m, '\n'); seq_putc(m, '\n');
iput(inode); iput(inode);
@ -121,12 +123,12 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
struct mount *mnt = fsnotify_conn_mount(mark->connector); struct mount *mnt = fsnotify_conn_mount(mark->connector);
seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n", seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
mnt->mnt_id, mflags, mark->mask, mark->ignore_mask); mnt->mnt_id, mflags, mark->mask, mark->ignored_mask);
} else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) { } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) {
struct super_block *sb = fsnotify_conn_sb(mark->connector); struct super_block *sb = fsnotify_conn_sb(mark->connector);
seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n", seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n",
sb->s_dev, mflags, mark->mask, mark->ignore_mask); sb->s_dev, mflags, mark->mask, mark->ignored_mask);
} }
} }
@ -135,8 +137,7 @@ void fanotify_show_fdinfo(struct seq_file *m, struct file *f)
struct fsnotify_group *group = f->private_data; struct fsnotify_group *group = f->private_data;
seq_printf(m, "fanotify flags:%x event-flags:%x\n", seq_printf(m, "fanotify flags:%x event-flags:%x\n",
group->fanotify_data.flags & FANOTIFY_INIT_FLAGS, group->fanotify_data.flags, group->fanotify_data.f_flags);
group->fanotify_data.f_flags);
show_fdinfo(m, f, fanotify_fdinfo); show_fdinfo(m, f, fanotify_fdinfo);
} }

View File

@ -70,7 +70,8 @@ static void fsnotify_unmount_inodes(struct super_block *sb)
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
spin_unlock(&sb->s_inode_list_lock); spin_unlock(&sb->s_inode_list_lock);
iput(iput_inode); if (iput_inode)
iput(iput_inode);
/* for each watch, send FS_UNMOUNT and then remove it */ /* for each watch, send FS_UNMOUNT and then remove it */
fsnotify_inode(inode, FS_UNMOUNT); fsnotify_inode(inode, FS_UNMOUNT);
@ -84,23 +85,24 @@ static void fsnotify_unmount_inodes(struct super_block *sb)
} }
spin_unlock(&sb->s_inode_list_lock); spin_unlock(&sb->s_inode_list_lock);
iput(iput_inode); if (iput_inode)
iput(iput_inode);
/* Wait for outstanding inode references from connectors */
wait_var_event(&sb->s_fsnotify_inode_refs,
!atomic_long_read(&sb->s_fsnotify_inode_refs));
} }
void fsnotify_sb_delete(struct super_block *sb) void fsnotify_sb_delete(struct super_block *sb)
{ {
fsnotify_unmount_inodes(sb); fsnotify_unmount_inodes(sb);
fsnotify_clear_marks_by_sb(sb); fsnotify_clear_marks_by_sb(sb);
/* Wait for outstanding object references from connectors */
wait_var_event(&sb->s_fsnotify_connectors,
!atomic_long_read(&sb->s_fsnotify_connectors));
} }
/* /*
* Given an inode, first check if we care what happens to our children. Inotify * Given an inode, first check if we care what happens to our children. Inotify
* and dnotify both tell their parents about events. If we care about any event * and dnotify both tell their parents about events. If we care about any event
* on a child we run all of our children and set a dentry flag saying that the * on a child we run all of our children and set a dentry flag saying that the
* parent cares. Thus when an event happens on a child it can quickly tell * parent cares. Thus when an event happens on a child it can quickly tell if
* if there is a need to find a parent and send the event to the parent. * if there is a need to find a parent and send the event to the parent.
*/ */
void __fsnotify_update_child_dentry_flags(struct inode *inode) void __fsnotify_update_child_dentry_flags(struct inode *inode)
@ -250,10 +252,7 @@ static int fsnotify_handle_inode_event(struct fsnotify_group *group,
if (WARN_ON_ONCE(!ops->handle_inode_event)) if (WARN_ON_ONCE(!ops->handle_inode_event))
return 0; return 0;
if (WARN_ON_ONCE(!inode && !dir)) if ((inode_mark->mask & FS_EXCL_UNLINK) &&
return 0;
if ((inode_mark->flags & FSNOTIFY_MARK_FLAG_EXCL_UNLINK) &&
path && d_unlinked(path->dentry)) path && d_unlinked(path->dentry))
return 0; return 0;
@ -277,28 +276,23 @@ static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
WARN_ON_ONCE(fsnotify_iter_vfsmount_mark(iter_info))) WARN_ON_ONCE(fsnotify_iter_vfsmount_mark(iter_info)))
return 0; return 0;
/* if (parent_mark) {
* For FS_RENAME, 'dir' is old dir and 'data' is new dentry. /*
* The only ->handle_inode_event() backend that supports FS_RENAME is * parent_mark indicates that the parent inode is watching
* dnotify, where it means file was renamed within same parent. * children and interested in this event, which is an event
*/ * possible on child. But is *this mark* watching children and
if (mask & FS_RENAME) { * interested in this event?
struct dentry *moved = fsnotify_data_dentry(data, data_type); */
if (parent_mark->mask & FS_EVENT_ON_CHILD) {
if (dir != moved->d_parent->d_inode) ret = fsnotify_handle_inode_event(group, parent_mark, mask,
data, data_type, dir, name, 0);
if (ret)
return ret;
}
if (!inode_mark)
return 0; return 0;
} }
if (parent_mark) {
ret = fsnotify_handle_inode_event(group, parent_mark, mask,
data, data_type, dir, name, 0);
if (ret)
return ret;
}
if (!inode_mark)
return 0;
if (mask & FS_EVENT_ON_CHILD) { if (mask & FS_EVENT_ON_CHILD) {
/* /*
* Some events can be sent on both parent dir and child marks * Some events can be sent on both parent dir and child marks
@ -324,36 +318,42 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
struct fsnotify_group *group = NULL; struct fsnotify_group *group = NULL;
__u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS); __u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS);
__u32 marks_mask = 0; __u32 marks_mask = 0;
__u32 marks_ignore_mask = 0; __u32 marks_ignored_mask = 0;
bool is_dir = mask & FS_ISDIR;
struct fsnotify_mark *mark; struct fsnotify_mark *mark;
int type; int type;
if (!iter_info->report_mask) if (WARN_ON(!iter_info->report_mask))
return 0; return 0;
/* clear ignored on inode modification */ /* clear ignored on inode modification */
if (mask & FS_MODIFY) { if (mask & FS_MODIFY) {
fsnotify_foreach_iter_mark_type(iter_info, mark, type) { fsnotify_foreach_obj_type(type) {
if (!(mark->flags & if (!fsnotify_iter_should_report_type(iter_info, type))
FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) continue;
mark->ignore_mask = 0; mark = iter_info->marks[type];
if (mark &&
!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
mark->ignored_mask = 0;
} }
} }
/* Are any of the group marks interested in this event? */ fsnotify_foreach_obj_type(type) {
fsnotify_foreach_iter_mark_type(iter_info, mark, type) { if (!fsnotify_iter_should_report_type(iter_info, type))
group = mark->group; continue;
marks_mask |= mark->mask; mark = iter_info->marks[type];
marks_ignore_mask |= /* does the object mark tell us to do something? */
fsnotify_effective_ignore_mask(mark, is_dir, type); if (mark) {
group = mark->group;
marks_mask |= mark->mask;
marks_ignored_mask |= mark->ignored_mask;
}
} }
pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignore_mask=%x data=%p data_type=%d dir=%p cookie=%d\n", pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignored_mask=%x data=%p data_type=%d dir=%p cookie=%d\n",
__func__, group, mask, marks_mask, marks_ignore_mask, __func__, group, mask, marks_mask, marks_ignored_mask,
data, data_type, dir, cookie); data, data_type, dir, cookie);
if (!(test_mask & marks_mask & ~marks_ignore_mask)) if (!(test_mask & marks_mask & ~marks_ignored_mask))
return 0; return 0;
if (group->ops->handle_event) { if (group->ops->handle_event) {
@ -390,11 +390,11 @@ static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark)
/* /*
* iter_info is a multi head priority queue of marks. * iter_info is a multi head priority queue of marks.
* Pick a subset of marks from queue heads, all with the same group * Pick a subset of marks from queue heads, all with the
* and set the report_mask to a subset of the selected marks. * same group and set the report_mask for selected subset.
* Returns false if there are no more groups to iterate. * Returns the report_mask of the selected subset.
*/ */
static bool fsnotify_iter_select_report_types( static unsigned int fsnotify_iter_select_report_types(
struct fsnotify_iter_info *iter_info) struct fsnotify_iter_info *iter_info)
{ {
struct fsnotify_group *max_prio_group = NULL; struct fsnotify_group *max_prio_group = NULL;
@ -402,7 +402,7 @@ static bool fsnotify_iter_select_report_types(
int type; int type;
/* Choose max prio group among groups of all queue heads */ /* Choose max prio group among groups of all queue heads */
fsnotify_foreach_iter_type(type) { fsnotify_foreach_obj_type(type) {
mark = iter_info->marks[type]; mark = iter_info->marks[type];
if (mark && if (mark &&
fsnotify_compare_groups(max_prio_group, mark->group) > 0) fsnotify_compare_groups(max_prio_group, mark->group) > 0)
@ -410,49 +410,30 @@ static bool fsnotify_iter_select_report_types(
} }
if (!max_prio_group) if (!max_prio_group)
return false; return 0;
/* Set the report mask for marks from same group as max prio group */ /* Set the report mask for marks from same group as max prio group */
iter_info->current_group = max_prio_group;
iter_info->report_mask = 0; iter_info->report_mask = 0;
fsnotify_foreach_iter_type(type) { fsnotify_foreach_obj_type(type) {
mark = iter_info->marks[type]; mark = iter_info->marks[type];
if (mark && mark->group == iter_info->current_group) { if (mark &&
/* fsnotify_compare_groups(max_prio_group, mark->group) == 0)
* FSNOTIFY_ITER_TYPE_PARENT indicates that this inode
* is watching children and interested in this event,
* which is an event possible on child.
* But is *this mark* watching children?
*/
if (type == FSNOTIFY_ITER_TYPE_PARENT &&
!(mark->mask & FS_EVENT_ON_CHILD) &&
!(fsnotify_ignore_mask(mark) & FS_EVENT_ON_CHILD))
continue;
fsnotify_iter_set_report_type(iter_info, type); fsnotify_iter_set_report_type(iter_info, type);
}
} }
return true; return iter_info->report_mask;
} }
/* /*
* Pop from iter_info multi head queue, the marks that belong to the group of * Pop from iter_info multi head queue, the marks that were iterated in the
* current iteration step. * current iteration step.
*/ */
static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info) static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
{ {
struct fsnotify_mark *mark;
int type; int type;
/* fsnotify_foreach_obj_type(type) {
* We cannot use fsnotify_foreach_iter_mark_type() here because we if (fsnotify_iter_should_report_type(iter_info, type))
* may need to advance a mark of type X that belongs to current_group
* but was not selected for reporting.
*/
fsnotify_foreach_iter_type(type) {
mark = iter_info->marks[type];
if (mark && mark->group == iter_info->current_group)
iter_info->marks[type] = iter_info->marks[type] =
fsnotify_next_mark(iter_info->marks[type]); fsnotify_next_mark(iter_info->marks[type]);
} }
@ -474,20 +455,18 @@ static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
* @file_name is relative to * @file_name is relative to
* @file_name: optional file name associated with event * @file_name: optional file name associated with event
* @inode: optional inode associated with event - * @inode: optional inode associated with event -
* If @dir and @inode are both non-NULL, event may be * either @dir or @inode must be non-NULL.
* reported to both. * if both are non-NULL event may be reported to both.
* @cookie: inotify rename cookie * @cookie: inotify rename cookie
*/ */
int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
const struct qstr *file_name, struct inode *inode, u32 cookie) const struct qstr *file_name, struct inode *inode, u32 cookie)
{ {
const struct path *path = fsnotify_data_path(data, data_type); const struct path *path = fsnotify_data_path(data, data_type);
struct super_block *sb = fsnotify_data_sb(data, data_type);
struct fsnotify_iter_info iter_info = {}; struct fsnotify_iter_info iter_info = {};
struct super_block *sb;
struct mount *mnt = NULL; struct mount *mnt = NULL;
struct inode *inode2 = NULL; struct inode *parent = NULL;
struct dentry *moved;
int inode2_type;
int ret = 0; int ret = 0;
__u32 test_mask, marks_mask; __u32 test_mask, marks_mask;
@ -497,20 +476,14 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
if (!inode) { if (!inode) {
/* Dirent event - report on TYPE_INODE to dir */ /* Dirent event - report on TYPE_INODE to dir */
inode = dir; inode = dir;
/* For FS_RENAME, inode is old_dir and inode2 is new_dir */
if (mask & FS_RENAME) {
moved = fsnotify_data_dentry(data, data_type);
inode2 = moved->d_parent->d_inode;
inode2_type = FSNOTIFY_ITER_TYPE_INODE2;
}
} else if (mask & FS_EVENT_ON_CHILD) { } else if (mask & FS_EVENT_ON_CHILD) {
/* /*
* Event on child - report on TYPE_PARENT to dir if it is * Event on child - report on TYPE_PARENT to dir if it is
* watching children and on TYPE_INODE to child. * watching children and on TYPE_INODE to child.
*/ */
inode2 = dir; parent = dir;
inode2_type = FSNOTIFY_ITER_TYPE_PARENT;
} }
sb = inode->i_sb;
/* /*
* Optimization: srcu_read_lock() has a memory barrier which can * Optimization: srcu_read_lock() has a memory barrier which can
@ -522,7 +495,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
if (!sb->s_fsnotify_marks && if (!sb->s_fsnotify_marks &&
(!mnt || !mnt->mnt_fsnotify_marks) && (!mnt || !mnt->mnt_fsnotify_marks) &&
(!inode || !inode->i_fsnotify_marks) && (!inode || !inode->i_fsnotify_marks) &&
(!inode2 || !inode2->i_fsnotify_marks)) (!parent || !parent->i_fsnotify_marks))
return 0; return 0;
marks_mask = sb->s_fsnotify_mask; marks_mask = sb->s_fsnotify_mask;
@ -530,35 +503,33 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
marks_mask |= mnt->mnt_fsnotify_mask; marks_mask |= mnt->mnt_fsnotify_mask;
if (inode) if (inode)
marks_mask |= inode->i_fsnotify_mask; marks_mask |= inode->i_fsnotify_mask;
if (inode2) if (parent)
marks_mask |= inode2->i_fsnotify_mask; marks_mask |= parent->i_fsnotify_mask;
/* /*
* If this is a modify event we may need to clear some ignore masks. * if this is a modify event we may need to clear the ignored masks
* In that case, the object with ignore masks will have the FS_MODIFY * otherwise return if none of the marks care about this type of event.
* event in its mask.
* Otherwise, return if none of the marks care about this type of event.
*/ */
test_mask = (mask & ALL_FSNOTIFY_EVENTS); test_mask = (mask & ALL_FSNOTIFY_EVENTS);
if (!(test_mask & marks_mask)) if (!(mask & FS_MODIFY) && !(test_mask & marks_mask))
return 0; return 0;
iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu); iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
iter_info.marks[FSNOTIFY_ITER_TYPE_SB] = iter_info.marks[FSNOTIFY_OBJ_TYPE_SB] =
fsnotify_first_mark(&sb->s_fsnotify_marks); fsnotify_first_mark(&sb->s_fsnotify_marks);
if (mnt) { if (mnt) {
iter_info.marks[FSNOTIFY_ITER_TYPE_VFSMOUNT] = iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
fsnotify_first_mark(&mnt->mnt_fsnotify_marks); fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
} }
if (inode) { if (inode) {
iter_info.marks[FSNOTIFY_ITER_TYPE_INODE] = iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
fsnotify_first_mark(&inode->i_fsnotify_marks); fsnotify_first_mark(&inode->i_fsnotify_marks);
} }
if (inode2) { if (parent) {
iter_info.marks[inode2_type] = iter_info.marks[FSNOTIFY_OBJ_TYPE_PARENT] =
fsnotify_first_mark(&inode2->i_fsnotify_marks); fsnotify_first_mark(&parent->i_fsnotify_marks);
} }
/* /*
@ -587,7 +558,7 @@ static __init int fsnotify_init(void)
{ {
int ret; int ret;
BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 23); BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 25);
ret = init_srcu_struct(&fsnotify_mark_srcu); ret = init_srcu_struct(&fsnotify_mark_srcu);
if (ret) if (ret)

View File

@ -27,21 +27,6 @@ static inline struct super_block *fsnotify_conn_sb(
return container_of(conn->obj, struct super_block, s_fsnotify_marks); return container_of(conn->obj, struct super_block, s_fsnotify_marks);
} }
static inline struct super_block *fsnotify_connector_sb(
struct fsnotify_mark_connector *conn)
{
switch (conn->type) {
case FSNOTIFY_OBJ_TYPE_INODE:
return fsnotify_conn_inode(conn)->i_sb;
case FSNOTIFY_OBJ_TYPE_VFSMOUNT:
return fsnotify_conn_mount(conn)->mnt.mnt_sb;
case FSNOTIFY_OBJ_TYPE_SB:
return fsnotify_conn_sb(conn);
default:
return NULL;
}
}
/* destroy all events sitting in this groups notification queue */ /* destroy all events sitting in this groups notification queue */
extern void fsnotify_flush_notify(struct fsnotify_group *group); extern void fsnotify_flush_notify(struct fsnotify_group *group);
@ -76,6 +61,10 @@ static inline void fsnotify_clear_marks_by_sb(struct super_block *sb)
*/ */
extern void __fsnotify_update_child_dentry_flags(struct inode *inode); extern void __fsnotify_update_child_dentry_flags(struct inode *inode);
/* allocate and destroy and event holder to attach events to notification/access queues */
extern struct fsnotify_event_holder *fsnotify_alloc_event_holder(void);
extern void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder);
extern struct kmem_cache *fsnotify_mark_connector_cachep; extern struct kmem_cache *fsnotify_mark_connector_cachep;
#endif /* __FS_NOTIFY_FSNOTIFY_H_ */ #endif /* __FS_NOTIFY_FSNOTIFY_H_ */

View File

@ -58,7 +58,7 @@ void fsnotify_destroy_group(struct fsnotify_group *group)
fsnotify_group_stop_queueing(group); fsnotify_group_stop_queueing(group);
/* Clear all marks for this group and queue them for destruction */ /* Clear all marks for this group and queue them for destruction */
fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_ANY); fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES_MASK);
/* /*
* Some marks can still be pinned when waiting for response from * Some marks can still be pinned when waiting for response from
@ -88,7 +88,7 @@ void fsnotify_destroy_group(struct fsnotify_group *group)
* that deliberately ignores overflow events. * that deliberately ignores overflow events.
*/ */
if (group->overflow_event) if (group->overflow_event)
group->ops->free_event(group, group->overflow_event); group->ops->free_event(group->overflow_event);
fsnotify_put_group(group); fsnotify_put_group(group);
} }
@ -111,19 +111,20 @@ void fsnotify_put_group(struct fsnotify_group *group)
} }
EXPORT_SYMBOL_GPL(fsnotify_put_group); EXPORT_SYMBOL_GPL(fsnotify_put_group);
static struct fsnotify_group *__fsnotify_alloc_group( /*
const struct fsnotify_ops *ops, * Create a new fsnotify_group and hold a reference for the group returned.
int flags, gfp_t gfp) */
struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops)
{ {
static struct lock_class_key nofs_marks_lock;
struct fsnotify_group *group; struct fsnotify_group *group;
group = kzalloc(sizeof(struct fsnotify_group), gfp); group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL);
if (!group) if (!group)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/* set to 0 when there a no external references to this group */ /* set to 0 when there a no external references to this group */
refcount_set(&group->refcnt, 1); refcount_set(&group->refcnt, 1);
atomic_set(&group->num_marks, 0);
atomic_set(&group->user_waits, 0); atomic_set(&group->user_waits, 0);
spin_lock_init(&group->notification_lock); spin_lock_init(&group->notification_lock);
@ -135,32 +136,9 @@ static struct fsnotify_group *__fsnotify_alloc_group(
INIT_LIST_HEAD(&group->marks_list); INIT_LIST_HEAD(&group->marks_list);
group->ops = ops; group->ops = ops;
group->flags = flags;
/*
* For most backends, eviction of inode with a mark is not expected,
* because marks hold a refcount on the inode against eviction.
*
* Use a different lockdep class for groups that support evictable
* inode marks, because with evictable marks, mark_mutex is NOT
* fs-reclaim safe - the mutex is taken when evicting inodes.
*/
if (flags & FSNOTIFY_GROUP_NOFS)
lockdep_set_class(&group->mark_mutex, &nofs_marks_lock);
return group; return group;
} }
/*
* Create a new fsnotify_group and hold a reference for the group returned.
*/
struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops,
int flags)
{
gfp_t gfp = (flags & FSNOTIFY_GROUP_USER) ? GFP_KERNEL_ACCOUNT :
GFP_KERNEL;
return __fsnotify_alloc_group(ops, flags, gfp);
}
EXPORT_SYMBOL_GPL(fsnotify_alloc_group); EXPORT_SYMBOL_GPL(fsnotify_alloc_group);
int fsnotify_fasync(int fd, struct file *file, int on) int fsnotify_fasync(int fd, struct file *file, int on)

Some files were not shown because too many files have changed in this diff Show More