Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

106
fs/nfsd/Kconfig Normal file
View file

@ -0,0 +1,106 @@
config NFSD
tristate "NFS server support"
depends on INET
depends on FILE_LOCKING
select LOCKD
select SUNRPC
select EXPORTFS
select NFS_ACL_SUPPORT if NFSD_V2_ACL
help
Choose Y here if you want to allow other computers to access
files residing on this system using Sun's Network File System
protocol. To compile the NFS server support as a module,
choose M here: the module will be called nfsd.
You may choose to use a user-space NFS server instead, in which
case you can choose N here.
To export local file systems using NFS, you also need to install
user space programs which can be found in the Linux nfs-utils
package, available from http://linux-nfs.org/. More detail about
the Linux NFS server implementation is available via the
exports(5) man page.
Below you can choose which versions of the NFS protocol are
available to clients mounting the NFS server on this system.
Support for NFS version 2 (RFC 1094) is always available when
CONFIG_NFSD is selected.
If unsure, say N.
config NFSD_V2_ACL
bool
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
bool "NFS server support for the NFSv3 ACL protocol extension"
depends on NFSD_V3
select NFSD_V2_ACL
help
Solaris NFS servers support an auxiliary NFSv3 ACL protocol that
never became an official part of the NFS version 3 protocol.
This protocol extension allows applications on NFS clients to
manipulate POSIX Access Control Lists on files residing on NFS
servers. NFS servers enforce POSIX ACLs on local files whether
this protocol is available or not.
This option enables support in your system's NFS server for the
NFSv3 ACL protocol extension allowing NFS clients to manipulate
POSIX ACLs on files exported by your system's NFS server. NFS
clients which support the Solaris NFSv3 ACL protocol can then
access and modify ACLs on your NFS server.
To store ACLs on your NFS server, you also need to enable ACL-
related CONFIG options for your local file systems of choice.
If unsure, say N.
config NFSD_V4
bool "NFS server support for NFS version 4"
depends on NFSD && PROC_FS
select NFSD_V3
select FS_POSIX_ACL
select SUNRPC_GSS
select CRYPTO
select GRACE_PERIOD
help
This option enables support in your system's NFS server for
version 4 of the NFS protocol (RFC 3530).
To export files using NFSv4, you need to install additional user
space programs which can be found in the Linux nfs-utils package,
available from http://linux-nfs.org/.
If unsure, say N.
config NFSD_V4_SECURITY_LABEL
bool "Provide Security Label support for NFSv4 server"
depends on NFSD_V4 && SECURITY
help
Say Y here if you want enable fine-grained security label attribute
support for NFS version 4. Security labels allow security modules like
SELinux and Smack to label files to facilitate enforcement of their policies.
Without this an NFSv4 mount will have the same label on each file.
If you do not wish to enable fine-grained security labels SELinux or
Smack policies on NFSv4 files, say N.
config NFSD_FAULT_INJECTION
bool "NFS server manual fault injection"
depends on NFSD_V4 && DEBUG_KERNEL
help
This option enables support for manually injecting faults
into the NFS server. This is intended to be used for
testing error recovery on the NFS client.
If unsure, say N.

14
fs/nfsd/Makefile Normal file
View file

@ -0,0 +1,14 @@
#
# Makefile for the Linux nfs server
#
obj-$(CONFIG_NFSD) += nfsd.o
nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
nfsd-$(CONFIG_NFSD_FAULT_INJECTION) += fault_inject.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
nfs4acl.o nfs4callback.o nfs4recover.o

59
fs/nfsd/acl.h Normal file
View file

@ -0,0 +1,59 @@
/*
* Common NFSv4 ACL handling definitions.
*
* Copyright (c) 2002 The Regents of the University of Michigan.
* All rights reserved.
*
* Marius Aamodt Eriksen <marius@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LINUX_NFS4_ACL_H
#define LINUX_NFS4_ACL_H
struct nfs4_acl;
struct svc_fh;
struct svc_rqst;
/*
* Maximum ACL we'll accept from a client; chosen (somewhat
* arbitrarily) so that kmalloc'ing the ACL shouldn't require a
* high-order allocation. This allows 204 ACEs on x86_64:
*/
#define NFS4_ACL_MAX ((PAGE_SIZE - sizeof(struct nfs4_acl)) \
/ sizeof(struct nfs4_ace))
int nfs4_acl_bytes(int entries);
int nfs4_acl_get_whotype(char *, u32);
__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
struct nfs4_acl **acl);
__be32 nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfs4_acl *acl);
#endif /* LINUX_NFS4_ACL_H */

90
fs/nfsd/auth.c Normal file
View file

@ -0,0 +1,90 @@
/* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */
#include <linux/sched.h>
#include "nfsd.h"
#include "auth.h"
int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
{
struct exp_flavor_info *f;
struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
for (f = exp->ex_flavors; f < end; f++) {
if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
return f->flags;
}
return exp->ex_flags;
}
int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
{
struct group_info *rqgi;
struct group_info *gi;
struct cred *new;
int i;
int flags = nfsexp_flags(rqstp, exp);
validate_process_creds();
/* discard any old override before preparing the new set */
revert_creds(get_cred(current_real_cred()));
new = prepare_creds();
if (!new)
return -ENOMEM;
new->fsuid = rqstp->rq_cred.cr_uid;
new->fsgid = rqstp->rq_cred.cr_gid;
rqgi = rqstp->rq_cred.cr_group_info;
if (flags & NFSEXP_ALLSQUASH) {
new->fsuid = exp->ex_anon_uid;
new->fsgid = exp->ex_anon_gid;
gi = groups_alloc(0);
if (!gi)
goto oom;
} else if (flags & NFSEXP_ROOTSQUASH) {
if (uid_eq(new->fsuid, GLOBAL_ROOT_UID))
new->fsuid = exp->ex_anon_uid;
if (gid_eq(new->fsgid, GLOBAL_ROOT_GID))
new->fsgid = exp->ex_anon_gid;
gi = groups_alloc(rqgi->ngroups);
if (!gi)
goto oom;
for (i = 0; i < rqgi->ngroups; i++) {
if (gid_eq(GLOBAL_ROOT_GID, GROUP_AT(rqgi, i)))
GROUP_AT(gi, i) = exp->ex_anon_gid;
else
GROUP_AT(gi, i) = GROUP_AT(rqgi, i);
}
} else {
gi = get_group_info(rqgi);
}
if (uid_eq(new->fsuid, INVALID_UID))
new->fsuid = exp->ex_anon_uid;
if (gid_eq(new->fsgid, INVALID_GID))
new->fsgid = exp->ex_anon_gid;
set_groups(new, gi);
put_group_info(gi);
if (!uid_eq(new->fsuid, GLOBAL_ROOT_UID))
new->cap_effective = cap_drop_nfsd_set(new->cap_effective);
else
new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
new->cap_permitted);
validate_process_creds();
put_cred(override_creds(new));
put_cred(new);
validate_process_creds();
return 0;
oom:
abort_creds(new);
return -ENOMEM;
}

16
fs/nfsd/auth.h Normal file
View file

@ -0,0 +1,16 @@
/*
* nfsd-specific authentication stuff.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef LINUX_NFSD_AUTH_H
#define LINUX_NFSD_AUTH_H
/*
* Set the current process's fsuid/fsgid etc to those of the NFS
* client user
*/
int nfsd_setuser(struct svc_rqst *, struct svc_export *);
#endif /* LINUX_NFSD_AUTH_H */

86
fs/nfsd/cache.h Normal file
View file

@ -0,0 +1,86 @@
/*
* Request reply cache. This was heavily inspired by the
* implementation in 4.3BSD/4.4BSD.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef NFSCACHE_H
#define NFSCACHE_H
#include <linux/sunrpc/svc.h>
/*
* Representation of a reply cache entry.
*
* Note that we use a sockaddr_in6 to hold the address instead of the more
* typical sockaddr_storage. This is for space reasons, since sockaddr_storage
* is much larger than a sockaddr_in6.
*/
struct svc_cacherep {
struct list_head c_lru;
unsigned char c_state, /* unused, inprog, done */
c_type, /* status, buffer */
c_secure : 1; /* req came from port < 1024 */
struct sockaddr_in6 c_addr;
__be32 c_xid;
u32 c_prot;
u32 c_proc;
u32 c_vers;
unsigned int c_len;
__wsum c_csum;
unsigned long c_timestamp;
union {
struct kvec u_vec;
__be32 u_status;
} c_u;
};
#define c_replvec c_u.u_vec
#define c_replstat c_u.u_status
/* cache entry states */
enum {
RC_UNUSED,
RC_INPROG,
RC_DONE
};
/* return values */
enum {
RC_DROPIT,
RC_REPLY,
RC_DOIT
};
/*
* Cache types.
* We may want to add more types one day, e.g. for diropres and
* attrstat replies. Using cache entries with fixed length instead
* of buffer pointers may be more efficient.
*/
enum {
RC_NOCACHE,
RC_REPLSTAT,
RC_REPLBUFF,
};
/*
* If requests are retransmitted within this interval, they're dropped.
*/
#define RC_DELAY (HZ/5)
/* Cache entries expire after this time period */
#define RC_EXPIRE (120 * HZ)
/* Checksum this amount of the request */
#define RC_CSUMLEN (256U)
int nfsd_reply_cache_init(void);
void nfsd_reply_cache_shutdown(void);
int nfsd_cache_lookup(struct svc_rqst *);
void nfsd_cache_update(struct svc_rqst *, int, __be32 *);
int nfsd_reply_cache_stats_open(struct inode *, struct file *);
#endif /* NFSCACHE_H */

28
fs/nfsd/current_stateid.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef _NFSD4_CURRENT_STATE_H
#define _NFSD4_CURRENT_STATE_H
#include "state.h"
#include "xdr4.h"
extern void clear_current_stateid(struct nfsd4_compound_state *cstate);
/*
* functions to set current state id
*/
extern void nfsd4_set_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *);
extern void nfsd4_set_openstateid(struct nfsd4_compound_state *, struct nfsd4_open *);
extern void nfsd4_set_lockstateid(struct nfsd4_compound_state *, struct nfsd4_lock *);
extern void nfsd4_set_closestateid(struct nfsd4_compound_state *, struct nfsd4_close *);
/*
* functions to consume current state id
*/
extern void nfsd4_get_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *);
extern void nfsd4_get_delegreturnstateid(struct nfsd4_compound_state *, struct nfsd4_delegreturn *);
extern void nfsd4_get_freestateid(struct nfsd4_compound_state *, struct nfsd4_free_stateid *);
extern void nfsd4_get_setattrstateid(struct nfsd4_compound_state *, struct nfsd4_setattr *);
extern void nfsd4_get_closestateid(struct nfsd4_compound_state *, struct nfsd4_close *);
extern void nfsd4_get_lockustateid(struct nfsd4_compound_state *, struct nfsd4_locku *);
extern void nfsd4_get_readstateid(struct nfsd4_compound_state *, struct nfsd4_read *);
extern void nfsd4_get_writestateid(struct nfsd4_compound_state *, struct nfsd4_write *);
#endif /* _NFSD4_CURRENT_STATE_H */

1337
fs/nfsd/export.c Normal file

File diff suppressed because it is too large Load diff

111
fs/nfsd/export.h Normal file
View file

@ -0,0 +1,111 @@
/*
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef NFSD_EXPORT_H
#define NFSD_EXPORT_H
#include <linux/sunrpc/cache.h>
#include <uapi/linux/nfsd/export.h>
struct knfsd_fh;
struct svc_fh;
struct svc_rqst;
/*
* FS Locations
*/
#define MAX_FS_LOCATIONS 128
struct nfsd4_fs_location {
char *hosts; /* colon separated list of hosts */
char *path; /* slash separated list of path components */
};
struct nfsd4_fs_locations {
uint32_t locations_count;
struct nfsd4_fs_location *locations;
/* If we're not actually serving this data ourselves (only providing a
* list of replicas that do serve it) then we set "migrated": */
int migrated;
};
/*
* We keep an array of pseudoflavors with the export, in order from most
* to least preferred. For the foreseeable future, we don't expect more
* than the eight pseudoflavors null, unix, krb5, krb5i, krb5p, skpm3,
* spkm3i, and spkm3p (and using all 8 at once should be rare).
*/
#define MAX_SECINFO_LIST 8
#define EX_UUID_LEN 16
struct exp_flavor_info {
u32 pseudoflavor;
u32 flags;
};
struct svc_export {
struct cache_head h;
struct auth_domain * ex_client;
int ex_flags;
struct path ex_path;
kuid_t ex_anon_uid;
kgid_t ex_anon_gid;
int ex_fsid;
unsigned char * ex_uuid; /* 16 byte fsid */
struct nfsd4_fs_locations ex_fslocs;
uint32_t ex_nflavors;
struct exp_flavor_info ex_flavors[MAX_SECINFO_LIST];
struct cache_detail *cd;
};
/* an "export key" (expkey) maps a filehandlefragement to an
* svc_export for a given client. There can be several per export,
* for the different fsid types.
*/
struct svc_expkey {
struct cache_head h;
struct auth_domain * ek_client;
int ek_fsidtype;
u32 ek_fsid[6];
struct path ek_path;
};
#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC))
#define EX_NOHIDE(exp) ((exp)->ex_flags & NFSEXP_NOHIDE)
#define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES)
int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp);
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp);
/*
* Function declarations
*/
int nfsd_export_init(struct net *);
void nfsd_export_shutdown(struct net *);
void nfsd_export_flush(struct net *);
struct svc_export * rqst_exp_get_by_name(struct svc_rqst *,
struct path *);
struct svc_export * rqst_exp_parent(struct svc_rqst *,
struct path *);
struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *);
int exp_rootfh(struct net *, struct auth_domain *,
char *path, struct knfsd_fh *, int maxsize);
__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *);
__be32 nfserrno(int errno);
static inline void exp_put(struct svc_export *exp)
{
cache_put(&exp->h, exp->cd);
}
static inline struct svc_export *exp_get(struct svc_export *exp)
{
cache_get(&exp->h);
return exp;
}
struct svc_export * rqst_exp_find(struct svc_rqst *, int, u32 *);
#endif /* NFSD_EXPORT_H */

150
fs/nfsd/fault_inject.c Normal file
View file

@ -0,0 +1,150 @@
/*
* Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com>
*
* Uses debugfs to create fault injection points for client testing
*/
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/nsproxy.h>
#include <linux/sunrpc/addr.h>
#include <asm/uaccess.h>
#include "state.h"
#include "netns.h"
struct nfsd_fault_inject_op {
char *file;
u64 (*get)(void);
u64 (*set_val)(u64);
u64 (*set_clnt)(struct sockaddr_storage *, size_t);
};
static struct dentry *debug_dir;
static ssize_t fault_inject_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos)
{
static u64 val;
char read_buf[25];
size_t size;
loff_t pos = *ppos;
struct nfsd_fault_inject_op *op = file_inode(file)->i_private;
if (!pos)
val = op->get();
size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val);
return simple_read_from_buffer(buf, len, ppos, read_buf, size);
}
static ssize_t fault_inject_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
char write_buf[INET6_ADDRSTRLEN];
size_t size = min(sizeof(write_buf) - 1, len);
struct net *net = current->nsproxy->net_ns;
struct sockaddr_storage sa;
struct nfsd_fault_inject_op *op = file_inode(file)->i_private;
u64 val;
char *nl;
if (copy_from_user(write_buf, buf, size))
return -EFAULT;
write_buf[size] = '\0';
/* Deal with any embedded newlines in the string */
nl = strchr(write_buf, '\n');
if (nl) {
size = nl - write_buf;
*nl = '\0';
}
size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa));
if (size > 0) {
val = op->set_clnt(&sa, size);
if (val)
pr_info("NFSD [%s]: Client %s had %llu state object(s)\n",
op->file, write_buf, val);
} else {
val = simple_strtoll(write_buf, NULL, 0);
if (val == 0)
pr_info("NFSD Fault Injection: %s (all)", op->file);
else
pr_info("NFSD Fault Injection: %s (n = %llu)",
op->file, val);
val = op->set_val(val);
pr_info("NFSD: %s: found %llu", op->file, val);
}
return len; /* on success, claim we got the whole input */
}
static const struct file_operations fops_nfsd = {
.owner = THIS_MODULE,
.read = fault_inject_read,
.write = fault_inject_write,
};
void nfsd_fault_inject_cleanup(void)
{
debugfs_remove_recursive(debug_dir);
}
static struct nfsd_fault_inject_op inject_ops[] = {
{
.file = "forget_clients",
.get = nfsd_inject_print_clients,
.set_val = nfsd_inject_forget_clients,
.set_clnt = nfsd_inject_forget_client,
},
{
.file = "forget_locks",
.get = nfsd_inject_print_locks,
.set_val = nfsd_inject_forget_locks,
.set_clnt = nfsd_inject_forget_client_locks,
},
{
.file = "forget_openowners",
.get = nfsd_inject_print_openowners,
.set_val = nfsd_inject_forget_openowners,
.set_clnt = nfsd_inject_forget_client_openowners,
},
{
.file = "forget_delegations",
.get = nfsd_inject_print_delegations,
.set_val = nfsd_inject_forget_delegations,
.set_clnt = nfsd_inject_forget_client_delegations,
},
{
.file = "recall_delegations",
.get = nfsd_inject_print_delegations,
.set_val = nfsd_inject_recall_delegations,
.set_clnt = nfsd_inject_recall_client_delegations,
},
};
#define NUM_INJECT_OPS (sizeof(inject_ops)/sizeof(struct nfsd_fault_inject_op))
int nfsd_fault_inject_init(void)
{
unsigned int i;
struct nfsd_fault_inject_op *op;
umode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
debug_dir = debugfs_create_dir("nfsd", NULL);
if (!debug_dir)
goto fail;
for (i = 0; i < NUM_INJECT_OPS; i++) {
op = &inject_ops[i];
if (!debugfs_create_file(op->file, mode, debug_dir, op, &fops_nfsd))
goto fail;
}
return 0;
fail:
nfsd_fault_inject_cleanup();
return -ENOMEM;
}

62
fs/nfsd/idmap.h Normal file
View file

@ -0,0 +1,62 @@
/*
* Mapping of UID to name and vice versa.
*
* Copyright (c) 2002, 2003 The Regents of the University of
* Michigan. All rights reserved.
> *
* Marius Aamodt Eriksen <marius@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LINUX_NFSD_IDMAP_H
#define LINUX_NFSD_IDMAP_H
#include <linux/in.h>
#include <linux/sunrpc/svc.h>
/* XXX from linux/nfs_idmap.h */
#define IDMAP_NAMESZ 128
#ifdef CONFIG_NFSD_V4
int nfsd_idmap_init(struct net *);
void nfsd_idmap_shutdown(struct net *);
#else
static inline int nfsd_idmap_init(struct net *net)
{
return 0;
}
static inline void nfsd_idmap_shutdown(struct net *net)
{
}
#endif
__be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, kuid_t *);
__be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, kgid_t *);
__be32 nfsd4_encode_user(struct xdr_stream *, struct svc_rqst *, kuid_t);
__be32 nfsd4_encode_group(struct xdr_stream *, struct svc_rqst *, kgid_t);
#endif /* LINUX_NFSD_IDMAP_H */

77
fs/nfsd/lockd.c Normal file
View file

@ -0,0 +1,77 @@
/*
* This file contains all the stubs needed when communicating with lockd.
* This level of indirection is necessary so we can run nfsd+lockd without
* requiring the nfs client to be compiled in/loaded, and vice versa.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/file.h>
#include <linux/lockd/bind.h>
#include "nfsd.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_LOCKD
#ifdef CONFIG_LOCKD_V4
#define nlm_stale_fh nlm4_stale_fh
#define nlm_failed nlm4_failed
#else
#define nlm_stale_fh nlm_lck_denied_nolocks
#define nlm_failed nlm_lck_denied_nolocks
#endif
/*
* Note: we hold the dentry use count while the file is open.
*/
static __be32
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
{
__be32 nfserr;
struct svc_fh fh;
/* must initialize before using! but maxsize doesn't matter */
fh_init(&fh,0);
fh.fh_handle.fh_size = f->size;
memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
fh.fh_export = NULL;
nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp);
fh_put(&fh);
/* We return nlm error codes as nlm doesn't know
* about nfsd, but nfsd does know about nlm..
*/
switch (nfserr) {
case nfs_ok:
return 0;
case nfserr_dropit:
return nlm_drop_reply;
case nfserr_stale:
return nlm_stale_fh;
default:
return nlm_failed;
}
}
static void
nlm_fclose(struct file *filp)
{
fput(filp);
}
static struct nlmsvc_binding nfsd_nlm_ops = {
.fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */
};
void
nfsd_lockd_init(void)
{
dprintk("nfsd: initializing lockd\n");
nlmsvc_ops = &nfsd_nlm_ops;
}
void
nfsd_lockd_shutdown(void)
{
nlmsvc_ops = NULL;
}

121
fs/nfsd/netns.h Normal file
View file

@ -0,0 +1,121 @@
/*
* per net namespace data structures for nfsd
*
* Copyright (C) 2012, Jeff Layton <jlayton@redhat.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __NFSD_NETNS_H__
#define __NFSD_NETNS_H__
#include <net/net_namespace.h>
#include <net/netns/generic.h>
/* Hash tables for nfs4_clientid state */
#define CLIENT_HASH_BITS 4
#define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS)
#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1)
#define SESSION_HASH_SIZE 512
struct cld_net;
struct nfsd4_client_tracking_ops;
/*
* Represents a nfsd "container". With respect to nfsv4 state tracking, the
* fields of interest are the *_id_hashtbls and the *_name_tree. These track
* the nfs4_client objects by either short or long form clientid.
*
* Each nfsd_net runs a nfs4_laundromat workqueue job when necessary to clean
* up expired clients and delegations within the container.
*/
struct nfsd_net {
struct cld_net *cld_net;
struct cache_detail *svc_expkey_cache;
struct cache_detail *svc_export_cache;
struct cache_detail *idtoname_cache;
struct cache_detail *nametoid_cache;
struct lock_manager nfsd4_manager;
bool grace_ended;
time_t boot_time;
/*
* reclaim_str_hashtbl[] holds known client info from previous reset/reboot
* used in reboot/reset lease grace period processing
*
* conf_id_hashtbl[], and conf_name_tree hold confirmed
* setclientid_confirmed info.
*
* unconf_str_hastbl[] and unconf_name_tree hold unconfirmed
* setclientid info.
*/
struct list_head *reclaim_str_hashtbl;
int reclaim_str_hashtbl_size;
struct list_head *conf_id_hashtbl;
struct rb_root conf_name_tree;
struct list_head *unconf_id_hashtbl;
struct rb_root unconf_name_tree;
struct list_head *sessionid_hashtbl;
/*
* client_lru holds client queue ordered by nfs4_client.cl_time
* for lease renewal.
*
* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
* for last close replay.
*
* All of the above fields are protected by the client_mutex.
*/
struct list_head client_lru;
struct list_head close_lru;
struct list_head del_recall_lru;
struct delayed_work laundromat_work;
/* client_lock protects the client lru list and session hash table */
spinlock_t client_lock;
struct file *rec_file;
bool in_grace;
struct nfsd4_client_tracking_ops *client_tracking_ops;
time_t nfsd4_lease;
time_t nfsd4_grace;
bool nfsd_net_up;
bool lockd_up;
/* Time of server startup */
struct timeval nfssvc_boot;
/*
* Max number of connections this nfsd container will allow. Defaults
* to '0' which is means that it bases this on the number of threads.
*/
unsigned int max_connections;
u32 clientid_counter;
struct svc_serv *nfsd_serv;
};
/* Simple check to find out if a given net was properly initialized */
#define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl)
extern int nfsd_net_id;
#endif /* __NFSD_NETNS_H__ */

382
fs/nfsd/nfs2acl.c Normal file
View file

@ -0,0 +1,382 @@
/*
* Process version 2 NFSACL requests.
*
* Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
*/
#include "nfsd.h"
/* FIXME: nfsacl.h is a broken header */
#include <linux/nfsacl.h>
#include <linux/gfp.h>
#include "cache.h"
#include "xdr3.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
#define RETURN_STATUS(st) { resp->status = (st); return (st); }
/*
* NULL call.
*/
static __be32
nfsacld_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
return nfs_ok;
}
/*
* Get the Access and/or Default ACL of a file.
*/
static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp,
struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
{
struct posix_acl *acl;
struct inode *inode;
svc_fh *fh;
__be32 nfserr = 0;
dprintk("nfsd: GETACL(2acl) %s\n", SVCFH_fmt(&argp->fh));
fh = fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
if (nfserr)
RETURN_STATUS(nfserr);
inode = fh->fh_dentry->d_inode;
if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
RETURN_STATUS(nfserr_inval);
resp->mask = argp->mask;
nfserr = fh_getattr(fh, &resp->stat);
if (nfserr)
goto fail;
if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
acl = get_acl(inode, ACL_TYPE_ACCESS);
if (acl == NULL) {
/* Solaris returns the inode's minimum ACL. */
acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
}
if (IS_ERR(acl)) {
nfserr = nfserrno(PTR_ERR(acl));
goto fail;
}
resp->acl_access = acl;
}
if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
/* Check how Solaris handles requests for the Default ACL
of a non-directory! */
acl = get_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(acl)) {
nfserr = nfserrno(PTR_ERR(acl));
goto fail;
}
resp->acl_default = acl;
}
/* resp->acl_{access,default} are released in nfssvc_release_getacl. */
RETURN_STATUS(0);
fail:
posix_acl_release(resp->acl_access);
posix_acl_release(resp->acl_default);
RETURN_STATUS(nfserr);
}
/*
* Set the Access and/or Default ACL of a file.
*/
static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp,
struct nfsd3_setaclargs *argp,
struct nfsd_attrstat *resp)
{
struct inode *inode;
svc_fh *fh;
__be32 nfserr = 0;
int error;
dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh));
fh = fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR);
if (nfserr)
goto out;
inode = fh->fh_dentry->d_inode;
if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) {
error = -EOPNOTSUPP;
goto out_errno;
}
error = fh_want_write(fh);
if (error)
goto out_errno;
error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS);
if (error)
goto out_drop_write;
error = inode->i_op->set_acl(inode, argp->acl_default,
ACL_TYPE_DEFAULT);
if (error)
goto out_drop_write;
fh_drop_write(fh);
nfserr = fh_getattr(fh, &resp->stat);
out:
/* argp->acl_{access,default} may have been allocated in
nfssvc_decode_setaclargs. */
posix_acl_release(argp->acl_access);
posix_acl_release(argp->acl_default);
return nfserr;
out_drop_write:
fh_drop_write(fh);
out_errno:
nfserr = nfserrno(error);
goto out;
}
/*
* Check file attributes
*/
static __be32 nfsacld_proc_getattr(struct svc_rqst * rqstp,
struct nfsd_fhandle *argp, struct nfsd_attrstat *resp)
{
__be32 nfserr;
dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
if (nfserr)
return nfserr;
nfserr = fh_getattr(&resp->fh, &resp->stat);
return nfserr;
}
/*
* Check file access
*/
static __be32 nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
struct nfsd3_accessres *resp)
{
__be32 nfserr;
dprintk("nfsd: ACCESS(2acl) %s 0x%x\n",
SVCFH_fmt(&argp->fh),
argp->access);
fh_copy(&resp->fh, &argp->fh);
resp->access = argp->access;
nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
if (nfserr)
return nfserr;
nfserr = fh_getattr(&resp->fh, &resp->stat);
return nfserr;
}
/*
* XDR decode functions
*/
static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclargs *argp)
{
p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0;
argp->mask = ntohl(*p); p++;
return xdr_argsize_check(rqstp, p);
}
static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_setaclargs *argp)
{
struct kvec *head = rqstp->rq_arg.head;
unsigned int base;
int n;
p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0;
argp->mask = ntohl(*p++);
if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
!xdr_argsize_check(rqstp, p))
return 0;
base = (char *)p - (char *)head->iov_base;
n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
(argp->mask & NFS_ACL) ?
&argp->acl_access : NULL);
if (n > 0)
n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
(argp->mask & NFS_DFACL) ?
&argp->acl_default : NULL);
return (n > 0);
}
static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_fhandle *argp)
{
p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0;
return xdr_argsize_check(rqstp, p);
}
static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_accessargs *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
*/
/*
* 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, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
/* GETACL */
static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclres *resp)
{
struct dentry *dentry = resp->fh.fh_dentry;
struct inode *inode;
struct kvec *head = rqstp->rq_res.head;
unsigned int base;
int n;
int w;
/*
* 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 || dentry->d_inode == NULL)
return 0;
inode = dentry->d_inode;
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
*p++ = htonl(resp->mask);
if (!xdr_ressize_check(rqstp, p))
return 0;
base = (char *)p - (char *)head->iov_base;
rqstp->rq_res.page_len = w = 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;
return 1;
}
static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_attrstat *resp)
{
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
return xdr_ressize_check(rqstp, p);
}
/* ACCESS */
static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_accessres *resp)
{
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
*p++ = htonl(resp->access);
return xdr_ressize_check(rqstp, p);
}
/*
* XDR release functions
*/
static int nfsaclsvc_release_getacl(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclres *resp)
{
fh_put(&resp->fh);
posix_acl_release(resp->acl_access);
posix_acl_release(resp->acl_default);
return 1;
}
static int nfsaclsvc_release_attrstat(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_attrstat *resp)
{
fh_put(&resp->fh);
return 1;
}
static int nfsaclsvc_release_access(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_accessres *resp)
{
fh_put(&resp->fh);
return 1;
}
#define nfsaclsvc_decode_voidargs NULL
#define nfsaclsvc_release_void NULL
#define nfsd3_fhandleargs nfsd_fhandle
#define nfsd3_attrstatres nfsd_attrstat
#define nfsd3_voidres nfsd3_voidargs
struct nfsd3_voidargs { int dummy; };
#define PROC(name, argt, rest, relt, cache, respsize) \
{ (svc_procfunc) nfsacld_proc_##name, \
(kxdrproc_t) nfsaclsvc_decode_##argt##args, \
(kxdrproc_t) nfsaclsvc_encode_##rest##res, \
(kxdrproc_t) nfsaclsvc_release_##relt, \
sizeof(struct nfsd3_##argt##args), \
sizeof(struct nfsd3_##rest##res), \
0, \
cache, \
respsize, \
}
#define ST 1 /* status*/
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
#define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */
static struct svc_procedure nfsd_acl_procedures2[] = {
PROC(null, void, void, void, RC_NOCACHE, ST),
PROC(getacl, getacl, getacl, getacl, RC_NOCACHE, ST+1+2*(1+ACL)),
PROC(setacl, setacl, attrstat, attrstat, RC_NOCACHE, ST+AT),
PROC(getattr, fhandle, attrstat, attrstat, RC_NOCACHE, ST+AT),
PROC(access, access, access, access, RC_NOCACHE, ST+AT+1),
};
struct svc_version nfsd_acl_version2 = {
.vs_vers = 2,
.vs_nproc = 5,
.vs_proc = nfsd_acl_procedures2,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
.vs_hidden = 0,
};

273
fs/nfsd/nfs3acl.c Normal file
View file

@ -0,0 +1,273 @@
/*
* Process version 3 NFSACL requests.
*
* Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
*/
#include "nfsd.h"
/* FIXME: nfsacl.h is a broken header */
#include <linux/nfsacl.h>
#include <linux/gfp.h>
#include "cache.h"
#include "xdr3.h"
#include "vfs.h"
#define RETURN_STATUS(st) { resp->status = (st); return (st); }
/*
* NULL call.
*/
static __be32
nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
return nfs_ok;
}
/*
* Get the Access and/or Default ACL of a file.
*/
static __be32 nfsd3_proc_getacl(struct svc_rqst * rqstp,
struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp)
{
struct posix_acl *acl;
struct inode *inode;
svc_fh *fh;
__be32 nfserr = 0;
fh = fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
if (nfserr)
RETURN_STATUS(nfserr);
inode = fh->fh_dentry->d_inode;
if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
RETURN_STATUS(nfserr_inval);
resp->mask = argp->mask;
if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
acl = get_acl(inode, ACL_TYPE_ACCESS);
if (acl == NULL) {
/* Solaris returns the inode's minimum ACL. */
acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
}
if (IS_ERR(acl)) {
nfserr = nfserrno(PTR_ERR(acl));
goto fail;
}
resp->acl_access = acl;
}
if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
/* Check how Solaris handles requests for the Default ACL
of a non-directory! */
acl = get_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(acl)) {
nfserr = nfserrno(PTR_ERR(acl));
goto fail;
}
resp->acl_default = acl;
}
/* resp->acl_{access,default} are released in nfs3svc_release_getacl. */
RETURN_STATUS(0);
fail:
posix_acl_release(resp->acl_access);
posix_acl_release(resp->acl_default);
RETURN_STATUS(nfserr);
}
/*
* Set the Access and/or Default ACL of a file.
*/
static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp,
struct nfsd3_setaclargs *argp,
struct nfsd3_attrstat *resp)
{
struct inode *inode;
svc_fh *fh;
__be32 nfserr = 0;
int error;
fh = fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR);
if (nfserr)
goto out;
inode = fh->fh_dentry->d_inode;
if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) {
error = -EOPNOTSUPP;
goto out_errno;
}
error = fh_want_write(fh);
if (error)
goto out_errno;
error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS);
if (error)
goto out_drop_write;
error = inode->i_op->set_acl(inode, argp->acl_default,
ACL_TYPE_DEFAULT);
out_drop_write:
fh_drop_write(fh);
out_errno:
nfserr = nfserrno(error);
out:
/* argp->acl_{access,default} may have been allocated in
nfs3svc_decode_setaclargs. */
posix_acl_release(argp->acl_access);
posix_acl_release(argp->acl_default);
RETURN_STATUS(nfserr);
}
/*
* XDR decode functions
*/
static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclargs *args)
{
p = nfs3svc_decode_fh(p, &args->fh);
if (!p)
return 0;
args->mask = ntohl(*p); p++;
return xdr_argsize_check(rqstp, p);
}
static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_setaclargs *args)
{
struct kvec *head = rqstp->rq_arg.head;
unsigned int base;
int n;
p = nfs3svc_decode_fh(p, &args->fh);
if (!p)
return 0;
args->mask = ntohl(*p++);
if (args->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
!xdr_argsize_check(rqstp, p))
return 0;
base = (char *)p - (char *)head->iov_base;
n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
(args->mask & NFS_ACL) ?
&args->acl_access : NULL);
if (n > 0)
n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
(args->mask & NFS_DFACL) ?
&args->acl_default : NULL);
return (n > 0);
}
/*
* XDR encode functions
*/
/* GETACL */
static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclres *resp)
{
struct dentry *dentry = resp->fh.fh_dentry;
p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
if (resp->status == 0 && dentry && dentry->d_inode) {
struct inode *inode = dentry->d_inode;
struct kvec *head = rqstp->rq_res.head;
unsigned int base;
int n;
int w;
*p++ = htonl(resp->mask);
if (!xdr_ressize_check(rqstp, p))
return 0;
base = (char *)p - (char *)head->iov_base;
rqstp->rq_res.page_len = w = 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 */
static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_attrstat *resp)
{
p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
return xdr_ressize_check(rqstp, p);
}
/*
* XDR release functions
*/
static int nfs3svc_release_getacl(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclres *resp)
{
fh_put(&resp->fh);
posix_acl_release(resp->acl_access);
posix_acl_release(resp->acl_default);
return 1;
}
#define nfs3svc_decode_voidargs NULL
#define nfs3svc_release_void NULL
#define nfsd3_setaclres nfsd3_attrstat
#define nfsd3_voidres nfsd3_voidargs
struct nfsd3_voidargs { int dummy; };
#define PROC(name, argt, rest, relt, cache, respsize) \
{ (svc_procfunc) nfsd3_proc_##name, \
(kxdrproc_t) nfs3svc_decode_##argt##args, \
(kxdrproc_t) nfs3svc_encode_##rest##res, \
(kxdrproc_t) nfs3svc_release_##relt, \
sizeof(struct nfsd3_##argt##args), \
sizeof(struct nfsd3_##rest##res), \
0, \
cache, \
respsize, \
}
#define ST 1 /* status*/
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
#define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */
static struct svc_procedure nfsd_acl_procedures3[] = {
PROC(null, void, void, void, RC_NOCACHE, ST),
PROC(getacl, getacl, getacl, getacl, RC_NOCACHE, ST+1+2*(1+ACL)),
PROC(setacl, setacl, setacl, fhandle, RC_NOCACHE, ST+pAT),
};
struct svc_version nfsd_acl_version3 = {
.vs_vers = 3,
.vs_nproc = 3,
.vs_proc = nfsd_acl_procedures3,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
.vs_hidden = 0,
};

891
fs/nfsd/nfs3proc.c Normal file
View file

@ -0,0 +1,891 @@
/*
* Process version 3 NFS requests.
*
* Copyright (C) 1996, 1997, 1998 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/magic.h>
#include "cache.h"
#include "xdr3.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
#define RETURN_STATUS(st) { resp->status = (st); return (st); }
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
S_IFDIR, /* NF3DIR */
S_IFBLK, /* NF3BLK */
S_IFCHR, /* NF3CHR */
S_IFLNK, /* NF3LNK */
S_IFSOCK, /* NF3SOCK */
S_IFIFO, /* NF3FIFO */
};
/*
* NULL call.
*/
static __be32
nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
return nfs_ok;
}
/*
* Get a file's attributes
*/
static __be32
nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
struct nfsd3_attrstat *resp)
{
__be32 nfserr;
dprintk("nfsd: GETATTR(3) %s\n",
SVCFH_fmt(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0,
NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
if (nfserr)
RETURN_STATUS(nfserr);
nfserr = fh_getattr(&resp->fh, &resp->stat);
RETURN_STATUS(nfserr);
}
/*
* Set a file's attributes
*/
static __be32
nfsd3_proc_setattr(struct svc_rqst *rqstp, struct nfsd3_sattrargs *argp,
struct nfsd3_attrstat *resp)
{
__be32 nfserr;
dprintk("nfsd: SETATTR(3) %s\n",
SVCFH_fmt(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,
argp->check_guard, argp->guardtime);
RETURN_STATUS(nfserr);
}
/*
* Look up a path name component
*/
static __be32
nfsd3_proc_lookup(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
struct nfsd3_diropres *resp)
{
__be32 nfserr;
dprintk("nfsd: LOOKUP(3) %s %.*s\n",
SVCFH_fmt(&argp->fh),
argp->len,
argp->name);
fh_copy(&resp->dirfh, &argp->fh);
fh_init(&resp->fh, NFS3_FHSIZE);
nfserr = nfsd_lookup(rqstp, &resp->dirfh,
argp->name,
argp->len,
&resp->fh);
RETURN_STATUS(nfserr);
}
/*
* Check file access
*/
static __be32
nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
struct nfsd3_accessres *resp)
{
__be32 nfserr;
dprintk("nfsd: ACCESS(3) %s 0x%x\n",
SVCFH_fmt(&argp->fh),
argp->access);
fh_copy(&resp->fh, &argp->fh);
resp->access = argp->access;
nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
RETURN_STATUS(nfserr);
}
/*
* Read a symlink.
*/
static __be32
nfsd3_proc_readlink(struct svc_rqst *rqstp, struct nfsd3_readlinkargs *argp,
struct nfsd3_readlinkres *resp)
{
__be32 nfserr;
dprintk("nfsd: READLINK(3) %s\n", SVCFH_fmt(&argp->fh));
/* Read the symlink. */
fh_copy(&resp->fh, &argp->fh);
resp->len = NFS3_MAXPATHLEN;
nfserr = nfsd_readlink(rqstp, &resp->fh, argp->buffer, &resp->len);
RETURN_STATUS(nfserr);
}
/*
* Read a portion of a file.
*/
static __be32
nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp,
struct nfsd3_readres *resp)
{
__be32 nfserr;
u32 max_blocksize = svc_max_payload(rqstp);
dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n",
SVCFH_fmt(&argp->fh),
(unsigned long) argp->count,
(unsigned long long) argp->offset);
/* Obtain buffer pointer for payload.
* 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
* + 1 (xdr opaque byte count) = 26
*/
resp->count = min(argp->count, max_blocksize);
svc_reserve_auth(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4);
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_read(rqstp, &resp->fh,
argp->offset,
rqstp->rq_vec, argp->vlen,
&resp->count);
if (nfserr == 0) {
struct inode *inode = resp->fh.fh_dentry->d_inode;
resp->eof = (argp->offset + resp->count) >= inode->i_size;
}
RETURN_STATUS(nfserr);
}
/*
* Write data to a file
*/
static __be32
nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
struct nfsd3_writeres *resp)
{
__be32 nfserr;
unsigned long cnt = argp->len;
dprintk("nfsd: WRITE(3) %s %d bytes at %Lu%s\n",
SVCFH_fmt(&argp->fh),
argp->len,
(unsigned long long) argp->offset,
argp->stable? " stable" : "");
fh_copy(&resp->fh, &argp->fh);
resp->committed = argp->stable;
nfserr = nfsd_write(rqstp, &resp->fh, NULL,
argp->offset,
rqstp->rq_vec, argp->vlen,
&cnt,
&resp->committed);
resp->count = cnt;
RETURN_STATUS(nfserr);
}
/*
* With NFSv3, CREATE processing is a lot easier than with NFSv2.
* At least in theory; we'll see how it fares in practice when the
* first reports about SunOS compatibility problems start to pour in...
*/
static __be32
nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
struct nfsd3_diropres *resp)
{
svc_fh *dirfhp, *newfhp = NULL;
struct iattr *attr;
__be32 nfserr;
dprintk("nfsd: CREATE(3) %s %.*s\n",
SVCFH_fmt(&argp->fh),
argp->len,
argp->name);
dirfhp = fh_copy(&resp->dirfh, &argp->fh);
newfhp = fh_init(&resp->fh, NFS3_FHSIZE);
attr = &argp->attrs;
/* 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 */
nfserr = do_nfsd_create(rqstp, dirfhp, argp->name, argp->len,
attr, newfhp,
argp->createmode, (u32 *)argp->verf, NULL, NULL);
RETURN_STATUS(nfserr);
}
/*
* Make directory. This operation is not idempotent.
*/
static __be32
nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
struct nfsd3_diropres *resp)
{
__be32 nfserr;
dprintk("nfsd: MKDIR(3) %s %.*s\n",
SVCFH_fmt(&argp->fh),
argp->len,
argp->name);
argp->attrs.ia_valid &= ~ATTR_SIZE;
fh_copy(&resp->dirfh, &argp->fh);
fh_init(&resp->fh, NFS3_FHSIZE);
nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&argp->attrs, S_IFDIR, 0, &resp->fh);
fh_unlock(&resp->dirfh);
RETURN_STATUS(nfserr);
}
static __be32
nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
struct nfsd3_diropres *resp)
{
__be32 nfserr;
dprintk("nfsd: SYMLINK(3) %s %.*s -> %.*s\n",
SVCFH_fmt(&argp->ffh),
argp->flen, argp->fname,
argp->tlen, argp->tname);
fh_copy(&resp->dirfh, &argp->ffh);
fh_init(&resp->fh, NFS3_FHSIZE);
nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen,
argp->tname, &resp->fh);
RETURN_STATUS(nfserr);
}
/*
* Make socket/fifo/device.
*/
static __be32
nfsd3_proc_mknod(struct svc_rqst *rqstp, struct nfsd3_mknodargs *argp,
struct nfsd3_diropres *resp)
{
__be32 nfserr;
int type;
dev_t rdev = 0;
dprintk("nfsd: MKNOD(3) %s %.*s\n",
SVCFH_fmt(&argp->fh),
argp->len,
argp->name);
fh_copy(&resp->dirfh, &argp->fh);
fh_init(&resp->fh, NFS3_FHSIZE);
if (argp->ftype == 0 || argp->ftype >= NF3BAD)
RETURN_STATUS(nfserr_inval);
if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) {
rdev = MKDEV(argp->major, argp->minor);
if (MAJOR(rdev) != argp->major ||
MINOR(rdev) != argp->minor)
RETURN_STATUS(nfserr_inval);
} else
if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO)
RETURN_STATUS(nfserr_inval);
type = nfs3_ftypes[argp->ftype];
nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&argp->attrs, type, rdev, &resp->fh);
fh_unlock(&resp->dirfh);
RETURN_STATUS(nfserr);
}
/*
* Remove file/fifo/socket etc.
*/
static __be32
nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
struct nfsd3_attrstat *resp)
{
__be32 nfserr;
dprintk("nfsd: REMOVE(3) %s %.*s\n",
SVCFH_fmt(&argp->fh),
argp->len,
argp->name);
/* Unlink. -S_IFDIR means file must not be a directory */
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
fh_unlock(&resp->fh);
RETURN_STATUS(nfserr);
}
/*
* Remove a directory
*/
static __be32
nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
struct nfsd3_attrstat *resp)
{
__be32 nfserr;
dprintk("nfsd: RMDIR(3) %s %.*s\n",
SVCFH_fmt(&argp->fh),
argp->len,
argp->name);
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len);
fh_unlock(&resp->fh);
RETURN_STATUS(nfserr);
}
static __be32
nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
struct nfsd3_renameres *resp)
{
__be32 nfserr;
dprintk("nfsd: RENAME(3) %s %.*s ->\n",
SVCFH_fmt(&argp->ffh),
argp->flen,
argp->fname);
dprintk("nfsd: -> %s %.*s\n",
SVCFH_fmt(&argp->tfh),
argp->tlen,
argp->tname);
fh_copy(&resp->ffh, &argp->ffh);
fh_copy(&resp->tfh, &argp->tfh);
nfserr = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen,
&resp->tfh, argp->tname, argp->tlen);
RETURN_STATUS(nfserr);
}
static __be32
nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
struct nfsd3_linkres *resp)
{
__be32 nfserr;
dprintk("nfsd: LINK(3) %s ->\n",
SVCFH_fmt(&argp->ffh));
dprintk("nfsd: -> %s %.*s\n",
SVCFH_fmt(&argp->tfh),
argp->tlen,
argp->tname);
fh_copy(&resp->fh, &argp->ffh);
fh_copy(&resp->tfh, &argp->tfh);
nfserr = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen,
&resp->fh);
RETURN_STATUS(nfserr);
}
/*
* Read a portion of a directory.
*/
static __be32
nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
struct nfsd3_readdirres *resp)
{
__be32 nfserr;
int count;
dprintk("nfsd: READDIR(3) %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh),
argp->count, (u32) argp->cookie);
/* 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);
resp->buflen = count;
resp->common.err = nfs_ok;
resp->buffer = argp->buffer;
resp->rqstp = rqstp;
nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie,
&resp->common, nfs3svc_encode_entry);
memcpy(resp->verf, argp->verf, 8);
resp->count = resp->buffer - argp->buffer;
if (resp->offset)
xdr_encode_hyper(resp->offset, argp->cookie);
RETURN_STATUS(nfserr);
}
/*
* Read a portion of a directory, including file handles and attrs.
* For now, we choose to ignore the dircount parameter.
*/
static __be32
nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
struct nfsd3_readdirres *resp)
{
__be32 nfserr;
int count = 0;
loff_t offset;
struct page **p;
caddr_t page_addr = NULL;
dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh),
argp->count, (u32) argp->cookie);
/* 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);
resp->common.err = nfs_ok;
resp->buffer = argp->buffer;
resp->buflen = resp->count;
resp->rqstp = rqstp;
offset = argp->cookie;
nfserr = fh_verify(rqstp, &resp->fh, S_IFDIR, NFSD_MAY_NOP);
if (nfserr)
RETURN_STATUS(nfserr);
if (resp->fh.fh_export->ex_flags & NFSEXP_NOREADDIRPLUS)
RETURN_STATUS(nfserr_notsupp);
nfserr = nfsd_readdir(rqstp, &resp->fh,
&offset,
&resp->common,
nfs3svc_encode_entry_plus);
memcpy(resp->verf, argp->verf, 8);
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) {
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);
}
}
RETURN_STATUS(nfserr);
}
/*
* Get file system stats
*/
static __be32
nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
struct nfsd3_fsstatres *resp)
{
__be32 nfserr;
dprintk("nfsd: FSSTAT(3) %s\n",
SVCFH_fmt(&argp->fh));
nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0);
fh_put(&argp->fh);
RETURN_STATUS(nfserr);
}
/*
* Get file system info
*/
static __be32
nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
struct nfsd3_fsinfores *resp)
{
__be32 nfserr;
u32 max_blocksize = svc_max_payload(rqstp);
dprintk("nfsd: FSINFO(3) %s\n",
SVCFH_fmt(&argp->fh));
resp->f_rtmax = max_blocksize;
resp->f_rtpref = max_blocksize;
resp->f_rtmult = PAGE_SIZE;
resp->f_wtmax = max_blocksize;
resp->f_wtpref = max_blocksize;
resp->f_wtmult = PAGE_SIZE;
resp->f_dtpref = PAGE_SIZE;
resp->f_maxfilesize = ~(u32) 0;
resp->f_properties = NFS3_FSF_DEFAULT;
nfserr = fh_verify(rqstp, &argp->fh, 0,
NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
/* Check special features of the file system. May request
* different read/write sizes for file systems known to have
* problems with large blocks */
if (nfserr == 0) {
struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
/* Note that we don't care for remote fs's here */
if (sb->s_magic == MSDOS_SUPER_MAGIC) {
resp->f_properties = NFS3_FSF_BILLYBOY;
}
resp->f_maxfilesize = sb->s_maxbytes;
}
fh_put(&argp->fh);
RETURN_STATUS(nfserr);
}
/*
* Get pathconf info for the specified file
*/
static __be32
nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
struct nfsd3_pathconfres *resp)
{
__be32 nfserr;
dprintk("nfsd: PATHCONF(3) %s\n",
SVCFH_fmt(&argp->fh));
/* Set default pathconf */
resp->p_link_max = 255; /* at least */
resp->p_name_max = 255; /* at least */
resp->p_no_trunc = 0;
resp->p_chown_restricted = 1;
resp->p_case_insensitive = 0;
resp->p_case_preserving = 1;
nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP);
if (nfserr == 0) {
struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
/* Note that we don't care for remote fs's here */
switch (sb->s_magic) {
case EXT2_SUPER_MAGIC:
resp->p_link_max = EXT2_LINK_MAX;
resp->p_name_max = EXT2_NAME_LEN;
break;
case MSDOS_SUPER_MAGIC:
resp->p_case_insensitive = 1;
resp->p_case_preserving = 0;
break;
}
}
fh_put(&argp->fh);
RETURN_STATUS(nfserr);
}
/*
* Commit a file (range) to stable storage.
*/
static __be32
nfsd3_proc_commit(struct svc_rqst * rqstp, struct nfsd3_commitargs *argp,
struct nfsd3_commitres *resp)
{
__be32 nfserr;
dprintk("nfsd: COMMIT(3) %s %u@%Lu\n",
SVCFH_fmt(&argp->fh),
argp->count,
(unsigned long long) argp->offset);
if (argp->offset > NFS_OFFSET_MAX)
RETURN_STATUS(nfserr_inval);
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count);
RETURN_STATUS(nfserr);
}
/*
* NFSv3 Server procedures.
* 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_wccstatres nfs3svc_encode_wccstat
#define nfsd3_mkdirargs nfsd3_createargs
#define nfsd3_readdirplusargs nfsd3_readdirargs
#define nfsd3_fhandleargs nfsd_fhandle
#define nfsd3_fhandleres nfsd3_attrstat
#define nfsd3_attrstatres nfsd3_attrstat
#define nfsd3_wccstatres nfsd3_attrstat
#define nfsd3_createres nfsd3_diropres
#define nfsd3_voidres nfsd3_voidargs
struct nfsd3_voidargs { int dummy; };
#define PROC(name, argt, rest, relt, cache, respsize) \
{ (svc_procfunc) nfsd3_proc_##name, \
(kxdrproc_t) nfs3svc_decode_##argt##args, \
(kxdrproc_t) nfs3svc_encode_##rest##res, \
(kxdrproc_t) nfs3svc_release_##relt, \
sizeof(struct nfsd3_##argt##args), \
sizeof(struct nfsd3_##rest##res), \
0, \
cache, \
respsize, \
}
#define ST 1 /* status*/
#define FH 17 /* filehandle with length */
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
#define WC (7+pAT) /* WCC attributes */
static struct svc_procedure nfsd_procedures3[22] = {
[NFS3PROC_NULL] = {
.pc_func = (svc_procfunc) nfsd3_proc_null,
.pc_encode = (kxdrproc_t) nfs3svc_encode_voidres,
.pc_argsize = sizeof(struct nfsd3_voidargs),
.pc_ressize = sizeof(struct nfsd3_voidres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST,
},
[NFS3PROC_GETATTR] = {
.pc_func = (svc_procfunc) nfsd3_proc_getattr,
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_attrstatres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_ressize = sizeof(struct nfsd3_attrstatres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT,
},
[NFS3PROC_SETATTR] = {
.pc_func = (svc_procfunc) nfsd3_proc_setattr,
.pc_decode = (kxdrproc_t) nfs3svc_decode_sattrargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_wccstatres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_sattrargs),
.pc_ressize = sizeof(struct nfsd3_wccstatres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC,
},
[NFS3PROC_LOOKUP] = {
.pc_func = (svc_procfunc) nfsd3_proc_lookup,
.pc_decode = (kxdrproc_t) nfs3svc_decode_diropargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_diropres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_diropargs),
.pc_ressize = sizeof(struct nfsd3_diropres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+FH+pAT+pAT,
},
[NFS3PROC_ACCESS] = {
.pc_func = (svc_procfunc) nfsd3_proc_access,
.pc_decode = (kxdrproc_t) nfs3svc_decode_accessargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_accessres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_accessargs),
.pc_ressize = sizeof(struct nfsd3_accessres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+1,
},
[NFS3PROC_READLINK] = {
.pc_func = (svc_procfunc) nfsd3_proc_readlink,
.pc_decode = (kxdrproc_t) nfs3svc_decode_readlinkargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_readlinkres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_readlinkargs),
.pc_ressize = sizeof(struct nfsd3_readlinkres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+1+NFS3_MAXPATHLEN/4,
},
[NFS3PROC_READ] = {
.pc_func = (svc_procfunc) nfsd3_proc_read,
.pc_decode = (kxdrproc_t) nfs3svc_decode_readargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_readres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_readargs),
.pc_ressize = sizeof(struct nfsd3_readres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+4+NFSSVC_MAXBLKSIZE/4,
},
[NFS3PROC_WRITE] = {
.pc_func = (svc_procfunc) nfsd3_proc_write,
.pc_decode = (kxdrproc_t) nfs3svc_decode_writeargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_writeres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_writeargs),
.pc_ressize = sizeof(struct nfsd3_writeres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC+4,
},
[NFS3PROC_CREATE] = {
.pc_func = (svc_procfunc) nfsd3_proc_create,
.pc_decode = (kxdrproc_t) nfs3svc_decode_createargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_createargs),
.pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC,
},
[NFS3PROC_MKDIR] = {
.pc_func = (svc_procfunc) nfsd3_proc_mkdir,
.pc_decode = (kxdrproc_t) nfs3svc_decode_mkdirargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_mkdirargs),
.pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC,
},
[NFS3PROC_SYMLINK] = {
.pc_func = (svc_procfunc) nfsd3_proc_symlink,
.pc_decode = (kxdrproc_t) nfs3svc_decode_symlinkargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_symlinkargs),
.pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC,
},
[NFS3PROC_MKNOD] = {
.pc_func = (svc_procfunc) nfsd3_proc_mknod,
.pc_decode = (kxdrproc_t) nfs3svc_decode_mknodargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_mknodargs),
.pc_ressize = sizeof(struct nfsd3_createres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+(1+FH+pAT)+WC,
},
[NFS3PROC_REMOVE] = {
.pc_func = (svc_procfunc) nfsd3_proc_remove,
.pc_decode = (kxdrproc_t) nfs3svc_decode_diropargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_wccstatres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_diropargs),
.pc_ressize = sizeof(struct nfsd3_wccstatres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC,
},
[NFS3PROC_RMDIR] = {
.pc_func = (svc_procfunc) nfsd3_proc_rmdir,
.pc_decode = (kxdrproc_t) nfs3svc_decode_diropargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_wccstatres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_diropargs),
.pc_ressize = sizeof(struct nfsd3_wccstatres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC,
},
[NFS3PROC_RENAME] = {
.pc_func = (svc_procfunc) nfsd3_proc_rename,
.pc_decode = (kxdrproc_t) nfs3svc_decode_renameargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_renameres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_renameargs),
.pc_ressize = sizeof(struct nfsd3_renameres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+WC+WC,
},
[NFS3PROC_LINK] = {
.pc_func = (svc_procfunc) nfsd3_proc_link,
.pc_decode = (kxdrproc_t) nfs3svc_decode_linkargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_linkres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
.pc_argsize = sizeof(struct nfsd3_linkargs),
.pc_ressize = sizeof(struct nfsd3_linkres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+pAT+WC,
},
[NFS3PROC_READDIR] = {
.pc_func = (svc_procfunc) nfsd3_proc_readdir,
.pc_decode = (kxdrproc_t) nfs3svc_decode_readdirargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_readdirres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_readdirargs),
.pc_ressize = sizeof(struct nfsd3_readdirres),
.pc_cachetype = RC_NOCACHE,
},
[NFS3PROC_READDIRPLUS] = {
.pc_func = (svc_procfunc) nfsd3_proc_readdirplus,
.pc_decode = (kxdrproc_t) nfs3svc_decode_readdirplusargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_readdirres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_readdirplusargs),
.pc_ressize = sizeof(struct nfsd3_readdirres),
.pc_cachetype = RC_NOCACHE,
},
[NFS3PROC_FSSTAT] = {
.pc_func = (svc_procfunc) nfsd3_proc_fsstat,
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_fsstatres,
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_ressize = sizeof(struct nfsd3_fsstatres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+2*6+1,
},
[NFS3PROC_FSINFO] = {
.pc_func = (svc_procfunc) nfsd3_proc_fsinfo,
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_fsinfores,
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_ressize = sizeof(struct nfsd3_fsinfores),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+12,
},
[NFS3PROC_PATHCONF] = {
.pc_func = (svc_procfunc) nfsd3_proc_pathconf,
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_pathconfres,
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
.pc_ressize = sizeof(struct nfsd3_pathconfres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+pAT+6,
},
[NFS3PROC_COMMIT] = {
.pc_func = (svc_procfunc) nfsd3_proc_commit,
.pc_decode = (kxdrproc_t) nfs3svc_decode_commitargs,
.pc_encode = (kxdrproc_t) nfs3svc_encode_commitres,
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
.pc_argsize = sizeof(struct nfsd3_commitargs),
.pc_ressize = sizeof(struct nfsd3_commitres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+WC+2,
},
};
struct svc_version nfsd_version3 = {
.vs_vers = 3,
.vs_nproc = 22,
.vs_proc = nfsd_procedures3,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
};

1114
fs/nfsd/nfs3xdr.c Normal file

File diff suppressed because it is too large Load diff

944
fs/nfsd/nfs4acl.c Normal file
View file

@ -0,0 +1,944 @@
/*
* Common NFSv4 ACL handling code.
*
* Copyright (c) 2002, 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Marius Aamodt Eriksen <marius@umich.edu>
* Jeff Sedlak <jsedlak@umich.edu>
* J. Bruce Fields <bfields@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/slab.h>
#include <linux/nfs_fs.h>
#include "nfsfh.h"
#include "nfsd.h"
#include "acl.h"
#include "vfs.h"
#define NFS4_ACL_TYPE_DEFAULT 0x01
#define NFS4_ACL_DIR 0x02
#define NFS4_ACL_OWNER 0x04
/* mode bit translations: */
#define NFS4_READ_MODE (NFS4_ACE_READ_DATA)
#define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_APPEND_DATA)
#define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE
#define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE)
#define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)
/* We don't support these bits; insist they be neither allowed nor denied */
#define NFS4_MASK_UNSUPP (NFS4_ACE_DELETE | NFS4_ACE_WRITE_OWNER \
| NFS4_ACE_READ_NAMED_ATTRS | NFS4_ACE_WRITE_NAMED_ATTRS)
/* flags used to simulate posix default ACLs */
#define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \
| NFS4_ACE_DIRECTORY_INHERIT_ACE)
#define NFS4_SUPPORTED_FLAGS (NFS4_INHERITANCE_FLAGS \
| NFS4_ACE_INHERIT_ONLY_ACE \
| NFS4_ACE_IDENTIFIER_GROUP)
#define MASK_EQUAL(mask1, mask2) \
( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) )
static u32
mask_from_posix(unsigned short perm, unsigned int flags)
{
int mask = NFS4_ANYONE_MODE;
if (flags & NFS4_ACL_OWNER)
mask |= NFS4_OWNER_MODE;
if (perm & ACL_READ)
mask |= NFS4_READ_MODE;
if (perm & ACL_WRITE)
mask |= NFS4_WRITE_MODE;
if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
mask |= NFS4_ACE_DELETE_CHILD;
if (perm & ACL_EXECUTE)
mask |= NFS4_EXECUTE_MODE;
return mask;
}
static u32
deny_mask_from_posix(unsigned short perm, u32 flags)
{
u32 mask = 0;
if (perm & ACL_READ)
mask |= NFS4_READ_MODE;
if (perm & ACL_WRITE)
mask |= NFS4_WRITE_MODE;
if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
mask |= NFS4_ACE_DELETE_CHILD;
if (perm & ACL_EXECUTE)
mask |= NFS4_EXECUTE_MODE;
return mask;
}
/* XXX: modify functions to return NFS errors; they're only ever
* used by nfs code, after all.... */
/* We only map from NFSv4 to POSIX ACLs when setting ACLs, when we err on the
* side of being more restrictive, so the mode bit mapping below is
* pessimistic. An optimistic version would be needed to handle DENY's,
* but we espect to coalesce all ALLOWs and DENYs before mapping to mode
* bits. */
static void
low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
{
u32 write_mode = NFS4_WRITE_MODE;
if (flags & NFS4_ACL_DIR)
write_mode |= NFS4_ACE_DELETE_CHILD;
*mode = 0;
if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
*mode |= ACL_READ;
if ((perm & write_mode) == write_mode)
*mode |= ACL_WRITE;
if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
*mode |= ACL_EXECUTE;
}
struct ace_container {
struct nfs4_ace *ace;
struct list_head ace_l;
};
static short ace2type(struct nfs4_ace *);
static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *,
unsigned int);
int
nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
struct nfs4_acl **acl)
{
struct inode *inode = dentry->d_inode;
int error = 0;
struct posix_acl *pacl = NULL, *dpacl = NULL;
unsigned int flags = 0;
int size = 0;
pacl = get_acl(inode, ACL_TYPE_ACCESS);
if (!pacl)
pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
if (IS_ERR(pacl))
return PTR_ERR(pacl);
/* allocate for worst case: one (deny, allow) pair each: */
size += 2 * pacl->a_count;
if (S_ISDIR(inode->i_mode)) {
flags = NFS4_ACL_DIR;
dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(dpacl)) {
error = PTR_ERR(dpacl);
goto rel_pacl;
}
if (dpacl)
size += 2 * dpacl->a_count;
}
*acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL);
if (*acl == NULL) {
error = -ENOMEM;
goto out;
}
(*acl)->naces = 0;
_posix_to_nfsv4_one(pacl, *acl, flags & ~NFS4_ACL_TYPE_DEFAULT);
if (dpacl)
_posix_to_nfsv4_one(dpacl, *acl, flags | NFS4_ACL_TYPE_DEFAULT);
out:
posix_acl_release(dpacl);
rel_pacl:
posix_acl_release(pacl);
return error;
}
struct posix_acl_summary {
unsigned short owner;
unsigned short users;
unsigned short group;
unsigned short groups;
unsigned short other;
unsigned short mask;
};
static void
summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)
{
struct posix_acl_entry *pa, *pe;
/*
* Only pas.users and pas.groups need initialization; previous
* posix_acl_valid() calls ensure that the other fields will be
* initialized in the following loop. But, just to placate gcc:
*/
memset(pas, 0, sizeof(*pas));
pas->mask = 07;
pe = acl->a_entries + acl->a_count;
FOREACH_ACL_ENTRY(pa, acl, pe) {
switch (pa->e_tag) {
case ACL_USER_OBJ:
pas->owner = pa->e_perm;
break;
case ACL_GROUP_OBJ:
pas->group = pa->e_perm;
break;
case ACL_USER:
pas->users |= pa->e_perm;
break;
case ACL_GROUP:
pas->groups |= pa->e_perm;
break;
case ACL_OTHER:
pas->other = pa->e_perm;
break;
case ACL_MASK:
pas->mask = pa->e_perm;
break;
}
}
/* We'll only care about effective permissions: */
pas->users &= pas->mask;
pas->group &= pas->mask;
pas->groups &= pas->mask;
}
/* We assume the acl has been verified with posix_acl_valid. */
static void
_posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
unsigned int flags)
{
struct posix_acl_entry *pa, *group_owner_entry;
struct nfs4_ace *ace;
struct posix_acl_summary pas;
unsigned short deny;
int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ?
NFS4_INHERITANCE_FLAGS | NFS4_ACE_INHERIT_ONLY_ACE : 0);
BUG_ON(pacl->a_count < 3);
summarize_posix_acl(pacl, &pas);
pa = pacl->a_entries;
ace = acl->aces + acl->naces;
/* We could deny everything not granted by the owner: */
deny = ~pas.owner;
/*
* but it is equivalent (and simpler) to deny only what is not
* granted by later entries:
*/
deny &= pas.users | pas.group | pas.groups | pas.other;
if (deny) {
ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
ace->flag = eflag;
ace->access_mask = deny_mask_from_posix(deny, flags);
ace->whotype = NFS4_ACL_WHO_OWNER;
ace++;
acl->naces++;
}
ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
ace->flag = eflag;
ace->access_mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER);
ace->whotype = NFS4_ACL_WHO_OWNER;
ace++;
acl->naces++;
pa++;
while (pa->e_tag == ACL_USER) {
deny = ~(pa->e_perm & pas.mask);
deny &= pas.groups | pas.group | pas.other;
if (deny) {
ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
ace->flag = eflag;
ace->access_mask = deny_mask_from_posix(deny, flags);
ace->whotype = NFS4_ACL_WHO_NAMED;
ace->who_uid = pa->e_uid;
ace++;
acl->naces++;
}
ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
ace->flag = eflag;
ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
flags);
ace->whotype = NFS4_ACL_WHO_NAMED;
ace->who_uid = pa->e_uid;
ace++;
acl->naces++;
pa++;
}
/* In the case of groups, we apply allow ACEs first, then deny ACEs,
* since a user can be in more than one group. */
/* allow ACEs */
group_owner_entry = pa;
ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
ace->flag = eflag;
ace->access_mask = mask_from_posix(pas.group, flags);
ace->whotype = NFS4_ACL_WHO_GROUP;
ace++;
acl->naces++;
pa++;
while (pa->e_tag == ACL_GROUP) {
ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
flags);
ace->whotype = NFS4_ACL_WHO_NAMED;
ace->who_gid = pa->e_gid;
ace++;
acl->naces++;
pa++;
}
/* deny ACEs */
pa = group_owner_entry;
deny = ~pas.group & pas.other;
if (deny) {
ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
ace->flag = eflag;
ace->access_mask = deny_mask_from_posix(deny, flags);
ace->whotype = NFS4_ACL_WHO_GROUP;
ace++;
acl->naces++;
}
pa++;
while (pa->e_tag == ACL_GROUP) {
deny = ~(pa->e_perm & pas.mask);
deny &= pas.other;
if (deny) {
ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
ace->access_mask = deny_mask_from_posix(deny, flags);
ace->whotype = NFS4_ACL_WHO_NAMED;
ace->who_gid = pa->e_gid;
ace++;
acl->naces++;
}
pa++;
}
if (pa->e_tag == ACL_MASK)
pa++;
ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
ace->flag = eflag;
ace->access_mask = mask_from_posix(pa->e_perm, flags);
ace->whotype = NFS4_ACL_WHO_EVERYONE;
acl->naces++;
}
static bool
pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2)
{
if (pace1->e_tag != pace2->e_tag)
return pace1->e_tag > pace2->e_tag;
if (pace1->e_tag == ACL_USER)
return uid_gt(pace1->e_uid, pace2->e_uid);
if (pace1->e_tag == ACL_GROUP)
return gid_gt(pace1->e_gid, pace2->e_gid);
return false;
}
static void
sort_pacl_range(struct posix_acl *pacl, int start, int end) {
int sorted = 0, i;
struct posix_acl_entry tmp;
/* We just do a bubble sort; easy to do in place, and we're not
* expecting acl's to be long enough to justify anything more. */
while (!sorted) {
sorted = 1;
for (i = start; i < end; i++) {
if (pace_gt(&pacl->a_entries[i],
&pacl->a_entries[i+1])) {
sorted = 0;
tmp = pacl->a_entries[i];
pacl->a_entries[i] = pacl->a_entries[i+1];
pacl->a_entries[i+1] = tmp;
}
}
}
}
static void
sort_pacl(struct posix_acl *pacl)
{
/* posix_acl_valid requires that users and groups be in order
* by uid/gid. */
int i, j;
/* no users or groups */
if (!pacl || pacl->a_count <= 4)
return;
i = 1;
while (pacl->a_entries[i].e_tag == ACL_USER)
i++;
sort_pacl_range(pacl, 1, i-1);
BUG_ON(pacl->a_entries[i].e_tag != ACL_GROUP_OBJ);
j = ++i;
while (pacl->a_entries[j].e_tag == ACL_GROUP)
j++;
sort_pacl_range(pacl, i, j-1);
return;
}
/*
* While processing the NFSv4 ACE, this maintains bitmasks representing
* which permission bits have been allowed and which denied to a given
* entity: */
struct posix_ace_state {
u32 allow;
u32 deny;
};
struct posix_user_ace_state {
union {
kuid_t uid;
kgid_t gid;
};
struct posix_ace_state perms;
};
struct posix_ace_state_array {
int n;
struct posix_user_ace_state aces[];
};
/*
* While processing the NFSv4 ACE, this maintains the partial permissions
* calculated so far: */
struct posix_acl_state {
int empty;
struct posix_ace_state owner;
struct posix_ace_state group;
struct posix_ace_state other;
struct posix_ace_state everyone;
struct posix_ace_state mask; /* Deny unused in this case */
struct posix_ace_state_array *users;
struct posix_ace_state_array *groups;
};
static int
init_state(struct posix_acl_state *state, int cnt)
{
int alloc;
memset(state, 0, sizeof(struct posix_acl_state));
state->empty = 1;
/*
* In the worst case, each individual acl could be for a distinct
* named user or group, but we don't no which, so we allocate
* enough space for either:
*/
alloc = sizeof(struct posix_ace_state_array)
+ cnt*sizeof(struct posix_user_ace_state);
state->users = kzalloc(alloc, GFP_KERNEL);
if (!state->users)
return -ENOMEM;
state->groups = kzalloc(alloc, GFP_KERNEL);
if (!state->groups) {
kfree(state->users);
return -ENOMEM;
}
return 0;
}
static void
free_state(struct posix_acl_state *state) {
kfree(state->users);
kfree(state->groups);
}
static inline void add_to_mask(struct posix_acl_state *state, struct posix_ace_state *astate)
{
state->mask.allow |= astate->allow;
}
/*
* Certain bits (SYNCHRONIZE, DELETE, WRITE_OWNER, READ/WRITE_NAMED_ATTRS,
* READ_ATTRIBUTES, READ_ACL) are currently unenforceable and don't translate
* to traditional read/write/execute permissions.
*
* It's problematic to reject acls that use certain mode bits, because it
* places the burden on users to learn the rules about which bits one
* particular server sets, without giving the user a lot of help--we return an
* error that could mean any number of different things. To make matters
* worse, the problematic bits might be introduced by some application that's
* automatically mapping from some other acl model.
*
* So wherever possible we accept anything, possibly erring on the side of
* denying more permissions than necessary.
*
* However we do reject *explicit* DENY's of a few bits representing
* permissions we could never deny:
*/
static inline int check_deny(u32 mask, int isowner)
{
if (mask & (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL))
return -EINVAL;
if (!isowner)
return 0;
if (mask & (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL))
return -EINVAL;
return 0;
}
static struct posix_acl *
posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
{
struct posix_acl_entry *pace;
struct posix_acl *pacl;
int nace;
int i, error = 0;
/*
* ACLs with no ACEs are treated differently in the inheritable
* and effective cases: when there are no inheritable ACEs,
* calls ->set_acl with a NULL ACL structure.
*/
if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT))
return NULL;
/*
* When there are no effective ACEs, the following will end
* up setting a 3-element effective posix ACL with all
* permissions zero.
*/
if (!state->users->n && !state->groups->n)
nace = 3;
else /* Note we also include a MASK ACE in this case: */
nace = 4 + state->users->n + state->groups->n;
pacl = posix_acl_alloc(nace, GFP_KERNEL);
if (!pacl)
return ERR_PTR(-ENOMEM);
pace = pacl->a_entries;
pace->e_tag = ACL_USER_OBJ;
error = check_deny(state->owner.deny, 1);
if (error)
goto out_err;
low_mode_from_nfs4(state->owner.allow, &pace->e_perm, flags);
for (i=0; i < state->users->n; i++) {
pace++;
pace->e_tag = ACL_USER;
error = check_deny(state->users->aces[i].perms.deny, 0);
if (error)
goto out_err;
low_mode_from_nfs4(state->users->aces[i].perms.allow,
&pace->e_perm, flags);
pace->e_uid = state->users->aces[i].uid;
add_to_mask(state, &state->users->aces[i].perms);
}
pace++;
pace->e_tag = ACL_GROUP_OBJ;
error = check_deny(state->group.deny, 0);
if (error)
goto out_err;
low_mode_from_nfs4(state->group.allow, &pace->e_perm, flags);
add_to_mask(state, &state->group);
for (i=0; i < state->groups->n; i++) {
pace++;
pace->e_tag = ACL_GROUP;
error = check_deny(state->groups->aces[i].perms.deny, 0);
if (error)
goto out_err;
low_mode_from_nfs4(state->groups->aces[i].perms.allow,
&pace->e_perm, flags);
pace->e_gid = state->groups->aces[i].gid;
add_to_mask(state, &state->groups->aces[i].perms);
}
if (state->users->n || state->groups->n) {
pace++;
pace->e_tag = ACL_MASK;
low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags);
}
pace++;
pace->e_tag = ACL_OTHER;
error = check_deny(state->other.deny, 0);
if (error)
goto out_err;
low_mode_from_nfs4(state->other.allow, &pace->e_perm, flags);
return pacl;
out_err:
posix_acl_release(pacl);
return ERR_PTR(error);
}
static inline void allow_bits(struct posix_ace_state *astate, u32 mask)
{
/* Allow all bits in the mask not already denied: */
astate->allow |= mask & ~astate->deny;
}
static inline void deny_bits(struct posix_ace_state *astate, u32 mask)
{
/* Deny all bits in the mask not already allowed: */
astate->deny |= mask & ~astate->allow;
}
static int find_uid(struct posix_acl_state *state, kuid_t uid)
{
struct posix_ace_state_array *a = state->users;
int i;
for (i = 0; i < a->n; i++)
if (uid_eq(a->aces[i].uid, uid))
return i;
/* Not found: */
a->n++;
a->aces[i].uid = uid;
a->aces[i].perms.allow = state->everyone.allow;
a->aces[i].perms.deny = state->everyone.deny;
return i;
}
static int find_gid(struct posix_acl_state *state, kgid_t gid)
{
struct posix_ace_state_array *a = state->groups;
int i;
for (i = 0; i < a->n; i++)
if (gid_eq(a->aces[i].gid, gid))
return i;
/* Not found: */
a->n++;
a->aces[i].gid = gid;
a->aces[i].perms.allow = state->everyone.allow;
a->aces[i].perms.deny = state->everyone.deny;
return i;
}
static void deny_bits_array(struct posix_ace_state_array *a, u32 mask)
{
int i;
for (i=0; i < a->n; i++)
deny_bits(&a->aces[i].perms, mask);
}
static void allow_bits_array(struct posix_ace_state_array *a, u32 mask)
{
int i;
for (i=0; i < a->n; i++)
allow_bits(&a->aces[i].perms, mask);
}
static void process_one_v4_ace(struct posix_acl_state *state,
struct nfs4_ace *ace)
{
u32 mask = ace->access_mask;
int i;
state->empty = 0;
switch (ace2type(ace)) {
case ACL_USER_OBJ:
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->owner, mask);
} else {
deny_bits(&state->owner, mask);
}
break;
case ACL_USER:
i = find_uid(state, ace->who_uid);
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->users->aces[i].perms, mask);
} else {
deny_bits(&state->users->aces[i].perms, mask);
mask = state->users->aces[i].perms.deny;
deny_bits(&state->owner, mask);
}
break;
case ACL_GROUP_OBJ:
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->group, mask);
} else {
deny_bits(&state->group, mask);
mask = state->group.deny;
deny_bits(&state->owner, mask);
deny_bits(&state->everyone, mask);
deny_bits_array(state->users, mask);
deny_bits_array(state->groups, mask);
}
break;
case ACL_GROUP:
i = find_gid(state, ace->who_gid);
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->groups->aces[i].perms, mask);
} else {
deny_bits(&state->groups->aces[i].perms, mask);
mask = state->groups->aces[i].perms.deny;
deny_bits(&state->owner, mask);
deny_bits(&state->group, mask);
deny_bits(&state->everyone, mask);
deny_bits_array(state->users, mask);
deny_bits_array(state->groups, mask);
}
break;
case ACL_OTHER:
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->owner, mask);
allow_bits(&state->group, mask);
allow_bits(&state->other, mask);
allow_bits(&state->everyone, mask);
allow_bits_array(state->users, mask);
allow_bits_array(state->groups, mask);
} else {
deny_bits(&state->owner, mask);
deny_bits(&state->group, mask);
deny_bits(&state->other, mask);
deny_bits(&state->everyone, mask);
deny_bits_array(state->users, mask);
deny_bits_array(state->groups, mask);
}
}
}
static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
struct posix_acl **pacl, struct posix_acl **dpacl,
unsigned int flags)
{
struct posix_acl_state effective_acl_state, default_acl_state;
struct nfs4_ace *ace;
int ret;
ret = init_state(&effective_acl_state, acl->naces);
if (ret)
return ret;
ret = init_state(&default_acl_state, acl->naces);
if (ret)
goto out_estate;
ret = -EINVAL;
for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
goto out_dstate;
if (ace->flag & ~NFS4_SUPPORTED_FLAGS)
goto out_dstate;
if ((ace->flag & NFS4_INHERITANCE_FLAGS) == 0) {
process_one_v4_ace(&effective_acl_state, ace);
continue;
}
if (!(flags & NFS4_ACL_DIR))
goto out_dstate;
/*
* Note that when only one of FILE_INHERIT or DIRECTORY_INHERIT
* is set, we're effectively turning on the other. That's OK,
* according to rfc 3530.
*/
process_one_v4_ace(&default_acl_state, ace);
if (!(ace->flag & NFS4_ACE_INHERIT_ONLY_ACE))
process_one_v4_ace(&effective_acl_state, ace);
}
*pacl = posix_state_to_acl(&effective_acl_state, flags);
if (IS_ERR(*pacl)) {
ret = PTR_ERR(*pacl);
*pacl = NULL;
goto out_dstate;
}
*dpacl = posix_state_to_acl(&default_acl_state,
flags | NFS4_ACL_TYPE_DEFAULT);
if (IS_ERR(*dpacl)) {
ret = PTR_ERR(*dpacl);
*dpacl = NULL;
posix_acl_release(*pacl);
*pacl = NULL;
goto out_dstate;
}
sort_pacl(*pacl);
sort_pacl(*dpacl);
ret = 0;
out_dstate:
free_state(&default_acl_state);
out_estate:
free_state(&effective_acl_state);
return ret;
}
__be32
nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfs4_acl *acl)
{
__be32 error;
int host_error;
struct dentry *dentry;
struct inode *inode;
struct posix_acl *pacl = NULL, *dpacl = NULL;
unsigned int flags = 0;
/* Get inode */
error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
if (error)
return error;
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
if (!inode->i_op->set_acl || !IS_POSIXACL(inode))
return nfserr_attrnotsupp;
if (S_ISDIR(inode->i_mode))
flags = NFS4_ACL_DIR;
host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
if (host_error == -EINVAL)
return nfserr_attrnotsupp;
if (host_error < 0)
goto out_nfserr;
host_error = inode->i_op->set_acl(inode, pacl, ACL_TYPE_ACCESS);
if (host_error < 0)
goto out_release;
if (S_ISDIR(inode->i_mode)) {
host_error = inode->i_op->set_acl(inode, dpacl,
ACL_TYPE_DEFAULT);
}
out_release:
posix_acl_release(pacl);
posix_acl_release(dpacl);
out_nfserr:
if (host_error == -EOPNOTSUPP)
return nfserr_attrnotsupp;
else
return nfserrno(host_error);
}
static short
ace2type(struct nfs4_ace *ace)
{
switch (ace->whotype) {
case NFS4_ACL_WHO_NAMED:
return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ?
ACL_GROUP : ACL_USER);
case NFS4_ACL_WHO_OWNER:
return ACL_USER_OBJ;
case NFS4_ACL_WHO_GROUP:
return ACL_GROUP_OBJ;
case NFS4_ACL_WHO_EVERYONE:
return ACL_OTHER;
}
BUG();
return -1;
}
/*
* return the size of the struct nfs4_acl required to represent an acl
* with @entries entries.
*/
int nfs4_acl_bytes(int entries)
{
return sizeof(struct nfs4_acl) + entries * sizeof(struct nfs4_ace);
}
static struct {
char *string;
int stringlen;
int type;
} s2t_map[] = {
{
.string = "OWNER@",
.stringlen = sizeof("OWNER@") - 1,
.type = NFS4_ACL_WHO_OWNER,
},
{
.string = "GROUP@",
.stringlen = sizeof("GROUP@") - 1,
.type = NFS4_ACL_WHO_GROUP,
},
{
.string = "EVERYONE@",
.stringlen = sizeof("EVERYONE@") - 1,
.type = NFS4_ACL_WHO_EVERYONE,
},
};
int
nfs4_acl_get_whotype(char *p, u32 len)
{
int i;
for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
if (s2t_map[i].stringlen == len &&
0 == memcmp(s2t_map[i].string, p, len))
return s2t_map[i].type;
}
return NFS4_ACL_WHO_NAMED;
}
__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who)
{
__be32 *p;
int i;
for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
if (s2t_map[i].type != who)
continue;
p = xdr_reserve_space(xdr, s2t_map[i].stringlen + 4);
if (!p)
return nfserr_resource;
p = xdr_encode_opaque(p, s2t_map[i].string,
s2t_map[i].stringlen);
return 0;
}
WARN_ON_ONCE(1);
return nfserr_serverfault;
}

1009
fs/nfsd/nfs4callback.c Normal file

File diff suppressed because it is too large Load diff

666
fs/nfsd/nfs4idmap.c Normal file
View file

@ -0,0 +1,666 @@
/*
* Mapping of UID/GIDs to name and vice versa.
*
* Copyright (c) 2002, 2003 The Regents of the University of
* Michigan. All rights reserved.
*
* Marius Aamodt Eriksen <marius@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/sunrpc/svc_xprt.h>
#include <net/net_namespace.h>
#include "idmap.h"
#include "nfsd.h"
#include "netns.h"
/*
* Turn off idmapping when using AUTH_SYS.
*/
static bool nfs4_disable_idmapping = true;
module_param(nfs4_disable_idmapping, bool, 0644);
MODULE_PARM_DESC(nfs4_disable_idmapping,
"Turn off server's NFSv4 idmapping when using 'sec=sys'");
/*
* Cache entry
*/
/*
* XXX we know that IDMAP_NAMESZ < PAGE_SIZE, but it's ugly to rely on
* that.
*/
#define IDMAP_TYPE_USER 0
#define IDMAP_TYPE_GROUP 1
struct ent {
struct cache_head h;
int type; /* User / Group */
u32 id;
char name[IDMAP_NAMESZ];
char authname[IDMAP_NAMESZ];
};
/* Common entry handling */
#define ENT_HASHBITS 8
#define ENT_HASHMAX (1 << ENT_HASHBITS)
static void
ent_init(struct cache_head *cnew, struct cache_head *citm)
{
struct ent *new = container_of(cnew, struct ent, h);
struct ent *itm = container_of(citm, struct ent, h);
new->id = itm->id;
new->type = itm->type;
strlcpy(new->name, itm->name, sizeof(new->name));
strlcpy(new->authname, itm->authname, sizeof(new->name));
}
static void
ent_put(struct kref *ref)
{
struct ent *map = container_of(ref, struct ent, h.ref);
kfree(map);
}
static struct cache_head *
ent_alloc(void)
{
struct ent *e = kmalloc(sizeof(*e), GFP_KERNEL);
if (e)
return &e->h;
else
return NULL;
}
/*
* ID -> Name cache
*/
static uint32_t
idtoname_hash(struct ent *ent)
{
uint32_t hash;
hash = hash_str(ent->authname, ENT_HASHBITS);
hash = hash_long(hash ^ ent->id, ENT_HASHBITS);
/* Flip LSB for user/group */
if (ent->type == IDMAP_TYPE_GROUP)
hash ^= 1;
return hash;
}
static void
idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
int *blen)
{
struct ent *ent = container_of(ch, struct ent, h);
char idstr[11];
qword_add(bpp, blen, ent->authname);
snprintf(idstr, sizeof(idstr), "%u", ent->id);
qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user");
qword_add(bpp, blen, idstr);
(*bpp)[-1] = '\n';
}
static int
idtoname_match(struct cache_head *ca, struct cache_head *cb)
{
struct ent *a = container_of(ca, struct ent, h);
struct ent *b = container_of(cb, struct ent, h);
return (a->id == b->id && a->type == b->type &&
strcmp(a->authname, b->authname) == 0);
}
static int
idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
{
struct ent *ent;
if (h == NULL) {
seq_puts(m, "#domain type id [name]\n");
return 0;
}
ent = container_of(h, struct ent, h);
seq_printf(m, "%s %s %u", ent->authname,
ent->type == IDMAP_TYPE_GROUP ? "group" : "user",
ent->id);
if (test_bit(CACHE_VALID, &h->flags))
seq_printf(m, " %s", ent->name);
seq_printf(m, "\n");
return 0;
}
static void
warn_no_idmapd(struct cache_detail *detail, int has_died)
{
printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n",
has_died ? "died" : "not been started");
}
static int idtoname_parse(struct cache_detail *, char *, int);
static struct ent *idtoname_lookup(struct cache_detail *, struct ent *);
static struct ent *idtoname_update(struct cache_detail *, struct ent *,
struct ent *);
static struct cache_detail idtoname_cache_template = {
.owner = THIS_MODULE,
.hash_size = ENT_HASHMAX,
.name = "nfs4.idtoname",
.cache_put = ent_put,
.cache_request = idtoname_request,
.cache_parse = idtoname_parse,
.cache_show = idtoname_show,
.warn_no_listener = warn_no_idmapd,
.match = idtoname_match,
.init = ent_init,
.update = ent_init,
.alloc = ent_alloc,
};
static int
idtoname_parse(struct cache_detail *cd, char *buf, int buflen)
{
struct ent ent, *res;
char *buf1, *bp;
int len;
int error = -EINVAL;
if (buf[buflen - 1] != '\n')
return (-EINVAL);
buf[buflen - 1]= '\0';
buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (buf1 == NULL)
return (-ENOMEM);
memset(&ent, 0, sizeof(ent));
/* Authentication name */
len = qword_get(&buf, buf1, PAGE_SIZE);
if (len <= 0 || len >= IDMAP_NAMESZ)
goto out;
memcpy(ent.authname, buf1, sizeof(ent.authname));
/* Type */
if (qword_get(&buf, buf1, PAGE_SIZE) <= 0)
goto out;
ent.type = strcmp(buf1, "user") == 0 ?
IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
/* ID */
if (qword_get(&buf, buf1, PAGE_SIZE) <= 0)
goto out;
ent.id = simple_strtoul(buf1, &bp, 10);
if (bp == buf1)
goto out;
/* expiry */
ent.h.expiry_time = get_expiry(&buf);
if (ent.h.expiry_time == 0)
goto out;
error = -ENOMEM;
res = idtoname_lookup(cd, &ent);
if (!res)
goto out;
/* Name */
error = -EINVAL;
len = qword_get(&buf, buf1, PAGE_SIZE);
if (len < 0 || len >= IDMAP_NAMESZ)
goto out;
if (len == 0)
set_bit(CACHE_NEGATIVE, &ent.h.flags);
else
memcpy(ent.name, buf1, sizeof(ent.name));
error = -ENOMEM;
res = idtoname_update(cd, &ent, res);
if (res == NULL)
goto out;
cache_put(&res->h, cd);
error = 0;
out:
kfree(buf1);
return error;
}
static struct ent *
idtoname_lookup(struct cache_detail *cd, struct ent *item)
{
struct cache_head *ch = sunrpc_cache_lookup(cd, &item->h,
idtoname_hash(item));
if (ch)
return container_of(ch, struct ent, h);
else
return NULL;
}
static struct ent *
idtoname_update(struct cache_detail *cd, struct ent *new, struct ent *old)
{
struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,
idtoname_hash(new));
if (ch)
return container_of(ch, struct ent, h);
else
return NULL;
}
/*
* Name -> ID cache
*/
static inline int
nametoid_hash(struct ent *ent)
{
return hash_str(ent->name, ENT_HASHBITS);
}
static void
nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
int *blen)
{
struct ent *ent = container_of(ch, struct ent, h);
qword_add(bpp, blen, ent->authname);
qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user");
qword_add(bpp, blen, ent->name);
(*bpp)[-1] = '\n';
}
static int
nametoid_match(struct cache_head *ca, struct cache_head *cb)
{
struct ent *a = container_of(ca, struct ent, h);
struct ent *b = container_of(cb, struct ent, h);
return (a->type == b->type && strcmp(a->name, b->name) == 0 &&
strcmp(a->authname, b->authname) == 0);
}
static int
nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
{
struct ent *ent;
if (h == NULL) {
seq_puts(m, "#domain type name [id]\n");
return 0;
}
ent = container_of(h, struct ent, h);
seq_printf(m, "%s %s %s", ent->authname,
ent->type == IDMAP_TYPE_GROUP ? "group" : "user",
ent->name);
if (test_bit(CACHE_VALID, &h->flags))
seq_printf(m, " %u", ent->id);
seq_printf(m, "\n");
return 0;
}
static struct ent *nametoid_lookup(struct cache_detail *, struct ent *);
static struct ent *nametoid_update(struct cache_detail *, struct ent *,
struct ent *);
static int nametoid_parse(struct cache_detail *, char *, int);
static struct cache_detail nametoid_cache_template = {
.owner = THIS_MODULE,
.hash_size = ENT_HASHMAX,
.name = "nfs4.nametoid",
.cache_put = ent_put,
.cache_request = nametoid_request,
.cache_parse = nametoid_parse,
.cache_show = nametoid_show,
.warn_no_listener = warn_no_idmapd,
.match = nametoid_match,
.init = ent_init,
.update = ent_init,
.alloc = ent_alloc,
};
static int
nametoid_parse(struct cache_detail *cd, char *buf, int buflen)
{
struct ent ent, *res;
char *buf1;
int len, error = -EINVAL;
if (buf[buflen - 1] != '\n')
return (-EINVAL);
buf[buflen - 1]= '\0';
buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (buf1 == NULL)
return (-ENOMEM);
memset(&ent, 0, sizeof(ent));
/* Authentication name */
len = qword_get(&buf, buf1, PAGE_SIZE);
if (len <= 0 || len >= IDMAP_NAMESZ)
goto out;
memcpy(ent.authname, buf1, sizeof(ent.authname));
/* Type */
if (qword_get(&buf, buf1, PAGE_SIZE) <= 0)
goto out;
ent.type = strcmp(buf1, "user") == 0 ?
IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
/* Name */
len = qword_get(&buf, buf1, PAGE_SIZE);
if (len <= 0 || len >= IDMAP_NAMESZ)
goto out;
memcpy(ent.name, buf1, sizeof(ent.name));
/* expiry */
ent.h.expiry_time = get_expiry(&buf);
if (ent.h.expiry_time == 0)
goto out;
/* ID */
error = get_int(&buf, &ent.id);
if (error == -EINVAL)
goto out;
if (error == -ENOENT)
set_bit(CACHE_NEGATIVE, &ent.h.flags);
error = -ENOMEM;
res = nametoid_lookup(cd, &ent);
if (res == NULL)
goto out;
res = nametoid_update(cd, &ent, res);
if (res == NULL)
goto out;
cache_put(&res->h, cd);
error = 0;
out:
kfree(buf1);
return (error);
}
static struct ent *
nametoid_lookup(struct cache_detail *cd, struct ent *item)
{
struct cache_head *ch = sunrpc_cache_lookup(cd, &item->h,
nametoid_hash(item));
if (ch)
return container_of(ch, struct ent, h);
else
return NULL;
}
static struct ent *
nametoid_update(struct cache_detail *cd, struct ent *new, struct ent *old)
{
struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,
nametoid_hash(new));
if (ch)
return container_of(ch, struct ent, h);
else
return NULL;
}
/*
* Exported API
*/
int
nfsd_idmap_init(struct net *net)
{
int rv;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nn->idtoname_cache = cache_create_net(&idtoname_cache_template, net);
if (IS_ERR(nn->idtoname_cache))
return PTR_ERR(nn->idtoname_cache);
rv = cache_register_net(nn->idtoname_cache, net);
if (rv)
goto destroy_idtoname_cache;
nn->nametoid_cache = cache_create_net(&nametoid_cache_template, net);
if (IS_ERR(nn->nametoid_cache)) {
rv = PTR_ERR(nn->nametoid_cache);
goto unregister_idtoname_cache;
}
rv = cache_register_net(nn->nametoid_cache, net);
if (rv)
goto destroy_nametoid_cache;
return 0;
destroy_nametoid_cache:
cache_destroy_net(nn->nametoid_cache, net);
unregister_idtoname_cache:
cache_unregister_net(nn->idtoname_cache, net);
destroy_idtoname_cache:
cache_destroy_net(nn->idtoname_cache, net);
return rv;
}
void
nfsd_idmap_shutdown(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
cache_unregister_net(nn->idtoname_cache, net);
cache_unregister_net(nn->nametoid_cache, net);
cache_destroy_net(nn->idtoname_cache, net);
cache_destroy_net(nn->nametoid_cache, net);
}
static int
idmap_lookup(struct svc_rqst *rqstp,
struct ent *(*lookup_fn)(struct cache_detail *, struct ent *),
struct ent *key, struct cache_detail *detail, struct ent **item)
{
int ret;
*item = lookup_fn(detail, key);
if (!*item)
return -ENOMEM;
retry:
ret = cache_check(detail, &(*item)->h, &rqstp->rq_chandle);
if (ret == -ETIMEDOUT) {
struct ent *prev_item = *item;
*item = lookup_fn(detail, key);
if (*item != prev_item)
goto retry;
cache_put(&(*item)->h, detail);
}
return ret;
}
static char *
rqst_authname(struct svc_rqst *rqstp)
{
struct auth_domain *clp;
clp = rqstp->rq_gssclient ? rqstp->rq_gssclient : rqstp->rq_client;
return clp->name;
}
static __be32
idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen,
u32 *id)
{
struct ent *item, key = {
.type = type,
};
int ret;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
if (namelen + 1 > sizeof(key.name))
return nfserr_badowner;
memcpy(key.name, name, namelen);
key.name[namelen] = '\0';
strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item);
if (ret == -ENOENT)
return nfserr_badowner;
if (ret)
return nfserrno(ret);
*id = item->id;
cache_put(&item->h, nn->nametoid_cache);
return 0;
}
static __be32 encode_ascii_id(struct xdr_stream *xdr, u32 id)
{
char buf[11];
int len;
__be32 *p;
len = sprintf(buf, "%u", id);
p = xdr_reserve_space(xdr, len + 4);
if (!p)
return nfserr_resource;
p = xdr_encode_opaque(p, buf, len);
return 0;
}
static __be32 idmap_id_to_name(struct xdr_stream *xdr,
struct svc_rqst *rqstp, int type, u32 id)
{
struct ent *item, key = {
.id = id,
.type = type,
};
__be32 *p;
int ret;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);
if (ret == -ENOENT)
return encode_ascii_id(xdr, id);
if (ret)
return nfserrno(ret);
ret = strlen(item->name);
WARN_ON_ONCE(ret > IDMAP_NAMESZ);
p = xdr_reserve_space(xdr, ret + 4);
if (!p)
return nfserr_resource;
p = xdr_encode_opaque(p, item->name, ret);
cache_put(&item->h, nn->idtoname_cache);
return 0;
}
static bool
numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id)
{
int ret;
char buf[11];
if (namelen + 1 > sizeof(buf))
/* too long to represent a 32-bit id: */
return false;
/* Just to make sure it's null-terminated: */
memcpy(buf, name, namelen);
buf[namelen] = '\0';
ret = kstrtouint(buf, 10, id);
return ret == 0;
}
static __be32
do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id)
{
if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
if (numeric_name_to_id(rqstp, type, name, namelen, id))
return 0;
/*
* otherwise, fall through and try idmapping, for
* backwards compatibility with clients sending names:
*/
return idmap_name_to_id(rqstp, type, name, namelen, id);
}
static __be32 encode_name_from_id(struct xdr_stream *xdr,
struct svc_rqst *rqstp, int type, u32 id)
{
if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
return encode_ascii_id(xdr, id);
return idmap_id_to_name(xdr, rqstp, type, id);
}
__be32
nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen,
kuid_t *uid)
{
__be32 status;
u32 id = -1;
status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id);
*uid = make_kuid(&init_user_ns, id);
if (!uid_valid(*uid))
status = nfserr_badowner;
return status;
}
__be32
nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen,
kgid_t *gid)
{
__be32 status;
u32 id = -1;
status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id);
*gid = make_kgid(&init_user_ns, id);
if (!gid_valid(*gid))
status = nfserr_badowner;
return status;
}
__be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp,
kuid_t uid)
{
u32 id = from_kuid(&init_user_ns, uid);
return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id);
}
__be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp,
kgid_t gid)
{
u32 id = from_kgid(&init_user_ns, gid);
return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id);
}

2005
fs/nfsd/nfs4proc.c Normal file

File diff suppressed because it is too large Load diff

1550
fs/nfsd/nfs4recover.c Normal file

File diff suppressed because it is too large Load diff

6596
fs/nfsd/nfs4state.c Normal file

File diff suppressed because it is too large Load diff

4102
fs/nfsd/nfs4xdr.c Normal file

File diff suppressed because it is too large Load diff

633
fs/nfsd/nfscache.c Normal file
View file

@ -0,0 +1,633 @@
/*
* Request reply cache. This is currently a global cache, but this may
* change in the future and be a per-client cache.
*
* This code is heavily inspired by the 44BSD implementation, although
* it does things a bit differently.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/slab.h>
#include <linux/sunrpc/addr.h>
#include <linux/highmem.h>
#include <linux/log2.h>
#include <linux/hash.h>
#include <net/checksum.h>
#include "nfsd.h"
#include "cache.h"
#define NFSDDBG_FACILITY NFSDDBG_REPCACHE
/*
* We use this value to determine the number of hash buckets from the max
* cache size, the idea being that when the cache is at its maximum number
* of entries, then this should be the average number of entries per bucket.
*/
#define TARGET_BUCKET_SIZE 64
struct nfsd_drc_bucket {
struct list_head lru_head;
spinlock_t cache_lock;
};
static struct nfsd_drc_bucket *drc_hashtbl;
static struct kmem_cache *drc_slab;
/* max number of entries allowed in the cache */
static unsigned int max_drc_entries;
/* number of significant bits in the hash value */
static unsigned int maskbits;
static unsigned int drc_hashsize;
/*
* Stats and other tracking of on the duplicate reply cache. All of these and
* the "rc" fields in nfsdstats are protected by the cache_lock
*/
/* total number of entries */
static atomic_t num_drc_entries;
/* cache misses due only to checksum comparison failures */
static unsigned int payload_misses;
/* amount of memory (in bytes) currently consumed by the DRC */
static unsigned int drc_mem_usage;
/* longest hash chain seen */
static unsigned int longest_chain;
/* size of cache when we saw the longest hash chain */
static unsigned int longest_chain_cachesize;
static int nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec);
static void cache_cleaner_func(struct work_struct *unused);
static unsigned long nfsd_reply_cache_count(struct shrinker *shrink,
struct shrink_control *sc);
static unsigned long nfsd_reply_cache_scan(struct shrinker *shrink,
struct shrink_control *sc);
static struct shrinker nfsd_reply_cache_shrinker = {
.scan_objects = nfsd_reply_cache_scan,
.count_objects = nfsd_reply_cache_count,
.seeks = 1,
};
/*
* locking for the reply cache:
* A cache entry is "single use" if c_state == RC_INPROG
* Otherwise, it when accessing _prev or _next, the lock must be held.
*/
static DECLARE_DELAYED_WORK(cache_cleaner, cache_cleaner_func);
/*
* Put a cap on the size of the DRC based on the amount of available
* low memory in the machine.
*
* 64MB: 8192
* 128MB: 11585
* 256MB: 16384
* 512MB: 23170
* 1GB: 32768
* 2GB: 46340
* 4GB: 65536
* 8GB: 92681
* 16GB: 131072
*
* ...with a hard cap of 256k entries. In the worst case, each entry will be
* ~1k, so the above numbers should give a rough max of the amount of memory
* used in k.
*/
static unsigned int
nfsd_cache_size_limit(void)
{
unsigned int limit;
unsigned long low_pages = totalram_pages - totalhigh_pages;
limit = (16 * int_sqrt(low_pages)) << (PAGE_SHIFT-10);
return min_t(unsigned int, limit, 256*1024);
}
/*
* Compute the number of hash buckets we need. Divide the max cachesize by
* the "target" max bucket size, and round up to next power of two.
*/
static unsigned int
nfsd_hashsize(unsigned int limit)
{
return roundup_pow_of_two(limit / TARGET_BUCKET_SIZE);
}
static u32
nfsd_cache_hash(__be32 xid)
{
return hash_32(be32_to_cpu(xid), maskbits);
}
static struct svc_cacherep *
nfsd_reply_cache_alloc(void)
{
struct svc_cacherep *rp;
rp = kmem_cache_alloc(drc_slab, GFP_KERNEL);
if (rp) {
rp->c_state = RC_UNUSED;
rp->c_type = RC_NOCACHE;
INIT_LIST_HEAD(&rp->c_lru);
}
return rp;
}
static void
nfsd_reply_cache_free_locked(struct svc_cacherep *rp)
{
if (rp->c_type == RC_REPLBUFF && rp->c_replvec.iov_base) {
drc_mem_usage -= rp->c_replvec.iov_len;
kfree(rp->c_replvec.iov_base);
}
list_del(&rp->c_lru);
atomic_dec(&num_drc_entries);
drc_mem_usage -= sizeof(*rp);
kmem_cache_free(drc_slab, rp);
}
static void
nfsd_reply_cache_free(struct nfsd_drc_bucket *b, struct svc_cacherep *rp)
{
spin_lock(&b->cache_lock);
nfsd_reply_cache_free_locked(rp);
spin_unlock(&b->cache_lock);
}
int nfsd_reply_cache_init(void)
{
unsigned int hashsize;
unsigned int i;
max_drc_entries = nfsd_cache_size_limit();
atomic_set(&num_drc_entries, 0);
hashsize = nfsd_hashsize(max_drc_entries);
maskbits = ilog2(hashsize);
register_shrinker(&nfsd_reply_cache_shrinker);
drc_slab = kmem_cache_create("nfsd_drc", sizeof(struct svc_cacherep),
0, 0, NULL);
if (!drc_slab)
goto out_nomem;
drc_hashtbl = kcalloc(hashsize, sizeof(*drc_hashtbl), GFP_KERNEL);
if (!drc_hashtbl)
goto out_nomem;
for (i = 0; i < hashsize; i++) {
INIT_LIST_HEAD(&drc_hashtbl[i].lru_head);
spin_lock_init(&drc_hashtbl[i].cache_lock);
}
drc_hashsize = hashsize;
return 0;
out_nomem:
printk(KERN_ERR "nfsd: failed to allocate reply cache\n");
nfsd_reply_cache_shutdown();
return -ENOMEM;
}
void nfsd_reply_cache_shutdown(void)
{
struct svc_cacherep *rp;
unsigned int i;
unregister_shrinker(&nfsd_reply_cache_shrinker);
cancel_delayed_work_sync(&cache_cleaner);
for (i = 0; i < drc_hashsize; i++) {
struct list_head *head = &drc_hashtbl[i].lru_head;
while (!list_empty(head)) {
rp = list_first_entry(head, struct svc_cacherep, c_lru);
nfsd_reply_cache_free_locked(rp);
}
}
kfree (drc_hashtbl);
drc_hashtbl = NULL;
drc_hashsize = 0;
if (drc_slab) {
kmem_cache_destroy(drc_slab);
drc_slab = NULL;
}
}
/*
* Move cache entry to end of LRU list, and queue the cleaner to run if it's
* not already scheduled.
*/
static void
lru_put_end(struct nfsd_drc_bucket *b, struct svc_cacherep *rp)
{
rp->c_timestamp = jiffies;
list_move_tail(&rp->c_lru, &b->lru_head);
schedule_delayed_work(&cache_cleaner, RC_EXPIRE);
}
static long
prune_bucket(struct nfsd_drc_bucket *b)
{
struct svc_cacherep *rp, *tmp;
long freed = 0;
list_for_each_entry_safe(rp, tmp, &b->lru_head, c_lru) {
/*
* Don't free entries attached to calls that are still
* in-progress, but do keep scanning the list.
*/
if (rp->c_state == RC_INPROG)
continue;
if (atomic_read(&num_drc_entries) <= max_drc_entries &&
time_before(jiffies, rp->c_timestamp + RC_EXPIRE))
break;
nfsd_reply_cache_free_locked(rp);
freed++;
}
return freed;
}
/*
* 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.
*/
static long
prune_cache_entries(void)
{
unsigned int i;
long freed = 0;
bool cancel = true;
for (i = 0; i < drc_hashsize; i++) {
struct nfsd_drc_bucket *b = &drc_hashtbl[i];
if (list_empty(&b->lru_head))
continue;
spin_lock(&b->cache_lock);
freed += prune_bucket(b);
if (!list_empty(&b->lru_head))
cancel = false;
spin_unlock(&b->cache_lock);
}
/*
* Conditionally rearm the job to run in RC_EXPIRE since we just
* ran the pruner.
*/
if (!cancel)
mod_delayed_work(system_wq, &cache_cleaner, RC_EXPIRE);
return freed;
}
static void
cache_cleaner_func(struct work_struct *unused)
{
prune_cache_entries();
}
static unsigned long
nfsd_reply_cache_count(struct shrinker *shrink, struct shrink_control *sc)
{
return atomic_read(&num_drc_entries);
}
static unsigned long
nfsd_reply_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
{
return prune_cache_entries();
}
/*
* Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes
*/
static __wsum
nfsd_cache_csum(struct svc_rqst *rqstp)
{
int idx;
unsigned int base;
__wsum csum;
struct xdr_buf *buf = &rqstp->rq_arg;
const unsigned char *p = buf->head[0].iov_base;
size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len,
RC_CSUMLEN);
size_t len = min(buf->head[0].iov_len, csum_len);
/* rq_arg.head first */
csum = csum_partial(p, len, 0);
csum_len -= len;
/* Continue into page array */
idx = buf->page_base / PAGE_SIZE;
base = buf->page_base & ~PAGE_MASK;
while (csum_len) {
p = page_address(buf->pages[idx]) + base;
len = min_t(size_t, PAGE_SIZE - base, csum_len);
csum = csum_partial(p, len, csum);
csum_len -= len;
base = 0;
++idx;
}
return csum;
}
static bool
nfsd_cache_match(struct svc_rqst *rqstp, __wsum csum, struct svc_cacherep *rp)
{
/* Check RPC XID first */
if (rqstp->rq_xid != rp->c_xid)
return false;
/* compare checksum of NFS data */
if (csum != rp->c_csum) {
++payload_misses;
return false;
}
/* Other discriminators */
if (rqstp->rq_proc != rp->c_proc ||
rqstp->rq_prot != rp->c_prot ||
rqstp->rq_vers != rp->c_vers ||
rqstp->rq_arg.len != rp->c_len ||
!rpc_cmp_addr(svc_addr(rqstp), (struct sockaddr *)&rp->c_addr) ||
rpc_get_port(svc_addr(rqstp)) != rpc_get_port((struct sockaddr *)&rp->c_addr))
return false;
return true;
}
/*
* Search the request hash for an entry that matches the given rqstp.
* Must be called with cache_lock held. Returns the found entry or
* NULL on failure.
*/
static struct svc_cacherep *
nfsd_cache_search(struct nfsd_drc_bucket *b, struct svc_rqst *rqstp,
__wsum csum)
{
struct svc_cacherep *rp, *ret = NULL;
struct list_head *rh = &b->lru_head;
unsigned int entries = 0;
list_for_each_entry(rp, rh, c_lru) {
++entries;
if (nfsd_cache_match(rqstp, csum, rp)) {
ret = rp;
break;
}
}
/* tally hash chain length stats */
if (entries > longest_chain) {
longest_chain = entries;
longest_chain_cachesize = atomic_read(&num_drc_entries);
} else if (entries == longest_chain) {
/* prefer to keep the smallest cachesize possible here */
longest_chain_cachesize = min_t(unsigned int,
longest_chain_cachesize,
atomic_read(&num_drc_entries));
}
return ret;
}
/*
* Try to find an entry matching the current call in the cache. When none
* is found, we try to grab the oldest expired entry off the LRU list. If
* a suitable one isn't there, then drop the cache_lock and allocate a
* new one, then search again in case one got inserted while this thread
* didn't hold the lock.
*/
int
nfsd_cache_lookup(struct svc_rqst *rqstp)
{
struct svc_cacherep *rp, *found;
__be32 xid = rqstp->rq_xid;
u32 proto = rqstp->rq_prot,
vers = rqstp->rq_vers,
proc = rqstp->rq_proc;
__wsum csum;
u32 hash = nfsd_cache_hash(xid);
struct nfsd_drc_bucket *b = &drc_hashtbl[hash];
unsigned long age;
int type = rqstp->rq_cachetype;
int rtn = RC_DOIT;
rqstp->rq_cacherep = NULL;
if (type == RC_NOCACHE) {
nfsdstats.rcnocache++;
return rtn;
}
csum = nfsd_cache_csum(rqstp);
/*
* Since the common case is a cache miss followed by an insert,
* preallocate an entry.
*/
rp = nfsd_reply_cache_alloc();
spin_lock(&b->cache_lock);
if (likely(rp)) {
atomic_inc(&num_drc_entries);
drc_mem_usage += sizeof(*rp);
}
/* go ahead and prune the cache */
prune_bucket(b);
found = nfsd_cache_search(b, rqstp, csum);
if (found) {
if (likely(rp))
nfsd_reply_cache_free_locked(rp);
rp = found;
goto found_entry;
}
if (!rp) {
dprintk("nfsd: unable to allocate DRC entry!\n");
goto out;
}
nfsdstats.rcmisses++;
rqstp->rq_cacherep = rp;
rp->c_state = RC_INPROG;
rp->c_xid = xid;
rp->c_proc = proc;
rpc_copy_addr((struct sockaddr *)&rp->c_addr, svc_addr(rqstp));
rpc_set_port((struct sockaddr *)&rp->c_addr, rpc_get_port(svc_addr(rqstp)));
rp->c_prot = proto;
rp->c_vers = vers;
rp->c_len = rqstp->rq_arg.len;
rp->c_csum = csum;
lru_put_end(b, rp);
/* release any buffer */
if (rp->c_type == RC_REPLBUFF) {
drc_mem_usage -= rp->c_replvec.iov_len;
kfree(rp->c_replvec.iov_base);
rp->c_replvec.iov_base = NULL;
}
rp->c_type = RC_NOCACHE;
out:
spin_unlock(&b->cache_lock);
return rtn;
found_entry:
nfsdstats.rchits++;
/* We found a matching entry which is either in progress or done. */
age = jiffies - rp->c_timestamp;
lru_put_end(b, rp);
rtn = RC_DROPIT;
/* Request being processed or excessive rexmits */
if (rp->c_state == RC_INPROG || age < RC_DELAY)
goto out;
/* From the hall of fame of impractical attacks:
* Is this a user who tries to snoop on the cache? */
rtn = RC_DOIT;
if (!rqstp->rq_secure && rp->c_secure)
goto out;
/* Compose RPC reply header */
switch (rp->c_type) {
case RC_NOCACHE:
break;
case RC_REPLSTAT:
svc_putu32(&rqstp->rq_res.head[0], rp->c_replstat);
rtn = RC_REPLY;
break;
case RC_REPLBUFF:
if (!nfsd_cache_append(rqstp, &rp->c_replvec))
goto out; /* should not happen */
rtn = RC_REPLY;
break;
default:
printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type);
nfsd_reply_cache_free_locked(rp);
}
goto out;
}
/*
* Update a cache entry. This is called from nfsd_dispatch when
* the procedure has been executed and the complete reply is in
* rqstp->rq_res.
*
* We're copying around data here rather than swapping buffers because
* the toplevel loop requires max-sized buffers, which would be a waste
* of memory for a cache with a max reply size of 100 bytes (diropokres).
*
* If we should start to use different types of cache entries tailored
* specifically for attrstat and fh's, we may save even more space.
*
* Also note that a cachetype of RC_NOCACHE can legally be passed when
* nfsd failed to encode a reply that otherwise would have been cached.
* In this case, nfsd_cache_update is called with statp == NULL.
*/
void
nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
{
struct svc_cacherep *rp = rqstp->rq_cacherep;
struct kvec *resv = &rqstp->rq_res.head[0], *cachv;
u32 hash;
struct nfsd_drc_bucket *b;
int len;
size_t bufsize = 0;
if (!rp)
return;
hash = nfsd_cache_hash(rp->c_xid);
b = &drc_hashtbl[hash];
len = resv->iov_len - ((char*)statp - (char*)resv->iov_base);
len >>= 2;
/* Don't cache excessive amounts of data and XDR failures */
if (!statp || len > (256 >> 2)) {
nfsd_reply_cache_free(b, rp);
return;
}
switch (cachetype) {
case RC_REPLSTAT:
if (len != 1)
printk("nfsd: RC_REPLSTAT/reply len %d!\n",len);
rp->c_replstat = *statp;
break;
case RC_REPLBUFF:
cachv = &rp->c_replvec;
bufsize = len << 2;
cachv->iov_base = kmalloc(bufsize, GFP_KERNEL);
if (!cachv->iov_base) {
nfsd_reply_cache_free(b, rp);
return;
}
cachv->iov_len = bufsize;
memcpy(cachv->iov_base, statp, bufsize);
break;
case RC_NOCACHE:
nfsd_reply_cache_free(b, rp);
return;
}
spin_lock(&b->cache_lock);
drc_mem_usage += bufsize;
lru_put_end(b, rp);
rp->c_secure = rqstp->rq_secure;
rp->c_type = cachetype;
rp->c_state = RC_DONE;
spin_unlock(&b->cache_lock);
return;
}
/*
* Copy cached reply to current reply buffer. Should always fit.
* FIXME as reply is in a page, we should just attach the page, and
* keep a refcount....
*/
static int
nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
{
struct kvec *vec = &rqstp->rq_res.head[0];
if (vec->iov_len + data->iov_len > PAGE_SIZE) {
printk(KERN_WARNING "nfsd: cached reply too large (%Zd).\n",
data->iov_len);
return 0;
}
memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len);
vec->iov_len += data->iov_len;
return 1;
}
/*
* Note that fields may be added, removed or reordered in the future. Programs
* scraping this file for info should test the labels to ensure they're
* getting the correct field.
*/
static int nfsd_reply_cache_stats_show(struct seq_file *m, void *v)
{
seq_printf(m, "max entries: %u\n", max_drc_entries);
seq_printf(m, "num entries: %u\n",
atomic_read(&num_drc_entries));
seq_printf(m, "hash buckets: %u\n", 1 << maskbits);
seq_printf(m, "mem usage: %u\n", drc_mem_usage);
seq_printf(m, "cache hits: %u\n", nfsdstats.rchits);
seq_printf(m, "cache misses: %u\n", nfsdstats.rcmisses);
seq_printf(m, "not cached: %u\n", nfsdstats.rcnocache);
seq_printf(m, "payload misses: %u\n", payload_misses);
seq_printf(m, "longest chain len: %u\n", longest_chain);
seq_printf(m, "cachesize at longest: %u\n", longest_chain_cachesize);
return 0;
}
int nfsd_reply_cache_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, nfsd_reply_cache_stats_show, NULL);
}

1316
fs/nfsd/nfsctl.c Normal file

File diff suppressed because it is too large Load diff

410
fs/nfsd/nfsd.h Normal file
View file

@ -0,0 +1,410 @@
/*
* Hodge-podge collection of knfsd-related stuff.
* I will sort this out later.
*
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef LINUX_NFSD_NFSD_H
#define LINUX_NFSD_NFSD_H
#include <linux/types.h>
#include <linux/mount.h>
#include <linux/nfs.h>
#include <linux/nfs2.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/msg_prot.h>
#include <uapi/linux/nfsd/debug.h>
#include "stats.h"
#include "export.h"
#undef ifdebug
#ifdef NFSD_DEBUG
# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag)
#else
# define ifdebug(flag) if (0)
#endif
/*
* nfsd version
*/
#define NFSD_SUPPORTED_MINOR_VERSION 2
/*
* Maximum blocksizes supported by daemon under various circumstances.
*/
#define NFSSVC_MAXBLKSIZE RPCSVC_MAXPAYLOAD
/* NFSv2 is limited by the protocol specification, see RFC 1094 */
#define NFSSVC_MAXBLKSIZE_V2 (8*1024)
/*
* Largest number of bytes we need to allocate for an NFS
* call or reply. Used to control buffer sizes. We use
* the length of v3 WRITE, READDIR and READDIR replies
* which are an RPC header, up to 26 XDR units of reply
* data, and some page data.
*
* Note that accuracy here doesn't matter too much as the
* size is rounded up to a page size when allocating space.
*/
#define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE)
struct readdir_cd {
__be32 err; /* 0, nfserr, or nfserr_eof */
};
extern struct svc_program nfsd_program;
extern struct svc_version nfsd_version2, nfsd_version3,
nfsd_version4;
extern struct mutex nfsd_mutex;
extern spinlock_t nfsd_drc_lock;
extern unsigned long nfsd_drc_max_mem;
extern unsigned long nfsd_drc_mem_used;
extern const struct seq_operations nfs_exports_op;
/*
* Function prototypes.
*/
int nfsd_svc(int nrservs, struct net *net);
int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp);
int nfsd_nrthreads(struct net *);
int nfsd_nrpools(struct net *);
int nfsd_get_nrthreads(int n, int *, struct net *);
int nfsd_set_nrthreads(int n, int *, struct net *);
int nfsd_pool_stats_open(struct inode *, struct file *);
int nfsd_pool_stats_release(struct inode *, struct file *);
void nfsd_destroy(struct net *net);
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
#ifdef CONFIG_NFSD_V2_ACL
extern struct svc_version nfsd_acl_version2;
#else
#define nfsd_acl_version2 NULL
#endif
#ifdef CONFIG_NFSD_V3_ACL
extern struct svc_version nfsd_acl_version3;
#else
#define nfsd_acl_version3 NULL
#endif
#endif
enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
int nfsd_vers(int vers, enum vers_op change);
int nfsd_minorversion(u32 minorversion, enum vers_op change);
void nfsd_reset_versions(void);
int nfsd_create_serv(struct net *net);
extern int nfsd_max_blksize;
static inline int nfsd_v4client(struct svc_rqst *rq)
{
return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
}
/*
* NFSv4 State
*/
#ifdef CONFIG_NFSD_V4
extern unsigned long max_delegations;
int nfsd4_init_slabs(void);
void nfsd4_free_slabs(void);
int nfs4_state_start(void);
int nfs4_state_start_net(struct net *net);
void nfs4_state_shutdown(void);
void nfs4_state_shutdown_net(struct net *net);
void nfs4_reset_lease(time_t leasetime);
int nfs4_reset_recoverydir(char *recdir);
char * nfs4_recoverydir(void);
#else
static inline int nfsd4_init_slabs(void) { return 0; }
static inline void nfsd4_free_slabs(void) { }
static inline int nfs4_state_start(void) { return 0; }
static inline int nfs4_state_start_net(struct net *net) { return 0; }
static inline void nfs4_state_shutdown(void) { }
static inline void nfs4_state_shutdown_net(struct net *net) { }
static inline void nfs4_reset_lease(time_t leasetime) { }
static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
static inline char * nfs4_recoverydir(void) {return NULL; }
#endif
/*
* lockd binding
*/
void nfsd_lockd_init(void);
void nfsd_lockd_shutdown(void);
/*
* These macros provide pre-xdr'ed values for faster operation.
*/
#define nfs_ok cpu_to_be32(NFS_OK)
#define nfserr_perm cpu_to_be32(NFSERR_PERM)
#define nfserr_noent cpu_to_be32(NFSERR_NOENT)
#define nfserr_io cpu_to_be32(NFSERR_IO)
#define nfserr_nxio cpu_to_be32(NFSERR_NXIO)
#define nfserr_eagain cpu_to_be32(NFSERR_EAGAIN)
#define nfserr_acces cpu_to_be32(NFSERR_ACCES)
#define nfserr_exist cpu_to_be32(NFSERR_EXIST)
#define nfserr_xdev cpu_to_be32(NFSERR_XDEV)
#define nfserr_nodev cpu_to_be32(NFSERR_NODEV)
#define nfserr_notdir cpu_to_be32(NFSERR_NOTDIR)
#define nfserr_isdir cpu_to_be32(NFSERR_ISDIR)
#define nfserr_inval cpu_to_be32(NFSERR_INVAL)
#define nfserr_fbig cpu_to_be32(NFSERR_FBIG)
#define nfserr_nospc cpu_to_be32(NFSERR_NOSPC)
#define nfserr_rofs cpu_to_be32(NFSERR_ROFS)
#define nfserr_mlink cpu_to_be32(NFSERR_MLINK)
#define nfserr_opnotsupp cpu_to_be32(NFSERR_OPNOTSUPP)
#define nfserr_nametoolong cpu_to_be32(NFSERR_NAMETOOLONG)
#define nfserr_notempty cpu_to_be32(NFSERR_NOTEMPTY)
#define nfserr_dquot cpu_to_be32(NFSERR_DQUOT)
#define nfserr_stale cpu_to_be32(NFSERR_STALE)
#define nfserr_remote cpu_to_be32(NFSERR_REMOTE)
#define nfserr_wflush cpu_to_be32(NFSERR_WFLUSH)
#define nfserr_badhandle cpu_to_be32(NFSERR_BADHANDLE)
#define nfserr_notsync cpu_to_be32(NFSERR_NOT_SYNC)
#define nfserr_badcookie cpu_to_be32(NFSERR_BAD_COOKIE)
#define nfserr_notsupp cpu_to_be32(NFSERR_NOTSUPP)
#define nfserr_toosmall cpu_to_be32(NFSERR_TOOSMALL)
#define nfserr_serverfault cpu_to_be32(NFSERR_SERVERFAULT)
#define nfserr_badtype cpu_to_be32(NFSERR_BADTYPE)
#define nfserr_jukebox cpu_to_be32(NFSERR_JUKEBOX)
#define nfserr_denied cpu_to_be32(NFSERR_DENIED)
#define nfserr_deadlock cpu_to_be32(NFSERR_DEADLOCK)
#define nfserr_expired cpu_to_be32(NFSERR_EXPIRED)
#define nfserr_bad_cookie cpu_to_be32(NFSERR_BAD_COOKIE)
#define nfserr_same cpu_to_be32(NFSERR_SAME)
#define nfserr_clid_inuse cpu_to_be32(NFSERR_CLID_INUSE)
#define nfserr_stale_clientid cpu_to_be32(NFSERR_STALE_CLIENTID)
#define nfserr_resource cpu_to_be32(NFSERR_RESOURCE)
#define nfserr_moved cpu_to_be32(NFSERR_MOVED)
#define nfserr_nofilehandle cpu_to_be32(NFSERR_NOFILEHANDLE)
#define nfserr_minor_vers_mismatch cpu_to_be32(NFSERR_MINOR_VERS_MISMATCH)
#define nfserr_share_denied cpu_to_be32(NFSERR_SHARE_DENIED)
#define nfserr_stale_stateid cpu_to_be32(NFSERR_STALE_STATEID)
#define nfserr_old_stateid cpu_to_be32(NFSERR_OLD_STATEID)
#define nfserr_bad_stateid cpu_to_be32(NFSERR_BAD_STATEID)
#define nfserr_bad_seqid cpu_to_be32(NFSERR_BAD_SEQID)
#define nfserr_symlink cpu_to_be32(NFSERR_SYMLINK)
#define nfserr_not_same cpu_to_be32(NFSERR_NOT_SAME)
#define nfserr_lock_range cpu_to_be32(NFSERR_LOCK_RANGE)
#define nfserr_restorefh cpu_to_be32(NFSERR_RESTOREFH)
#define nfserr_attrnotsupp cpu_to_be32(NFSERR_ATTRNOTSUPP)
#define nfserr_bad_xdr cpu_to_be32(NFSERR_BAD_XDR)
#define nfserr_openmode cpu_to_be32(NFSERR_OPENMODE)
#define nfserr_badowner cpu_to_be32(NFSERR_BADOWNER)
#define nfserr_locks_held cpu_to_be32(NFSERR_LOCKS_HELD)
#define nfserr_op_illegal cpu_to_be32(NFSERR_OP_ILLEGAL)
#define nfserr_grace cpu_to_be32(NFSERR_GRACE)
#define nfserr_no_grace cpu_to_be32(NFSERR_NO_GRACE)
#define nfserr_reclaim_bad cpu_to_be32(NFSERR_RECLAIM_BAD)
#define nfserr_badname cpu_to_be32(NFSERR_BADNAME)
#define nfserr_cb_path_down cpu_to_be32(NFSERR_CB_PATH_DOWN)
#define nfserr_locked cpu_to_be32(NFSERR_LOCKED)
#define nfserr_wrongsec cpu_to_be32(NFSERR_WRONGSEC)
#define nfserr_badiomode cpu_to_be32(NFS4ERR_BADIOMODE)
#define nfserr_badlayout cpu_to_be32(NFS4ERR_BADLAYOUT)
#define nfserr_bad_session_digest cpu_to_be32(NFS4ERR_BAD_SESSION_DIGEST)
#define nfserr_badsession cpu_to_be32(NFS4ERR_BADSESSION)
#define nfserr_badslot cpu_to_be32(NFS4ERR_BADSLOT)
#define nfserr_complete_already cpu_to_be32(NFS4ERR_COMPLETE_ALREADY)
#define nfserr_conn_not_bound_to_session cpu_to_be32(NFS4ERR_CONN_NOT_BOUND_TO_SESSION)
#define nfserr_deleg_already_wanted cpu_to_be32(NFS4ERR_DELEG_ALREADY_WANTED)
#define nfserr_back_chan_busy cpu_to_be32(NFS4ERR_BACK_CHAN_BUSY)
#define nfserr_layouttrylater cpu_to_be32(NFS4ERR_LAYOUTTRYLATER)
#define nfserr_layoutunavailable cpu_to_be32(NFS4ERR_LAYOUTUNAVAILABLE)
#define nfserr_nomatching_layout cpu_to_be32(NFS4ERR_NOMATCHING_LAYOUT)
#define nfserr_recallconflict cpu_to_be32(NFS4ERR_RECALLCONFLICT)
#define nfserr_unknown_layouttype cpu_to_be32(NFS4ERR_UNKNOWN_LAYOUTTYPE)
#define nfserr_seq_misordered cpu_to_be32(NFS4ERR_SEQ_MISORDERED)
#define nfserr_sequence_pos cpu_to_be32(NFS4ERR_SEQUENCE_POS)
#define nfserr_req_too_big cpu_to_be32(NFS4ERR_REQ_TOO_BIG)
#define nfserr_rep_too_big cpu_to_be32(NFS4ERR_REP_TOO_BIG)
#define nfserr_rep_too_big_to_cache cpu_to_be32(NFS4ERR_REP_TOO_BIG_TO_CACHE)
#define nfserr_retry_uncached_rep cpu_to_be32(NFS4ERR_RETRY_UNCACHED_REP)
#define nfserr_unsafe_compound cpu_to_be32(NFS4ERR_UNSAFE_COMPOUND)
#define nfserr_too_many_ops cpu_to_be32(NFS4ERR_TOO_MANY_OPS)
#define nfserr_op_not_in_session cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION)
#define nfserr_hash_alg_unsupp cpu_to_be32(NFS4ERR_HASH_ALG_UNSUPP)
#define nfserr_clientid_busy cpu_to_be32(NFS4ERR_CLIENTID_BUSY)
#define nfserr_pnfs_io_hole cpu_to_be32(NFS4ERR_PNFS_IO_HOLE)
#define nfserr_seq_false_retry cpu_to_be32(NFS4ERR_SEQ_FALSE_RETRY)
#define nfserr_bad_high_slot cpu_to_be32(NFS4ERR_BAD_HIGH_SLOT)
#define nfserr_deadsession cpu_to_be32(NFS4ERR_DEADSESSION)
#define nfserr_encr_alg_unsupp cpu_to_be32(NFS4ERR_ENCR_ALG_UNSUPP)
#define nfserr_pnfs_no_layout cpu_to_be32(NFS4ERR_PNFS_NO_LAYOUT)
#define nfserr_not_only_op cpu_to_be32(NFS4ERR_NOT_ONLY_OP)
#define nfserr_wrong_cred cpu_to_be32(NFS4ERR_WRONG_CRED)
#define nfserr_wrong_type cpu_to_be32(NFS4ERR_WRONG_TYPE)
#define nfserr_dirdeleg_unavail cpu_to_be32(NFS4ERR_DIRDELEG_UNAVAIL)
#define nfserr_reject_deleg cpu_to_be32(NFS4ERR_REJECT_DELEG)
#define nfserr_returnconflict cpu_to_be32(NFS4ERR_RETURNCONFLICT)
#define nfserr_deleg_revoked cpu_to_be32(NFS4ERR_DELEG_REVOKED)
#define nfserr_partner_notsupp cpu_to_be32(NFS4ERR_PARTNER_NOTSUPP)
#define nfserr_partner_no_auth cpu_to_be32(NFS4ERR_PARTNER_NO_AUTH)
#define nfserr_union_notsupp cpu_to_be32(NFS4ERR_UNION_NOTSUPP)
#define nfserr_offload_denied cpu_to_be32(NFS4ERR_OFFLOAD_DENIED)
#define nfserr_wrong_lfs cpu_to_be32(NFS4ERR_WRONG_LFS)
#define nfserr_badlabel cpu_to_be32(NFS4ERR_BADLABEL)
/* error codes for internal use */
/* if a request fails due to kmalloc failure, it gets dropped.
* Client should resend eventually
*/
#define nfserr_dropit cpu_to_be32(30000)
/* end-of-file indicator in readdir */
#define nfserr_eof cpu_to_be32(30001)
/* replay detected */
#define nfserr_replay_me cpu_to_be32(11001)
/* nfs41 replay detected */
#define nfserr_replay_cache cpu_to_be32(11002)
/* Check for dir entries '.' and '..' */
#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
#ifdef CONFIG_NFSD_V4
/* before processing a COMPOUND operation, we have to check that there
* is enough space in the buffer for XDR encode to succeed. otherwise,
* we might process an operation with side effects, and be unable to
* tell the client that the operation succeeded.
*
* COMPOUND_SLACK_SPACE - this is the minimum bytes of buffer space
* needed to encode an "ordinary" _successful_ operation. (GETATTR,
* READ, READDIR, and READLINK have their own buffer checks.) if we
* fall below this level, we fail the next operation with NFS4ERR_RESOURCE.
*
* COMPOUND_ERR_SLACK_SPACE - this is the minimum bytes of buffer space
* needed to encode an operation which has failed with NFS4ERR_RESOURCE.
* care is taken to ensure that we never fall below this level for any
* reason.
*/
#define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */
#define COMPOUND_ERR_SLACK_SPACE 16 /* OP_SETATTR */
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
/*
* The following attributes are currently not supported by the NFSv4 server:
* ARCHIVE (deprecated anyway)
* HIDDEN (unlikely to be supported any time soon)
* MIMETYPE (unlikely to be supported any time soon)
* QUOTA_* (will be supported in a forthcoming patch)
* SYSTEM (unlikely to be supported any time soon)
* TIME_BACKUP (unlikely to be supported any time soon)
* TIME_CREATE (unlikely to be supported any time soon)
*/
#define NFSD4_SUPPORTED_ATTRS_WORD0 \
(FATTR4_WORD0_SUPPORTED_ATTRS | FATTR4_WORD0_TYPE | FATTR4_WORD0_FH_EXPIRE_TYPE \
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE | FATTR4_WORD0_LINK_SUPPORT \
| FATTR4_WORD0_SYMLINK_SUPPORT | FATTR4_WORD0_NAMED_ATTR | FATTR4_WORD0_FSID \
| FATTR4_WORD0_UNIQUE_HANDLES | FATTR4_WORD0_LEASE_TIME | FATTR4_WORD0_RDATTR_ERROR \
| FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME | FATTR4_WORD0_CASE_INSENSITIVE \
| FATTR4_WORD0_CASE_PRESERVING | FATTR4_WORD0_CHOWN_RESTRICTED \
| FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FILEID | FATTR4_WORD0_FILES_AVAIL \
| FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_HOMOGENEOUS \
| FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \
| FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL)
#define NFSD4_SUPPORTED_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_NO_TRUNC | FATTR4_WORD1_NUMLINKS \
| 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_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \
| FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA \
| FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
#define NFSD4_SUPPORTED_ATTRS_WORD2 0
#define NFSD4_1_SUPPORTED_ATTRS_WORD0 \
NFSD4_SUPPORTED_ATTRS_WORD0
#define NFSD4_1_SUPPORTED_ATTRS_WORD1 \
NFSD4_SUPPORTED_ATTRS_WORD1
#define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
(NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define NFSD4_2_SECURITY_ATTRS FATTR4_WORD2_SECURITY_LABEL
#else
#define NFSD4_2_SECURITY_ATTRS 0
#endif
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
NFSD4_2_SECURITY_ATTRS)
static inline u32 nfsd_suppattrs0(u32 minorversion)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0
: NFSD4_SUPPORTED_ATTRS_WORD0;
}
static inline u32 nfsd_suppattrs1(u32 minorversion)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD1
: NFSD4_SUPPORTED_ATTRS_WORD1;
}
static inline u32 nfsd_suppattrs2(u32 minorversion)
{
switch (minorversion) {
default: return NFSD4_2_SUPPORTED_ATTRS_WORD2;
case 1: return NFSD4_1_SUPPORTED_ATTRS_WORD2;
case 0: return NFSD4_SUPPORTED_ATTRS_WORD2;
}
}
/* These will return ERR_INVAL if specified in GETATTR or READDIR. */
#define NFSD_WRITEONLY_ATTRS_WORD1 \
(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
/* These are the only attrs allowed in CREATE/OPEN/SETATTR. */
#define NFSD_WRITEABLE_ATTRS_WORD0 \
(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
#define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL
#else
#define NFSD_WRITEABLE_ATTRS_WORD2 0
#endif
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
NFSD_WRITEABLE_ATTRS_WORD0
/*
* we currently store the exclusive create verifier in the v_{a,m}time
* attributes so the client can't set these at create time using EXCLUSIVE4_1
*/
#define NFSD_SUPPATTR_EXCLCREAT_WORD1 \
(NFSD_WRITEABLE_ATTRS_WORD1 & \
~(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET))
#define NFSD_SUPPATTR_EXCLCREAT_WORD2 \
NFSD_WRITEABLE_ATTRS_WORD2
extern int nfsd4_is_junction(struct dentry *dentry);
extern int register_cld_notifier(void);
extern void unregister_cld_notifier(void);
#else /* CONFIG_NFSD_V4 */
static inline int nfsd4_is_junction(struct dentry *dentry)
{
return 0;
}
#define register_cld_notifier() 0
#define unregister_cld_notifier() do { } while(0)
#endif /* CONFIG_NFSD_V4 */
#endif /* LINUX_NFSD_NFSD_H */

692
fs/nfsd/nfsfh.c Normal file
View file

@ -0,0 +1,692 @@
/*
* NFS server file handle treatment.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
* Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
* Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
* ... and again Southern-Winter 2001 to support export_operations
*/
#include <linux/exportfs.h>
#include <linux/sunrpc/svcauth_gss.h>
#include "nfsd.h"
#include "vfs.h"
#include "auth.h"
#define NFSDDBG_FACILITY NFSDDBG_FH
/*
* our acceptability function.
* if NOSUBTREECHECK, accept anything
* if not, require that we can walk up to exp->ex_dentry
* doing some checks on the 'x' bits
*/
static int nfsd_acceptable(void *expv, struct dentry *dentry)
{
struct svc_export *exp = expv;
int rv;
struct dentry *tdentry;
struct dentry *parent;
if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
return 1;
tdentry = dget(dentry);
while (tdentry != exp->ex_path.dentry && !IS_ROOT(tdentry)) {
/* make sure parents give x permission to user */
int err;
parent = dget_parent(tdentry);
err = inode_permission(parent->d_inode, MAY_EXEC);
if (err < 0) {
dput(parent);
break;
}
dput(tdentry);
tdentry = parent;
}
if (tdentry != exp->ex_path.dentry)
dprintk("nfsd_acceptable failed at %p %pd\n", tdentry, tdentry);
rv = (tdentry == exp->ex_path.dentry);
dput(tdentry);
return rv;
}
/* Type check. The correct error return for type mismatches does not seem to be
* generally agreed upon. SunOS seems to use EISDIR if file isn't S_IFREG; a
* comment in the NFSv3 spec says this is incorrect (implementation notes for
* the write call).
*/
static inline __be32
nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, umode_t requested)
{
mode &= S_IFMT;
if (requested == 0) /* the caller doesn't care */
return nfs_ok;
if (mode == requested)
return nfs_ok;
/*
* v4 has an error more specific than err_notdir which we should
* return in preference to err_notdir:
*/
if (rqstp->rq_vers == 4 && mode == S_IFLNK)
return nfserr_symlink;
if (requested == S_IFDIR)
return nfserr_notdir;
if (mode == S_IFDIR)
return nfserr_isdir;
return nfserr_inval;
}
static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
struct svc_export *exp)
{
int flags = nfsexp_flags(rqstp, exp);
/* Check if the request originated from a secure port. */
if (!rqstp->rq_secure && !(flags & NFSEXP_INSECURE_PORT)) {
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
dprintk("nfsd: request from insecure port %s!\n",
svc_print_addr(rqstp, buf, sizeof(buf)));
return nfserr_perm;
}
/* Set user creds for this exportpoint */
return nfserrno(nfsd_setuser(rqstp, exp));
}
static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
struct dentry *dentry, struct svc_export *exp)
{
if (!(exp->ex_flags & NFSEXP_V4ROOT))
return nfs_ok;
/*
* v2/v3 clients have no need for the V4ROOT export--they use
* the mount protocl instead; also, further V4ROOT checks may be
* in v4-specific code, in which case v2/v3 clients could bypass
* them.
*/
if (!nfsd_v4client(rqstp))
return nfserr_stale;
/*
* We're exposing only the directories and symlinks that have to be
* traversed on the way to real exports:
*/
if (unlikely(!S_ISDIR(dentry->d_inode->i_mode) &&
!S_ISLNK(dentry->d_inode->i_mode)))
return nfserr_stale;
/*
* A pseudoroot export gives permission to access only one
* single directory; the kernel has to make another upcall
* before granting access to anything else under it:
*/
if (unlikely(dentry != exp->ex_path.dentry))
return nfserr_stale;
return nfs_ok;
}
/*
* Use the given filehandle to look up the corresponding export and
* dentry. On success, the results are used to set fh_export and
* fh_dentry.
*/
static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
{
struct knfsd_fh *fh = &fhp->fh_handle;
struct fid *fid = NULL, sfid;
struct svc_export *exp;
struct dentry *dentry;
int fileid_type;
int data_left = fh->fh_size/4;
__be32 error;
error = nfserr_stale;
if (rqstp->rq_vers > 2)
error = nfserr_badhandle;
if (rqstp->rq_vers == 4 && fh->fh_size == 0)
return nfserr_nofilehandle;
if (fh->fh_version == 1) {
int len;
if (--data_left < 0)
return error;
if (fh->fh_auth_type != 0)
return error;
len = key_len(fh->fh_fsid_type) / 4;
if (len == 0)
return error;
if (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
/* deprecated, convert to type 3 */
len = key_len(FSID_ENCODE_DEV)/4;
fh->fh_fsid_type = FSID_ENCODE_DEV;
/*
* struct knfsd_fh uses host-endian fields, which are
* sometimes used to hold net-endian values. This
* confuses sparse, so we must use __force here to
* keep it from complaining.
*/
fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl((__force __be32)fh->fh_fsid[0]),
ntohl((__force __be32)fh->fh_fsid[1])));
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);
}
error = nfserr_stale;
if (PTR_ERR(exp) == -ENOENT)
return error;
if (IS_ERR(exp))
return nfserrno(PTR_ERR(exp));
if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) {
/* Elevate privileges so that the lack of 'r' or 'x'
* permission on some parent directory will
* not stop exportfs_decode_fh from being able
* to reconnect a directory into the dentry cache.
* The same problem can affect "SUBTREECHECK" exports,
* but as nfsd_acceptable depends on correct
* access control settings being in effect, we cannot
* fix that case easily.
*/
struct cred *new = prepare_creds();
if (!new) {
error = nfserrno(-ENOMEM);
goto out;
}
new->cap_effective =
cap_raise_nfsd_set(new->cap_effective,
new->cap_permitted);
put_cred(override_creds(new));
put_cred(new);
} else {
error = nfsd_setuser_and_check_port(rqstp, exp);
if (error)
goto out;
}
/*
* Look up the dentry using the NFS file handle.
*/
error = nfserr_stale;
if (rqstp->rq_vers > 2)
error = nfserr_badhandle;
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)
dentry = dget(exp->ex_path.dentry);
else {
dentry = exportfs_decode_fh(exp->ex_path.mnt, fid,
data_left, fileid_type,
nfsd_acceptable, exp);
}
if (dentry == NULL)
goto out;
if (IS_ERR(dentry)) {
if (PTR_ERR(dentry) != -EINVAL)
error = nfserrno(PTR_ERR(dentry));
goto out;
}
if (S_ISDIR(dentry->d_inode->i_mode) &&
(dentry->d_flags & DCACHE_DISCONNECTED)) {
printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %pd2\n",
dentry);
}
fhp->fh_dentry = dentry;
fhp->fh_export = exp;
return 0;
out:
exp_put(exp);
return error;
}
/**
* fh_verify - filehandle lookup and access checking
* @rqstp: pointer to current rpc request
* @fhp: filehandle to be verified
* @type: expected type of object pointed to by filehandle
* @access: type of access needed to object
*
* Look up a dentry from the on-the-wire filehandle, check the client's
* access to the export, and set the current task's credentials.
*
* Regardless of success or failure of fh_verify(), fh_put() should be
* called on @fhp when the caller is finished with the filehandle.
*
* fh_verify() may be called multiple times on a given filehandle, for
* example, when processing an NFSv4 compound. The first call will look
* up a dentry using the on-the-wire filehandle. Subsequent calls will
* skip the lookup and just perform the other checks and possibly change
* the current task's credentials.
*
* @type specifies the type of object expected using one of the S_IF*
* constants defined in include/linux/stat.h. The caller may use zero
* to indicate that it doesn't care, or a negative integer to indicate
* that it expects something not of the given type.
*
* @access is formed from the NFSD_MAY_* constants defined in
* include/linux/nfsd/nfsd.h.
*/
__be32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
{
struct svc_export *exp;
struct dentry *dentry;
__be32 error;
dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
if (!fhp->fh_dentry) {
error = nfsd_set_fh_dentry(rqstp, fhp);
if (error)
goto out;
}
dentry = fhp->fh_dentry;
exp = fhp->fh_export;
/*
* We still have to do all these permission checks, even when
* fh_dentry is already set:
* - fh_verify may be called multiple times with different
* "access" arguments (e.g. nfsd_proc_create calls
* fh_verify(...,NFSD_MAY_EXEC) first, then later (in
* nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE).
* - in the NFSv4 case, the filehandle may have been filled
* in by fh_compose, and given a dentry, but further
* compound operations performed with that filehandle
* still need permissions checks. In the worst case, a
* mountpoint crossing may have changed the export
* options, and we may now need to use a different uid
* (for example, if different id-squashing options are in
* effect on the new filesystem).
*/
error = check_pseudo_root(rqstp, dentry, exp);
if (error)
goto out;
error = nfsd_setuser_and_check_port(rqstp, exp);
if (error)
goto out;
error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
if (error)
goto out;
/*
* pseudoflavor restrictions are not enforced on NLM,
* which clients virtually always use auth_sys for,
* even while using RPCSEC_GSS for NFS.
*/
if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
goto skip_pseudoflavor_check;
/*
* Clients may expect to be able to use auth_sys during mount,
* even if they use gss for everything else; see section 2.3.2
* of rfc 2623.
*/
if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
&& exp->ex_path.dentry == dentry)
goto skip_pseudoflavor_check;
error = check_nfsd_access(exp, rqstp);
if (error)
goto out;
skip_pseudoflavor_check:
/* Finally, check access permissions. */
error = nfsd_permission(rqstp, exp, dentry, access);
if (error) {
dprintk("fh_verify: %pd2 permission failure, "
"acc=%x, error=%d\n",
dentry,
access, ntohl(error));
}
out:
if (error == nfserr_stale)
nfsdstats.fh_stale++;
return error;
}
/*
* Compose a file handle for an NFS reply.
*
* Note that when first composed, the dentry may not yet have
* an inode. In this case a call to fh_update should be made
* before the fh goes out on the wire ...
*/
static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry)
{
if (dentry != exp->ex_path.dentry) {
struct fid *fid = (struct fid *)
(fhp->fh_handle.fh_fsid + fhp->fh_handle.fh_size/4 - 1);
int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
fhp->fh_handle.fh_fileid_type =
exportfs_encode_fh(dentry, fid, &maxsize, subtreecheck);
fhp->fh_handle.fh_size += maxsize * 4;
} else {
fhp->fh_handle.fh_fileid_type = FILEID_ROOT;
}
}
/*
* 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(dentry->d_inode->i_ino);
fh->ofh_generation = dentry->d_inode->i_generation;
if (S_ISDIR(dentry->d_inode->i_mode) ||
(exp->ex_flags & NFSEXP_NOSUBTREECHECK))
fh->ofh_dirino = 0;
}
static bool is_root_export(struct svc_export *exp)
{
return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root;
}
static struct super_block *exp_sb(struct svc_export *exp)
{
return exp->ex_path.dentry->d_inode->i_sb;
}
static bool fsid_type_ok_for_exp(u8 fsid_type, struct svc_export *exp)
{
switch (fsid_type) {
case FSID_DEV:
if (!old_valid_dev(exp_sb(exp)->s_dev))
return 0;
/* FALL THROUGH */
case FSID_MAJOR_MINOR:
case FSID_ENCODE_DEV:
return exp_sb(exp)->s_type->fs_flags & FS_REQUIRES_DEV;
case FSID_NUM:
return exp->ex_flags & NFSEXP_FSID;
case FSID_UUID8:
case FSID_UUID16:
if (!is_root_export(exp))
return 0;
/* fall through */
case FSID_UUID4_INUM:
case FSID_UUID16_INUM:
return exp->ex_uuid != NULL;
}
return 1;
}
static void set_version_and_fsid_type(struct svc_fh *fhp, struct svc_export *exp, struct svc_fh *ref_fh)
{
u8 version;
u8 fsid_type;
retry:
version = 1;
if (ref_fh && ref_fh->fh_export == exp) {
version = ref_fh->fh_handle.fh_version;
fsid_type = ref_fh->fh_handle.fh_fsid_type;
ref_fh = NULL;
switch (version) {
case 0xca:
fsid_type = FSID_DEV;
break;
case 1:
break;
default:
goto retry;
}
/*
* As the fsid -> filesystem mapping was guided by
* user-space, there is no guarantee that the filesystem
* actually supports that fsid type. If it doesn't we
* loop around again without ref_fh set.
*/
if (!fsid_type_ok_for_exp(fsid_type, exp))
goto retry;
} else if (exp->ex_flags & NFSEXP_FSID) {
fsid_type = FSID_NUM;
} else if (exp->ex_uuid) {
if (fhp->fh_maxsize >= 64) {
if (is_root_export(exp))
fsid_type = FSID_UUID16;
else
fsid_type = FSID_UUID16_INUM;
} else {
if (is_root_export(exp))
fsid_type = FSID_UUID8;
else
fsid_type = FSID_UUID4_INUM;
}
} else if (!old_valid_dev(exp_sb(exp)->s_dev))
/* for newer device numbers, we must use a newer fsid format */
fsid_type = FSID_ENCODE_DEV;
else
fsid_type = FSID_DEV;
fhp->fh_handle.fh_version = version;
if (version)
fhp->fh_handle.fh_fsid_type = fsid_type;
}
__be32
fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
struct svc_fh *ref_fh)
{
/* ref_fh is a reference file handle.
* if it is non-null and for the same filesystem, then we should compose
* 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 = dentry->d_inode;
dev_t ex_dev = exp_sb(exp)->s_dev;
dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %pd2, ino=%ld)\n",
MAJOR(ex_dev), MINOR(ex_dev),
(long) exp->ex_path.dentry->d_inode->i_ino,
dentry,
(inode ? inode->i_ino : 0));
/* Choose filehandle version and fsid type based on
* the reference filehandle (if it is in the same export)
* or the export options.
*/
set_version_and_fsid_type(fhp, exp, ref_fh);
if (ref_fh == fhp)
fh_put(ref_fh);
if (fhp->fh_locked || fhp->fh_dentry) {
printk(KERN_ERR "fh_compose: fh %pd2 not initialized!\n",
dentry);
}
if (fhp->fh_maxsize < NFS_FHSIZE)
printk(KERN_ERR "fh_compose: called with maxsize %d! %pd2\n",
fhp->fh_maxsize,
dentry);
fhp->fh_dentry = dget(dentry); /* our internal copy */
fhp->fh_export = exp_get(exp);
if (fhp->fh_handle.fh_version == 0xca) {
/* old style filehandle please */
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(exp->ex_path.dentry->d_inode->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,
fhp->fh_handle.fh_fsid,
ex_dev,
exp->ex_path.dentry->d_inode->i_ino,
exp->ex_fsid, exp->ex_uuid);
if (inode)
_fh_update(fhp, exp, dentry);
if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) {
fh_put(fhp);
return nfserr_opnotsupp;
}
}
return 0;
}
/*
* Update file handle information after changing a dentry.
* This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create
*/
__be32
fh_update(struct svc_fh *fhp)
{
struct dentry *dentry;
if (!fhp->fh_dentry)
goto out_bad;
dentry = fhp->fh_dentry;
if (!dentry->d_inode)
goto out_negative;
if (fhp->fh_handle.fh_version != 1) {
_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);
if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID)
return nfserr_opnotsupp;
}
return 0;
out_bad:
printk(KERN_ERR "fh_update: fh not verified!\n");
return nfserr_serverfault;
out_negative:
printk(KERN_ERR "fh_update: %pd2 still negative!\n",
dentry);
return nfserr_serverfault;
}
/*
* Release a file handle.
*/
void
fh_put(struct svc_fh *fhp)
{
struct dentry * dentry = fhp->fh_dentry;
struct svc_export * exp = fhp->fh_export;
if (dentry) {
fh_unlock(fhp);
fhp->fh_dentry = NULL;
dput(dentry);
#ifdef CONFIG_NFSD_V3
fhp->fh_pre_saved = 0;
fhp->fh_post_saved = 0;
#endif
}
fh_drop_write(fhp);
if (exp) {
exp_put(exp);
fhp->fh_export = NULL;
}
return;
}
/*
* Shorthand for dprintk()'s
*/
char * SVCFH_fmt(struct svc_fh *fhp)
{
struct knfsd_fh *fh = &fhp->fh_handle;
static char buf[80];
sprintf(buf, "%d: %08x %08x %08x %08x %08x %08x",
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;
}
enum fsid_source fsid_source(struct svc_fh *fhp)
{
if (fhp->fh_handle.fh_version != 1)
return FSIDSOURCE_DEV;
switch(fhp->fh_handle.fh_fsid_type) {
case FSID_DEV:
case FSID_ENCODE_DEV:
case FSID_MAJOR_MINOR:
if (exp_sb(fhp->fh_export)->s_type->fs_flags & FS_REQUIRES_DEV)
return FSIDSOURCE_DEV;
break;
case FSID_NUM:
if (fhp->fh_export->ex_flags & NFSEXP_FSID)
return FSIDSOURCE_FSID;
break;
default:
break;
}
/* either a UUID type filehandle, or the filehandle doesn't
* match the export.
*/
if (fhp->fh_export->ex_flags & NFSEXP_FSID)
return FSIDSOURCE_FSID;
if (fhp->fh_export->ex_uuid)
return FSIDSOURCE_UUID;
return FSIDSOURCE_DEV;
}

274
fs/nfsd/nfsfh.h Normal file
View file

@ -0,0 +1,274 @@
/*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*
* This file describes the layout of the file handles as passed
* over the wire.
*/
#ifndef _LINUX_NFSD_NFSFH_H
#define _LINUX_NFSD_NFSFH_H
#include <linux/sunrpc/svc.h>
#include <uapi/linux/nfsd/nfsfh.h>
static inline __u32 ino_t_to_u32(ino_t ino)
{
return (__u32) ino;
}
static inline ino_t u32_to_ino_t(__u32 uino)
{
return (ino_t) uino;
}
/*
* This is the internal representation of an NFS handle used in knfsd.
* pre_mtime/post_version will be used to support wcc_attr's in NFSv3.
*/
typedef struct svc_fh {
struct knfsd_fh fh_handle; /* FH data */
struct dentry * fh_dentry; /* validated dentry */
struct svc_export * fh_export; /* export pointer */
int fh_maxsize; /* max size for fh_handle */
unsigned char fh_locked; /* inode locked by us */
unsigned char fh_want_write; /* remount protection taken */
#ifdef CONFIG_NFSD_V3
unsigned char fh_post_saved; /* post-op attrs saved */
unsigned char fh_pre_saved; /* pre-op attrs saved */
/* Pre-op attributes saved during fh_lock */
__u64 fh_pre_size; /* size before operation */
struct timespec fh_pre_mtime; /* mtime before oper */
struct timespec fh_pre_ctime; /* ctime before oper */
/*
* pre-op nfsv4 change attr: note must check IS_I_VERSION(inode)
* to find out if it is valid.
*/
u64 fh_pre_change;
/* Post-op attributes saved in fh_unlock */
struct kstat fh_post_attr; /* full attrs after operation */
u64 fh_post_change; /* nfsv4 change; see above */
#endif /* CONFIG_NFSD_V3 */
} svc_fh;
enum nfsd_fsid {
FSID_DEV = 0,
FSID_NUM,
FSID_MAJOR_MINOR,
FSID_ENCODE_DEV,
FSID_UUID4_INUM,
FSID_UUID8,
FSID_UUID16,
FSID_UUID16_INUM,
};
enum fsid_source {
FSIDSOURCE_DEV,
FSIDSOURCE_FSID,
FSIDSOURCE_UUID,
};
extern enum fsid_source fsid_source(struct svc_fh *fhp);
/*
* This might look a little large to "inline" but in all calls except
* one, 'vers' is constant so moste of the function disappears.
*
* In some cases the values are considered to be host endian and in
* others, net endian. fsidv is always considered to be u32 as the
* callers don't know which it will be. So we must use __force to keep
* sparse from complaining. Since these values are opaque to the
* client, that shouldn't be a problem.
*/
static inline void mk_fsid(int vers, u32 *fsidv, dev_t dev, ino_t ino,
u32 fsid, unsigned char *uuid)
{
u32 *up;
switch(vers) {
case FSID_DEV:
fsidv[0] = (__force __u32)htonl((MAJOR(dev)<<16) |
MINOR(dev));
fsidv[1] = ino_t_to_u32(ino);
break;
case FSID_NUM:
fsidv[0] = fsid;
break;
case FSID_MAJOR_MINOR:
fsidv[0] = (__force __u32)htonl(MAJOR(dev));
fsidv[1] = (__force __u32)htonl(MINOR(dev));
fsidv[2] = ino_t_to_u32(ino);
break;
case FSID_ENCODE_DEV:
fsidv[0] = new_encode_dev(dev);
fsidv[1] = ino_t_to_u32(ino);
break;
case FSID_UUID4_INUM:
/* 4 byte fsid and inode number */
up = (u32*)uuid;
fsidv[0] = ino_t_to_u32(ino);
fsidv[1] = up[0] ^ up[1] ^ up[2] ^ up[3];
break;
case FSID_UUID8:
/* 8 byte fsid */
up = (u32*)uuid;
fsidv[0] = up[0] ^ up[2];
fsidv[1] = up[1] ^ up[3];
break;
case FSID_UUID16:
/* 16 byte fsid - NFSv3+ only */
memcpy(fsidv, uuid, 16);
break;
case FSID_UUID16_INUM:
/* 8 byte inode and 16 byte fsid */
*(u64*)fsidv = (u64)ino;
memcpy(fsidv+2, uuid, 16);
break;
default: BUG();
}
}
static inline int key_len(int type)
{
switch(type) {
case FSID_DEV: return 8;
case FSID_NUM: return 4;
case FSID_MAJOR_MINOR: return 12;
case FSID_ENCODE_DEV: return 8;
case FSID_UUID4_INUM: return 8;
case FSID_UUID8: return 8;
case FSID_UUID16: return 16;
case FSID_UUID16_INUM: return 24;
default: return 0;
}
}
/*
* Shorthand for dprintk()'s
*/
extern char * SVCFH_fmt(struct svc_fh *fhp);
/*
* Function prototypes
*/
__be32 fh_verify(struct svc_rqst *, struct svc_fh *, umode_t, int);
__be32 fh_compose(struct svc_fh *, struct svc_export *, struct dentry *, struct svc_fh *);
__be32 fh_update(struct svc_fh *);
void fh_put(struct svc_fh *);
static __inline__ struct svc_fh *
fh_copy(struct svc_fh *dst, struct svc_fh *src)
{
WARN_ON(src->fh_dentry || src->fh_locked);
*dst = *src;
return dst;
}
static inline void
fh_copy_shallow(struct knfsd_fh *dst, struct knfsd_fh *src)
{
dst->fh_size = src->fh_size;
memcpy(&dst->fh_base, &src->fh_base, src->fh_size);
}
static __inline__ struct svc_fh *
fh_init(struct svc_fh *fhp, int maxsize)
{
memset(fhp, 0, sizeof(*fhp));
fhp->fh_maxsize = maxsize;
return fhp;
}
#ifdef CONFIG_NFSD_V3
/*
* The wcc data stored in current_fh should be cleared
* between compound ops.
*/
static inline void
fh_clear_wcc(struct svc_fh *fhp)
{
fhp->fh_post_saved = 0;
fhp->fh_pre_saved = 0;
}
/*
* Fill in the pre_op attr for the wcc data
*/
static inline void
fill_pre_wcc(struct svc_fh *fhp)
{
struct inode *inode;
inode = fhp->fh_dentry->d_inode;
if (!fhp->fh_pre_saved) {
fhp->fh_pre_mtime = inode->i_mtime;
fhp->fh_pre_ctime = inode->i_ctime;
fhp->fh_pre_size = inode->i_size;
fhp->fh_pre_change = inode->i_version;
fhp->fh_pre_saved = 1;
}
}
extern void fill_post_wcc(struct svc_fh *);
#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 = dentry->d_inode;
mutex_lock_nested(&inode->i_mutex, subclass);
fill_pre_wcc(fhp);
fhp->fh_locked = 1;
}
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);
mutex_unlock(&fhp->fh_dentry->d_inode->i_mutex);
fhp->fh_locked = 0;
}
}
#endif /* _LINUX_NFSD_NFSFH_H */

759
fs/nfsd/nfsproc.c Normal file
View file

@ -0,0 +1,759 @@
/*
* Process version 2 NFS requests.
*
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/namei.h>
#include "cache.h"
#include "xdr.h"
#include "vfs.h"
typedef struct svc_rqst svc_rqst;
typedef struct svc_buf svc_buf;
#define NFSDDBG_FACILITY NFSDDBG_PROC
static __be32
nfsd_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
return nfs_ok;
}
static __be32
nfsd_return_attrs(__be32 err, struct nfsd_attrstat *resp)
{
if (err) return err;
return fh_getattr(&resp->fh, &resp->stat);
}
static __be32
nfsd_return_dirop(__be32 err, struct nfsd_diropres *resp)
{
if (err) return err;
return fh_getattr(&resp->fh, &resp->stat);
}
/*
* Get a file's attributes
* N.B. After this call resp->fh needs an fh_put
*/
static __be32
nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
struct nfsd_attrstat *resp)
{
__be32 nfserr;
dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0,
NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
return nfsd_return_attrs(nfserr, resp);
}
/*
* Set a file's attributes
* N.B. After this call resp->fh needs an fh_put
*/
static __be32
nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
struct nfsd_attrstat *resp)
{
__be32 nfserr;
dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
SVCFH_fmt(&argp->fh),
argp->attrs.ia_valid, (long) argp->attrs.ia_size);
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,0, (time_t)0);
return nfsd_return_attrs(nfserr, resp);
}
/*
* Look up a path name component
* Note: the dentry in the resp->fh may be negative if the file
* doesn't exist yet.
* N.B. After this call resp->fh needs an fh_put
*/
static __be32
nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
struct nfsd_diropres *resp)
{
__be32 nfserr;
dprintk("nfsd: LOOKUP %s %.*s\n",
SVCFH_fmt(&argp->fh), argp->len, argp->name);
fh_init(&resp->fh, NFS_FHSIZE);
nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
&resp->fh);
fh_put(&argp->fh);
return nfsd_return_dirop(nfserr, resp);
}
/*
* Read a symlink.
*/
static __be32
nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_readlinkargs *argp,
struct nfsd_readlinkres *resp)
{
__be32 nfserr;
dprintk("nfsd: READLINK %s\n", SVCFH_fmt(&argp->fh));
/* Read the symlink. */
resp->len = NFS_MAXPATHLEN;
nfserr = nfsd_readlink(rqstp, &argp->fh, argp->buffer, &resp->len);
fh_put(&argp->fh);
return nfserr;
}
/*
* Read a portion of a file.
* N.B. After this call resp->fh needs an fh_put
*/
static __be32
nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
struct nfsd_readres *resp)
{
__be32 nfserr;
dprintk("nfsd: READ %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh),
argp->count, argp->offset);
/* Obtain buffer pointer for payload. 19 is 1 word for
* 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);
resp->count = argp->count;
nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
argp->offset,
rqstp->rq_vec, argp->vlen,
&resp->count);
if (nfserr) return nfserr;
return fh_getattr(&resp->fh, &resp->stat);
}
/*
* Write data to a file
* N.B. After this call resp->fh needs an fh_put
*/
static __be32
nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
struct nfsd_attrstat *resp)
{
__be32 nfserr;
int stable = 1;
unsigned long cnt = argp->len;
dprintk("nfsd: WRITE %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh),
argp->len, argp->offset);
nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL,
argp->offset,
rqstp->rq_vec, argp->vlen,
&cnt,
&stable);
return nfsd_return_attrs(nfserr, resp);
}
/*
* CREATE processing is complicated. The keyword here is `overloaded.'
* The parent directory is kept locked between the check for existence
* and the actual create() call in compliance with VFS protocols.
* N.B. After this call _both_ argp->fh and resp->fh need an fh_put
*/
static __be32
nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
struct nfsd_diropres *resp)
{
svc_fh *dirfhp = &argp->fh;
svc_fh *newfhp = &resp->fh;
struct iattr *attr = &argp->attrs;
struct inode *inode;
struct dentry *dchild;
int type, mode;
__be32 nfserr;
int hosterr;
dev_t rdev = 0, wanted = new_decode_dev(attr->ia_size);
dprintk("nfsd: CREATE %s %.*s\n",
SVCFH_fmt(dirfhp), argp->len, argp->name);
/* First verify the parent file handle */
nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC);
if (nfserr)
goto done; /* must fh_put dirfhp even on error */
/* Check for NFSD_MAY_WRITE in nfsd_create if necessary */
nfserr = nfserr_acces;
if (!argp->len)
goto done;
nfserr = nfserr_exist;
if (isdotent(argp->name, argp->len))
goto done;
hosterr = fh_want_write(dirfhp);
if (hosterr) {
nfserr = nfserrno(hosterr);
goto done;
}
fh_lock_nested(dirfhp, I_MUTEX_PARENT);
dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
if (IS_ERR(dchild)) {
nfserr = nfserrno(PTR_ERR(dchild));
goto out_unlock;
}
fh_init(newfhp, NFS_FHSIZE);
nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
if (!nfserr && !dchild->d_inode)
nfserr = nfserr_noent;
dput(dchild);
if (nfserr) {
if (nfserr != nfserr_noent)
goto out_unlock;
/*
* If the new file handle wasn't verified, we can't tell
* whether the file exists or not. Time to bail ...
*/
nfserr = nfserr_acces;
if (!newfhp->fh_dentry) {
printk(KERN_WARNING
"nfsd_proc_create: file handle not verified\n");
goto out_unlock;
}
}
inode = newfhp->fh_dentry->d_inode;
/* Unfudge the mode bits */
if (attr->ia_valid & ATTR_MODE) {
type = attr->ia_mode & S_IFMT;
mode = attr->ia_mode & ~S_IFMT;
if (!type) {
/* no type, so if target exists, assume same as that,
* else assume a file */
if (inode) {
type = inode->i_mode & S_IFMT;
switch(type) {
case S_IFCHR:
case S_IFBLK:
/* reserve rdev for later checking */
rdev = inode->i_rdev;
attr->ia_valid |= ATTR_SIZE;
/* FALLTHROUGH */
case S_IFIFO:
/* this is probably a permission check..
* at least IRIX implements perm checking on
* echo thing > device-special-file-or-pipe
* by doing a CREATE with type==0
*/
nfserr = nfsd_permission(rqstp,
newfhp->fh_export,
newfhp->fh_dentry,
NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS);
if (nfserr && nfserr != nfserr_rofs)
goto out_unlock;
}
} else
type = S_IFREG;
}
} else if (inode) {
type = inode->i_mode & S_IFMT;
mode = inode->i_mode & ~S_IFMT;
} else {
type = S_IFREG;
mode = 0; /* ??? */
}
attr->ia_valid |= ATTR_MODE;
attr->ia_mode = mode;
/* Special treatment for non-regular files according to the
* gospel of sun micro
*/
if (type != S_IFREG) {
if (type != S_IFBLK && type != S_IFCHR) {
rdev = 0;
} else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) {
/* If you think you've seen the worst, grok this. */
type = S_IFIFO;
} else {
/* Okay, char or block special */
if (!rdev)
rdev = wanted;
}
/* we've used the SIZE information, so discard it */
attr->ia_valid &= ~ATTR_SIZE;
/* Make sure the type and device matches */
nfserr = nfserr_exist;
if (inode && type != (inode->i_mode & S_IFMT))
goto out_unlock;
}
nfserr = 0;
if (!inode) {
/* File doesn't exist. Create it and set attrs */
nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
attr, type, rdev, newfhp);
} else if (type == S_IFREG) {
dprintk("nfsd: existing %s, valid=%x, size=%ld\n",
argp->name, attr->ia_valid, (long) attr->ia_size);
/* File already exists. We ignore all attributes except
* size, so that creat() behaves exactly like
* open(..., O_CREAT|O_TRUNC|O_WRONLY).
*/
attr->ia_valid &= ATTR_SIZE;
if (attr->ia_valid)
nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time_t)0);
}
out_unlock:
/* We don't really need to unlock, as fh_put does it. */
fh_unlock(dirfhp);
fh_drop_write(dirfhp);
done:
fh_put(dirfhp);
return nfsd_return_dirop(nfserr, resp);
}
static __be32
nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
void *resp)
{
__be32 nfserr;
dprintk("nfsd: REMOVE %s %.*s\n", SVCFH_fmt(&argp->fh),
argp->len, argp->name);
/* Unlink. -SIFDIR means file must not be a directory */
nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
fh_put(&argp->fh);
return nfserr;
}
static __be32
nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp,
void *resp)
{
__be32 nfserr;
dprintk("nfsd: RENAME %s %.*s -> \n",
SVCFH_fmt(&argp->ffh), argp->flen, argp->fname);
dprintk("nfsd: -> %s %.*s\n",
SVCFH_fmt(&argp->tfh), argp->tlen, argp->tname);
nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
&argp->tfh, argp->tname, argp->tlen);
fh_put(&argp->ffh);
fh_put(&argp->tfh);
return nfserr;
}
static __be32
nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp,
void *resp)
{
__be32 nfserr;
dprintk("nfsd: LINK %s ->\n",
SVCFH_fmt(&argp->ffh));
dprintk("nfsd: %s %.*s\n",
SVCFH_fmt(&argp->tfh),
argp->tlen,
argp->tname);
nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
&argp->ffh);
fh_put(&argp->ffh);
fh_put(&argp->tfh);
return nfserr;
}
static __be32
nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
void *resp)
{
struct svc_fh newfh;
__be32 nfserr;
dprintk("nfsd: SYMLINK %s %.*s -> %.*s\n",
SVCFH_fmt(&argp->ffh), argp->flen, argp->fname,
argp->tlen, argp->tname);
fh_init(&newfh, NFS_FHSIZE);
/*
* Crazy hack: the request fits in a page, and already-decoded
* attributes follow argp->tname, so it's safe to just write a
* null to ensure it's null-terminated:
*/
argp->tname[argp->tlen] = '\0';
nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
argp->tname, &newfh);
fh_put(&argp->ffh);
fh_put(&newfh);
return nfserr;
}
/*
* Make directory. This operation is not idempotent.
* N.B. After this call resp->fh needs an fh_put
*/
static __be32
nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
struct nfsd_diropres *resp)
{
__be32 nfserr;
dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
if (resp->fh.fh_dentry) {
printk(KERN_WARNING
"nfsd_proc_mkdir: response already verified??\n");
}
argp->attrs.ia_valid &= ~ATTR_SIZE;
fh_init(&resp->fh, NFS_FHSIZE);
nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
&argp->attrs, S_IFDIR, 0, &resp->fh);
fh_put(&argp->fh);
return nfsd_return_dirop(nfserr, resp);
}
/*
* Remove a directory
*/
static __be32
nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
void *resp)
{
__be32 nfserr;
dprintk("nfsd: RMDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
fh_put(&argp->fh);
return nfserr;
}
/*
* Read a portion of a directory.
*/
static __be32
nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
struct nfsd_readdirres *resp)
{
int count;
__be32 nfserr;
loff_t offset;
dprintk("nfsd: READDIR %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh),
argp->count, argp->cookie);
/* 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;
/* Read directory and encode entries on the fly */
offset = argp->cookie;
nfserr = nfsd_readdir(rqstp, &argp->fh, &offset,
&resp->common, nfssvc_encode_entry);
resp->count = resp->buffer - argp->buffer;
if (resp->offset)
*resp->offset = htonl(offset);
fh_put(&argp->fh);
return nfserr;
}
/*
* Get file system info
*/
static __be32
nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
struct nfsd_statfsres *resp)
{
__be32 nfserr;
dprintk("nfsd: STATFS %s\n", SVCFH_fmt(&argp->fh));
nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats,
NFSD_MAY_BYPASS_GSS_ON_ROOT);
fh_put(&argp->fh);
return nfserr;
}
/*
* NFSv2 Server procedures.
* Only the results of non-idempotent operations are cached.
*/
struct nfsd_void { int dummy; };
#define ST 1 /* status */
#define FH 8 /* filehandle */
#define AT 18 /* attributes */
static struct svc_procedure nfsd_procedures2[18] = {
[NFSPROC_NULL] = {
.pc_func = (svc_procfunc) nfsd_proc_null,
.pc_decode = (kxdrproc_t) nfssvc_decode_void,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_void),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST,
},
[NFSPROC_GETATTR] = {
.pc_func = (svc_procfunc) nfsd_proc_getattr,
.pc_decode = (kxdrproc_t) nfssvc_decode_fhandle,
.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT,
},
[NFSPROC_SETATTR] = {
.pc_func = (svc_procfunc) nfsd_proc_setattr,
.pc_decode = (kxdrproc_t) nfssvc_decode_sattrargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_sattrargs),
.pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+AT,
},
[NFSPROC_ROOT] = {
.pc_decode = (kxdrproc_t) nfssvc_decode_void,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_void),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST,
},
[NFSPROC_LOOKUP] = {
.pc_func = (svc_procfunc) nfsd_proc_lookup,
.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_diropargs),
.pc_ressize = sizeof(struct nfsd_diropres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+FH+AT,
},
[NFSPROC_READLINK] = {
.pc_func = (svc_procfunc) nfsd_proc_readlink,
.pc_decode = (kxdrproc_t) nfssvc_decode_readlinkargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_readlinkres,
.pc_argsize = sizeof(struct nfsd_readlinkargs),
.pc_ressize = sizeof(struct nfsd_readlinkres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+1+NFS_MAXPATHLEN/4,
},
[NFSPROC_READ] = {
.pc_func = (svc_procfunc) nfsd_proc_read,
.pc_decode = (kxdrproc_t) nfssvc_decode_readargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_readres,
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_readargs),
.pc_ressize = sizeof(struct nfsd_readres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4,
},
[NFSPROC_WRITECACHE] = {
.pc_decode = (kxdrproc_t) nfssvc_decode_void,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_void),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST,
},
[NFSPROC_WRITE] = {
.pc_func = (svc_procfunc) nfsd_proc_write,
.pc_decode = (kxdrproc_t) nfssvc_decode_writeargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_writeargs),
.pc_ressize = sizeof(struct nfsd_attrstat),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+AT,
},
[NFSPROC_CREATE] = {
.pc_func = (svc_procfunc) nfsd_proc_create,
.pc_decode = (kxdrproc_t) nfssvc_decode_createargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_createargs),
.pc_ressize = sizeof(struct nfsd_diropres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+FH+AT,
},
[NFSPROC_REMOVE] = {
.pc_func = (svc_procfunc) nfsd_proc_remove,
.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_diropargs),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST,
},
[NFSPROC_RENAME] = {
.pc_func = (svc_procfunc) nfsd_proc_rename,
.pc_decode = (kxdrproc_t) nfssvc_decode_renameargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_renameargs),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST,
},
[NFSPROC_LINK] = {
.pc_func = (svc_procfunc) nfsd_proc_link,
.pc_decode = (kxdrproc_t) nfssvc_decode_linkargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_linkargs),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST,
},
[NFSPROC_SYMLINK] = {
.pc_func = (svc_procfunc) nfsd_proc_symlink,
.pc_decode = (kxdrproc_t) nfssvc_decode_symlinkargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_symlinkargs),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST,
},
[NFSPROC_MKDIR] = {
.pc_func = (svc_procfunc) nfsd_proc_mkdir,
.pc_decode = (kxdrproc_t) nfssvc_decode_createargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
.pc_argsize = sizeof(struct nfsd_createargs),
.pc_ressize = sizeof(struct nfsd_diropres),
.pc_cachetype = RC_REPLBUFF,
.pc_xdrressize = ST+FH+AT,
},
[NFSPROC_RMDIR] = {
.pc_func = (svc_procfunc) nfsd_proc_rmdir,
.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
.pc_argsize = sizeof(struct nfsd_diropargs),
.pc_ressize = sizeof(struct nfsd_void),
.pc_cachetype = RC_REPLSTAT,
.pc_xdrressize = ST,
},
[NFSPROC_READDIR] = {
.pc_func = (svc_procfunc) nfsd_proc_readdir,
.pc_decode = (kxdrproc_t) nfssvc_decode_readdirargs,
.pc_encode = (kxdrproc_t) nfssvc_encode_readdirres,
.pc_argsize = sizeof(struct nfsd_readdirargs),
.pc_ressize = sizeof(struct nfsd_readdirres),
.pc_cachetype = RC_NOCACHE,
},
[NFSPROC_STATFS] = {
.pc_func = (svc_procfunc) nfsd_proc_statfs,
.pc_decode = (kxdrproc_t) nfssvc_decode_fhandle,
.pc_encode = (kxdrproc_t) nfssvc_encode_statfsres,
.pc_argsize = sizeof(struct nfsd_fhandle),
.pc_ressize = sizeof(struct nfsd_statfsres),
.pc_cachetype = RC_NOCACHE,
.pc_xdrressize = ST+5,
},
};
struct svc_version nfsd_version2 = {
.vs_vers = 2,
.vs_nproc = 18,
.vs_proc = nfsd_procedures2,
.vs_dispatch = nfsd_dispatch,
.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 },
};
int i;
for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
if (nfs_errtbl[i].syserr == errno)
return nfs_errtbl[i].nfserr;
}
WARN(1, "nfsd: non-standard errno: %d\n", errno);
return nfserr_io;
}

751
fs/nfsd/nfssvc.c Normal file
View file

@ -0,0 +1,751 @@
/*
* Central processing for nfsd.
*
* Authors: Olaf Kirch (okir@monad.swb.de)
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/sched.h>
#include <linux/freezer.h>
#include <linux/module.h>
#include <linux/fs_struct.h>
#include <linux/swap.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/lockd/bind.h>
#include <linux/nfsacl.h>
#include <linux/seq_file.h>
#include <net/net_namespace.h>
#include "nfsd.h"
#include "cache.h"
#include "vfs.h"
#include "netns.h"
#define NFSDDBG_FACILITY NFSDDBG_SVC
extern struct svc_program nfsd_program;
static int nfsd(void *vrqstp);
/*
* nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members
* 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
* properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number
* of nfsd threads must exist and each must listed in ->sp_all_threads in each
* entry of ->sv_pools[].
*
* Transitions of the thread count between zero and non-zero are of particular
* 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
* accessed when nfsd starts and that are settable via the write_* routines in
* nfsctl.c. In particular:
*
* user_recovery_dirname
* user_lease_time
* nfsd_versions
*/
DEFINE_MUTEX(nfsd_mutex);
/*
* nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used.
* nfsd_drc_max_pages limits the total amount of memory available for
* version 4.1 DRC caches.
* nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
*/
spinlock_t nfsd_drc_lock;
unsigned long nfsd_drc_max_mem;
unsigned long nfsd_drc_mem_used;
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
static struct svc_stat nfsd_acl_svcstats;
static struct svc_version * nfsd_acl_version[] = {
[2] = &nfsd_acl_version2,
[3] = &nfsd_acl_version3,
};
#define NFSD_ACL_MINVERS 2
#define NFSD_ACL_NRVERS ARRAY_SIZE(nfsd_acl_version)
static struct svc_version *nfsd_acl_versions[NFSD_ACL_NRVERS];
static struct svc_program nfsd_acl_program = {
.pg_prog = NFS_ACL_PROGRAM,
.pg_nvers = NFSD_ACL_NRVERS,
.pg_vers = nfsd_acl_versions,
.pg_name = "nfsacl",
.pg_class = "nfsd",
.pg_stats = &nfsd_acl_svcstats,
.pg_authenticate = &svc_set_client,
};
static struct svc_stat nfsd_acl_svcstats = {
.program = &nfsd_acl_program,
};
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
static struct svc_version * nfsd_version[] = {
[2] = &nfsd_version2,
#if defined(CONFIG_NFSD_V3)
[3] = &nfsd_version3,
#endif
#if defined(CONFIG_NFSD_V4)
[4] = &nfsd_version4,
#endif
};
#define NFSD_MINVERS 2
#define NFSD_NRVERS ARRAY_SIZE(nfsd_version)
static struct svc_version *nfsd_versions[NFSD_NRVERS];
struct svc_program nfsd_program = {
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
.pg_next = &nfsd_acl_program,
#endif
.pg_prog = NFS_PROGRAM, /* program number */
.pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */
.pg_vers = nfsd_versions, /* version table */
.pg_name = "nfsd", /* program name */
.pg_class = "nfsd", /* authentication class */
.pg_stats = &nfsd_svcstats, /* version table */
.pg_authenticate = &svc_set_client, /* export authentication */
};
static bool nfsd_supported_minorversions[NFSD_SUPPORTED_MINOR_VERSION + 1] = {
[0] = 1,
[1] = 1,
};
int nfsd_vers(int vers, enum vers_op change)
{
if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS)
return 0;
switch(change) {
case NFSD_SET:
nfsd_versions[vers] = nfsd_version[vers];
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
if (vers < NFSD_ACL_NRVERS)
nfsd_acl_versions[vers] = nfsd_acl_version[vers];
#endif
break;
case NFSD_CLEAR:
nfsd_versions[vers] = NULL;
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
if (vers < NFSD_ACL_NRVERS)
nfsd_acl_versions[vers] = NULL;
#endif
break;
case NFSD_TEST:
return nfsd_versions[vers] != NULL;
case NFSD_AVAIL:
return nfsd_version[vers] != NULL;
}
return 0;
}
int nfsd_minorversion(u32 minorversion, enum vers_op change)
{
if (minorversion > NFSD_SUPPORTED_MINOR_VERSION)
return -1;
switch(change) {
case NFSD_SET:
nfsd_supported_minorversions[minorversion] = true;
break;
case NFSD_CLEAR:
nfsd_supported_minorversions[minorversion] = false;
break;
case NFSD_TEST:
return nfsd_supported_minorversions[minorversion];
case NFSD_AVAIL:
return minorversion <= NFSD_SUPPORTED_MINOR_VERSION;
}
return 0;
}
/*
* Maximum number of nfsd processes
*/
#define NFSD_MAXSERVS 8192
int nfsd_nrthreads(struct net *net)
{
int rv = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
mutex_lock(&nfsd_mutex);
if (nn->nfsd_serv)
rv = nn->nfsd_serv->sv_nrthreads;
mutex_unlock(&nfsd_mutex);
return rv;
}
static int nfsd_init_socks(struct net *net)
{
int error;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (!list_empty(&nn->nfsd_serv->sv_permsocks))
return 0;
error = svc_create_xprt(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
SVC_SOCK_DEFAULTS);
if (error < 0)
return error;
error = svc_create_xprt(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
SVC_SOCK_DEFAULTS);
if (error < 0)
return error;
return 0;
}
static int nfsd_users = 0;
static int nfsd_startup_generic(int nrservs)
{
int ret;
if (nfsd_users++)
return 0;
/*
* Readahead param cache - will no-op if it already exists.
* (Note therefore results will be suboptimal if number of
* threads is modified after nfsd start.)
*/
ret = nfsd_racache_init(2*nrservs);
if (ret)
goto dec_users;
ret = nfs4_state_start();
if (ret)
goto out_racache;
return 0;
out_racache:
nfsd_racache_shutdown();
dec_users:
nfsd_users--;
return ret;
}
static void nfsd_shutdown_generic(void)
{
if (--nfsd_users)
return;
nfs4_state_shutdown();
nfsd_racache_shutdown();
}
static bool nfsd_needs_lockd(void)
{
#if defined(CONFIG_NFSD_V3)
return (nfsd_versions[2] != NULL) || (nfsd_versions[3] != NULL);
#else
return (nfsd_versions[2] != NULL);
#endif
}
static int nfsd_startup_net(int nrservs, struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
int ret;
if (nn->nfsd_net_up)
return 0;
ret = nfsd_startup_generic(nrservs);
if (ret)
return ret;
ret = nfsd_init_socks(net);
if (ret)
goto out_socks;
if (nfsd_needs_lockd() && !nn->lockd_up) {
ret = lockd_up(net);
if (ret)
goto out_socks;
nn->lockd_up = 1;
}
ret = nfs4_state_start_net(net);
if (ret)
goto out_lockd;
nn->nfsd_net_up = true;
return 0;
out_lockd:
if (nn->lockd_up) {
lockd_down(net);
nn->lockd_up = 0;
}
out_socks:
nfsd_shutdown_generic();
return ret;
}
static void nfsd_shutdown_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nfs4_state_shutdown_net(net);
if (nn->lockd_up) {
lockd_down(net);
nn->lockd_up = 0;
}
nn->nfsd_net_up = false;
nfsd_shutdown_generic();
}
static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
/*
* write_ports can create the server without actually starting
* any threads--if we get shut down before any threads are
* started, then nfsd_last_thread will be run before any of this
* other initialization has been done.
*/
if (!nn->nfsd_net_up)
return;
nfsd_shutdown_net(net);
svc_rpcb_cleanup(serv, net);
printk(KERN_WARNING "nfsd: last server has exited, flushing export "
"cache\n");
nfsd_export_flush(net);
}
void nfsd_reset_versions(void)
{
int found_one = 0;
int i;
for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) {
if (nfsd_program.pg_vers[i])
found_one = 1;
}
if (!found_one) {
for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++)
nfsd_program.pg_vers[i] = nfsd_version[i];
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++)
nfsd_acl_program.pg_vers[i] =
nfsd_acl_version[i];
#endif
}
}
/*
* Each session guarantees a negotiated per slot memory cache for replies
* which in turn consumes memory beyond the v2/v3/v4.0 server. A dedicated
* NFSv4.1 server might want to use more memory for a DRC than a machine
* with mutiple services.
*
* Impose a hard limit on the number of pages for the DRC which varies
* according to the machines free pages. This is of course only a default.
*
* For now this is a #defined shift which could be under admin control
* in the future.
*/
static void set_max_drc(void)
{
#define NFSD_DRC_SIZE_SHIFT 10
nfsd_drc_max_mem = (nr_free_buffer_pages()
>> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
nfsd_drc_mem_used = 0;
spin_lock_init(&nfsd_drc_lock);
dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem);
}
static int nfsd_get_default_max_blksize(void)
{
struct sysinfo i;
unsigned long long target;
unsigned long ret;
si_meminfo(&i);
target = (i.totalram - i.totalhigh) << PAGE_SHIFT;
/*
* Aim for 1/4096 of memory per thread This gives 1MB on 4Gig
* machines, but only uses 32K on 128M machines. Bottom out at
* 8K on 32M and smaller. Of course, this is only a default.
*/
target >>= 12;
ret = NFSSVC_MAXBLKSIZE;
while (ret > target && ret >= 8*1024*2)
ret /= 2;
return ret;
}
int nfsd_create_serv(struct net *net)
{
int error;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
WARN_ON(!mutex_is_locked(&nfsd_mutex));
if (nn->nfsd_serv) {
svc_get(nn->nfsd_serv);
return 0;
}
if (nfsd_max_blksize == 0)
nfsd_max_blksize = nfsd_get_default_max_blksize();
nfsd_reset_versions();
nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
nfsd_last_thread, nfsd, THIS_MODULE);
if (nn->nfsd_serv == NULL)
return -ENOMEM;
nn->nfsd_serv->sv_maxconn = nn->max_connections;
error = svc_bind(nn->nfsd_serv, net);
if (error < 0) {
svc_destroy(nn->nfsd_serv);
return error;
}
set_max_drc();
do_gettimeofday(&nn->nfssvc_boot); /* record boot time */
return 0;
}
int nfsd_nrpools(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (nn->nfsd_serv == NULL)
return 0;
else
return nn->nfsd_serv->sv_nrpools;
}
int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
{
int i = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (nn->nfsd_serv != NULL) {
for (i = 0; i < nn->nfsd_serv->sv_nrpools && i < n; i++)
nthreads[i] = nn->nfsd_serv->sv_pools[i].sp_nrthreads;
}
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)
nn->nfsd_serv = NULL;
}
int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
{
int i = 0;
int tot = 0;
int err = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
WARN_ON(!mutex_is_locked(&nfsd_mutex));
if (nn->nfsd_serv == NULL || n <= 0)
return 0;
if (n > nn->nfsd_serv->sv_nrpools)
n = nn->nfsd_serv->sv_nrpools;
/* enforce a global maximum number of threads */
tot = 0;
for (i = 0; i < n; i++) {
nthreads[i] = min(nthreads[i], NFSD_MAXSERVS);
tot += nthreads[i];
}
if (tot > NFSD_MAXSERVS) {
/* total too large: scale down requested numbers */
for (i = 0; i < n && tot > 0; i++) {
int new = nthreads[i] * NFSD_MAXSERVS / tot;
tot -= (nthreads[i] - new);
nthreads[i] = new;
}
for (i = 0; i < n && tot > 0; i++) {
nthreads[i]--;
tot--;
}
}
/*
* There must always be a thread in pool 0; the admin
* can't shut down NFS completely using pool_threads.
*/
if (nthreads[0] == 0)
nthreads[0] = 1;
/* apply the new numbers */
svc_get(nn->nfsd_serv);
for (i = 0; i < n; i++) {
err = svc_set_num_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i],
nthreads[i]);
if (err)
break;
}
nfsd_destroy(net);
return err;
}
/*
* Adjust the number of threads and return the new number of threads.
* This is also the function that starts the server if necessary, if
* this is the first time nrservs is nonzero.
*/
int
nfsd_svc(int nrservs, struct net *net)
{
int error;
bool nfsd_up_before;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
mutex_lock(&nfsd_mutex);
dprintk("nfsd: creating service\n");
nrservs = max(nrservs, 0);
nrservs = min(nrservs, NFSD_MAXSERVS);
error = 0;
if (nrservs == 0 && nn->nfsd_serv == NULL)
goto out;
error = nfsd_create_serv(net);
if (error)
goto out;
nfsd_up_before = nn->nfsd_net_up;
error = nfsd_startup_net(nrservs, net);
if (error)
goto out_destroy;
error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs);
if (error)
goto out_shutdown;
/* We are holding a reference to nn->nfsd_serv which
* we don't want to count in the return value,
* so subtract 1
*/
error = nn->nfsd_serv->sv_nrthreads - 1;
out_shutdown:
if (error < 0 && !nfsd_up_before)
nfsd_shutdown_net(net);
out_destroy:
nfsd_destroy(net); /* Release server */
out:
mutex_unlock(&nfsd_mutex);
return error;
}
/*
* This is the NFS server kernel thread
*/
static int
nfsd(void *vrqstp)
{
struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list);
struct net *net = perm_sock->xpt_net;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
int err;
/* Lock module and set up kernel thread */
mutex_lock(&nfsd_mutex);
/* At this point, the thread shares current->fs
* with the init process. We need to create files with a
* umask of 0 instead of init's umask. */
if (unshare_fs_struct() < 0) {
printk("Unable to start nfsd thread: out of memory\n");
goto out;
}
current->fs->umask = 0;
/*
* 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();
/*
* The main request loop
*/
for (;;) {
/* Update sv_maxconn if it has changed */
rqstp->rq_server->sv_maxconn = nn->max_connections;
/*
* Find a socket with data available and call its
* recvfrom routine.
*/
while ((err = svc_recv(rqstp, 60*60*HZ)) == -EAGAIN)
;
if (err == -EINTR)
break;
validate_process_creds();
svc_process(rqstp);
validate_process_creds();
}
/* Clear signals before calling svc_exit_thread() */
flush_signals(current);
mutex_lock(&nfsd_mutex);
nfsdstats.th_cnt --;
out:
rqstp->rq_server = NULL;
/* Release the thread */
svc_exit_thread(rqstp);
nfsd_destroy(net);
/* Release module */
mutex_unlock(&nfsd_mutex);
module_put_and_exit(0);
return 0;
}
static __be32 map_new_errors(u32 vers, __be32 nfserr)
{
if (nfserr == nfserr_jukebox && vers == 2)
return nfserr_dropit;
if (nfserr == nfserr_wrongsec && vers < 4)
return nfserr_acces;
return nfserr;
}
int
nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
{
struct svc_procedure *proc;
kxdrproc_t xdr;
__be32 nfserr;
__be32 *nfserrp;
dprintk("nfsd_dispatch: vers %d proc %d\n",
rqstp->rq_vers, rqstp->rq_proc);
proc = rqstp->rq_procinfo;
/*
* Give the xdr decoder a chance to change this if it wants
* (necessary in the NFSv4.0 compound case)
*/
rqstp->rq_cachetype = proc->pc_cachetype;
/* Decode arguments */
xdr = proc->pc_decode;
if (xdr && !xdr(rqstp, (__be32*)rqstp->rq_arg.head[0].iov_base,
rqstp->rq_argp)) {
dprintk("nfsd: failed to decode arguments!\n");
*statp = rpc_garbage_args;
return 1;
}
/* Check whether we have this call in the cache. */
switch (nfsd_cache_lookup(rqstp)) {
case RC_DROPIT:
return 0;
case RC_REPLY:
return 1;
case RC_DOIT:;
/* do it */
}
/* need to grab the location to store the status, as
* nfsv4 does some encoding while processing
*/
nfserrp = rqstp->rq_res.head[0].iov_base
+ rqstp->rq_res.head[0].iov_len;
rqstp->rq_res.head[0].iov_len += sizeof(__be32);
/* Now call the procedure handler, and encode NFS status. */
nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
nfserr = map_new_errors(rqstp->rq_vers, nfserr);
if (nfserr == nfserr_dropit || rqstp->rq_dropme) {
dprintk("nfsd: Dropping request; may be revisited later\n");
nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
return 0;
}
if (rqstp->rq_proc != 0)
*nfserrp++ = nfserr;
/* Encode result.
* For NFSv2, additional info is never returned in case of an error.
*/
if (!(nfserr && rqstp->rq_vers == 2)) {
xdr = proc->pc_encode;
if (xdr && !xdr(rqstp, nfserrp,
rqstp->rq_resp)) {
/* Failed to encode result. Release cache entry */
dprintk("nfsd: failed to encode result!\n");
nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
*statp = rpc_system_err;
return 1;
}
}
/* Store reply in cache. */
nfsd_cache_update(rqstp, rqstp->rq_cachetype, statp + 1);
return 1;
}
int nfsd_pool_stats_open(struct inode *inode, struct file *file)
{
int ret;
struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id);
mutex_lock(&nfsd_mutex);
if (nn->nfsd_serv == NULL) {
mutex_unlock(&nfsd_mutex);
return -ENODEV;
}
/* bump up the psudo refcount while traversing */
svc_get(nn->nfsd_serv);
ret = svc_pool_stats_open(nn->nfsd_serv, file);
mutex_unlock(&nfsd_mutex);
return ret;
}
int nfsd_pool_stats_release(struct inode *inode, struct file *file)
{
int ret = seq_release(inode, file);
struct net *net = inode->i_sb->s_fs_info;
mutex_lock(&nfsd_mutex);
/* this function really, really should have been called svc_put() */
nfsd_destroy(net);
mutex_unlock(&nfsd_mutex);
return ret;
}

550
fs/nfsd/nfsxdr.c Normal file
View file

@ -0,0 +1,550 @@
/*
* XDR support for nfsd
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#include "vfs.h"
#include "xdr.h"
#include "auth.h"
#define NFSDDBG_FACILITY NFSDDBG_XDR
/*
* Mapping of S_IF* types to NFS file types
*/
static u32 nfs_ftypes[] = {
NFNON, NFCHR, NFCHR, NFBAD,
NFDIR, NFBAD, NFBLK, NFBAD,
NFREG, NFBAD, NFLNK, NFBAD,
NFSOCK, NFBAD, NFLNK, NFBAD,
};
/*
* XDR functions for basic NFS types
*/
static __be32 *
decode_fh(__be32 *p, struct svc_fh *fhp)
{
fh_init(fhp, NFS_FHSIZE);
memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE);
fhp->fh_handle.fh_size = NFS_FHSIZE;
/* FIXME: Look up export pointer here and verify
* Sun Secure RPC if requested */
return p + (NFS_FHSIZE >> 2);
}
/* Helper function for NFSv2 ACL code */
__be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp)
{
return decode_fh(p, fhp);
}
static __be32 *
encode_fh(__be32 *p, struct svc_fh *fhp)
{
memcpy(p, &fhp->fh_handle.fh_base, NFS_FHSIZE);
return p + (NFS_FHSIZE>> 2);
}
/*
* Decode a file name and make sure that the path contains
* no slashes or null bytes.
*/
static __be32 *
decode_filename(__be32 *p, char **namp, unsigned int *lenp)
{
char *name;
unsigned int i;
if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) {
for (i = 0, name = *namp; i < *lenp; i++, name++) {
if (*name == '\0' || *name == '/')
return NULL;
}
}
return p;
}
static __be32 *
decode_pathname(__be32 *p, char **namp, unsigned int *lenp)
{
char *name;
unsigned int i;
if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) {
for (i = 0, name = *namp; i < *lenp; i++, name++) {
if (*name == '\0')
return NULL;
}
}
return p;
}
static __be32 *
decode_sattr(__be32 *p, struct iattr *iap)
{
u32 tmp, tmp1;
iap->ia_valid = 0;
/* Sun client bug compatibility check: some sun clients seem to
* put 0xffff in the mode field when they mean 0xffffffff.
* Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah.
*/
if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) {
iap->ia_valid |= ATTR_MODE;
iap->ia_mode = tmp;
}
if ((tmp = ntohl(*p++)) != (u32)-1) {
iap->ia_uid = make_kuid(&init_user_ns, tmp);
if (uid_valid(iap->ia_uid))
iap->ia_valid |= ATTR_UID;
}
if ((tmp = ntohl(*p++)) != (u32)-1) {
iap->ia_gid = make_kgid(&init_user_ns, tmp);
if (gid_valid(iap->ia_gid))
iap->ia_valid |= ATTR_GID;
}
if ((tmp = ntohl(*p++)) != (u32)-1) {
iap->ia_valid |= ATTR_SIZE;
iap->ia_size = tmp;
}
tmp = ntohl(*p++); tmp1 = ntohl(*p++);
if (tmp != (u32)-1 && tmp1 != (u32)-1) {
iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
iap->ia_atime.tv_sec = tmp;
iap->ia_atime.tv_nsec = tmp1 * 1000;
}
tmp = ntohl(*p++); tmp1 = ntohl(*p++);
if (tmp != (u32)-1 && tmp1 != (u32)-1) {
iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
iap->ia_mtime.tv_sec = tmp;
iap->ia_mtime.tv_nsec = tmp1 * 1000;
/*
* Passing the invalid value useconds=1000000 for mtime
* is a Sun convention for "set both mtime and atime to
* current server time". It's needed to make permissions
* checks for the "touch" program across v2 mounts to
* Solaris and Irix boxes work correctly. See description of
* sattr in section 6.1 of "NFS Illustrated" by
* Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
*/
if (tmp1 == 1000000)
iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
}
return p;
}
static __be32 *
encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
struct kstat *stat)
{
struct dentry *dentry = fhp->fh_dentry;
int type;
struct timespec time;
u32 f;
type = (stat->mode & S_IFMT);
*p++ = htonl(nfs_ftypes[type >> 12]);
*p++ = htonl((u32) stat->mode);
*p++ = htonl((u32) stat->nlink);
*p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid));
*p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid));
if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
*p++ = htonl(NFS_MAXPATHLEN);
} else {
*p++ = htonl((u32) stat->size);
}
*p++ = htonl((u32) stat->blksize);
if (S_ISCHR(type) || S_ISBLK(type))
*p++ = htonl(new_encode_dev(stat->rdev));
else
*p++ = htonl(0xffffffff);
*p++ = htonl((u32) stat->blocks);
switch (fsid_source(fhp)) {
default:
case FSIDSOURCE_DEV:
*p++ = htonl(new_encode_dev(stat->dev));
break;
case FSIDSOURCE_FSID:
*p++ = htonl((u32) fhp->fh_export->ex_fsid);
break;
case FSIDSOURCE_UUID:
f = ((u32*)fhp->fh_export->ex_uuid)[0];
f ^= ((u32*)fhp->fh_export->ex_uuid)[1];
f ^= ((u32*)fhp->fh_export->ex_uuid)[2];
f ^= ((u32*)fhp->fh_export->ex_uuid)[3];
*p++ = htonl(f);
break;
}
*p++ = htonl((u32) stat->ino);
*p++ = htonl((u32) stat->atime.tv_sec);
*p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0);
lease_get_mtime(dentry->d_inode, &time);
*p++ = htonl((u32) time.tv_sec);
*p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0);
*p++ = htonl((u32) stat->ctime.tv_sec);
*p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0);
return p;
}
/* Helper function for NFSv2 ACL code */
__be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat)
{
return encode_fattr(rqstp, p, fhp, stat);
}
/*
* XDR decode functions
*/
int
nfssvc_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args)
{
p = decode_fh(p, &args->fh);
if (!p)
return 0;
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_sattrargs *args)
{
p = decode_fh(p, &args->fh);
if (!p)
return 0;
p = decode_sattr(p, &args->attrs);
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_diropargs *args)
{
if (!(p = decode_fh(p, &args->fh))
|| !(p = decode_filename(p, &args->name, &args->len)))
return 0;
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_readargs *args)
{
unsigned int len;
int v;
p = decode_fh(p, &args->fh);
if (!p)
return 0;
args->offset = ntohl(*p++);
len = args->count = ntohl(*p++);
p++; /* totalcount - unused */
len = min_t(unsigned int, len, NFSSVC_MAXBLKSIZE_V2);
/* set up somewhere to store response.
* We take pages, put them on reslist and include in iovec
*/
v=0;
while (len > 0) {
struct page *p = *(rqstp->rq_next_page++);
rqstp->rq_vec[v].iov_base = page_address(p);
rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
len -= rqstp->rq_vec[v].iov_len;
v++;
}
args->vlen = v;
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_writeargs *args)
{
unsigned int len, hdr, dlen;
int v;
p = decode_fh(p, &args->fh);
if (!p)
return 0;
p++; /* beginoffset */
args->offset = ntohl(*p++); /* offset */
p++; /* totalcount */
len = args->len = ntohl(*p++);
/*
* The protocol specifies a maximum of 8192 bytes.
*/
if (len > NFSSVC_MAXBLKSIZE_V2)
return 0;
/*
* Check to make sure that we got the right number of
* bytes.
*/
hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len
- hdr;
/*
* Round the length of the data which was specified up to
* the next multiple of XDR units and then compare that
* against the length which was actually received.
* Note that when RPCSEC/GSS (for example) is used, the
* data buffer can be padded so dlen might be larger
* than required. It must never be smaller.
*/
if (dlen < XDR_QUADLEN(len)*4)
return 0;
rqstp->rq_vec[0].iov_base = (void*)p;
rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
v = 0;
while (len > rqstp->rq_vec[v].iov_len) {
len -= rqstp->rq_vec[v].iov_len;
v++;
rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]);
rqstp->rq_vec[v].iov_len = PAGE_SIZE;
}
rqstp->rq_vec[v].iov_len = len;
args->vlen = v + 1;
return 1;
}
int
nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_createargs *args)
{
if ( !(p = decode_fh(p, &args->fh))
|| !(p = decode_filename(p, &args->name, &args->len)))
return 0;
p = decode_sattr(p, &args->attrs);
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_renameargs *args)
{
if (!(p = decode_fh(p, &args->ffh))
|| !(p = decode_filename(p, &args->fname, &args->flen))
|| !(p = decode_fh(p, &args->tfh))
|| !(p = decode_filename(p, &args->tname, &args->tlen)))
return 0;
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readlinkargs *args)
{
p = decode_fh(p, &args->fh);
if (!p)
return 0;
args->buffer = page_address(*(rqstp->rq_next_page++));
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_linkargs *args)
{
if (!(p = decode_fh(p, &args->ffh))
|| !(p = decode_fh(p, &args->tfh))
|| !(p = decode_filename(p, &args->tname, &args->tlen)))
return 0;
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_symlinkargs *args)
{
if ( !(p = decode_fh(p, &args->ffh))
|| !(p = decode_filename(p, &args->fname, &args->flen))
|| !(p = decode_pathname(p, &args->tname, &args->tlen)))
return 0;
p = decode_sattr(p, &args->attrs);
return xdr_argsize_check(rqstp, p);
}
int
nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_readdirargs *args)
{
p = decode_fh(p, &args->fh);
if (!p)
return 0;
args->cookie = ntohl(*p++);
args->count = ntohl(*p++);
args->count = min_t(u32, args->count, PAGE_SIZE);
args->buffer = page_address(*(rqstp->rq_next_page++));
return xdr_argsize_check(rqstp, p);
}
/*
* XDR encode functions
*/
int
nfssvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
int
nfssvc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_attrstat *resp)
{
p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
return xdr_ressize_check(rqstp, p);
}
int
nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_diropres *resp)
{
p = encode_fh(p, &resp->fh);
p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
return xdr_ressize_check(rqstp, p);
}
int
nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_readlinkres *resp)
{
*p++ = htonl(resp->len);
xdr_ressize_check(rqstp, p);
rqstp->rq_res.page_len = resp->len;
if (resp->len & 3) {
/* need to pad the tail */
rqstp->rq_res.tail[0].iov_base = p;
*p = 0;
rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
}
return 1;
}
int
nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_readres *resp)
{
p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
*p++ = htonl(resp->count);
xdr_ressize_check(rqstp, p);
/* now update rqstp->rq_res to reflect data as well */
rqstp->rq_res.page_len = resp->count;
if (resp->count & 3) {
/* need to pad the tail */
rqstp->rq_res.tail[0].iov_base = p;
*p = 0;
rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3);
}
return 1;
}
int
nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_readdirres *resp)
{
xdr_ressize_check(rqstp, p);
p = resp->buffer;
*p++ = 0; /* no more entries */
*p++ = htonl((resp->common.err == nfserr_eof));
rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1;
return 1;
}
int
nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_statfsres *resp)
{
struct kstatfs *stat = &resp->stats;
*p++ = htonl(NFSSVC_MAXBLKSIZE_V2); /* max transfer size */
*p++ = htonl(stat->f_bsize);
*p++ = htonl(stat->f_blocks);
*p++ = htonl(stat->f_bfree);
*p++ = htonl(stat->f_bavail);
return xdr_ressize_check(rqstp, p);
}
int
nfssvc_encode_entry(void *ccdv, const char *name,
int namlen, loff_t offset, u64 ino, unsigned int d_type)
{
struct readdir_cd *ccd = ccdv;
struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common);
__be32 *p = cd->buffer;
int buflen, slen;
/*
dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
namlen, name, offset, ino);
*/
if (offset > ~((u32) 0)) {
cd->common.err = nfserr_fbig;
return -EINVAL;
}
if (cd->offset)
*cd->offset = htonl(offset);
/* truncate filename */
namlen = min(namlen, NFS2_MAXNAMLEN);
slen = XDR_QUADLEN(namlen);
if ((buflen = cd->buflen - slen - 4) < 0) {
cd->common.err = nfserr_toosmall;
return -EINVAL;
}
if (ino > ~((u32) 0)) {
cd->common.err = nfserr_fbig;
return -EINVAL;
}
*p++ = xdr_one; /* mark entry present */
*p++ = htonl((u32) ino); /* file id */
p = xdr_encode_array(p, name, namlen);/* name length & name */
cd->offset = p; /* remember pointer */
*p++ = htonl(~0U); /* offset of next entry */
cd->buflen = buflen;
cd->buffer = p;
cd->common.err = nfs_ok;
return 0;
}
/*
* XDR release functions
*/
int
nfssvc_release_fhandle(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_fhandle *resp)
{
fh_put(&resp->fh);
return 1;
}

601
fs/nfsd/state.h Normal file
View file

@ -0,0 +1,601 @@
/*
* Copyright (c) 2001 The Regents of the University of Michigan.
* All rights reserved.
*
* Kendrick Smith <kmsmith@umich.edu>
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _NFSD4_STATE_H
#define _NFSD4_STATE_H
#include <linux/idr.h>
#include <linux/sunrpc/svc_xprt.h>
#include "nfsfh.h"
typedef struct {
u32 cl_boot;
u32 cl_id;
} clientid_t;
typedef struct {
clientid_t so_clid;
u32 so_id;
} stateid_opaque_t;
typedef struct {
u32 si_generation;
stateid_opaque_t si_opaque;
} stateid_t;
#define STATEID_FMT "(%08x/%08x/%08x/%08x)"
#define STATEID_VAL(s) \
(s)->si_opaque.so_clid.cl_boot, \
(s)->si_opaque.so_clid.cl_id, \
(s)->si_opaque.so_id, \
(s)->si_generation
struct nfsd4_callback {
struct nfs4_client *cb_clp;
struct list_head cb_per_client;
u32 cb_minorversion;
struct rpc_message cb_msg;
struct nfsd4_callback_ops *cb_ops;
struct work_struct cb_work;
bool cb_done;
};
struct nfsd4_callback_ops {
void (*prepare)(struct nfsd4_callback *);
int (*done)(struct nfsd4_callback *, struct rpc_task *);
void (*release)(struct nfsd4_callback *);
};
/*
* A core object that represents a "common" stateid. These are generally
* embedded within the different (more specific) stateid objects and contain
* fields that are of general use to any stateid.
*/
struct nfs4_stid {
atomic_t sc_count;
#define NFS4_OPEN_STID 1
#define NFS4_LOCK_STID 2
#define NFS4_DELEG_STID 4
/* For an open stateid kept around *only* to process close replays: */
#define NFS4_CLOSED_STID 8
/* For a deleg stateid kept around only to process free_stateid's: */
#define NFS4_REVOKED_DELEG_STID 16
#define NFS4_CLOSED_DELEG_STID 32
unsigned char sc_type;
stateid_t sc_stateid;
struct nfs4_client *sc_client;
struct nfs4_file *sc_file;
void (*sc_free)(struct nfs4_stid *);
};
/*
* Represents a delegation stateid. The nfs4_client holds references to these
* and they are put when it is being destroyed or when the delegation is
* returned by the client:
*
* o 1 reference as long as a delegation is still in force (taken when it's
* alloc'd, put when it's returned or revoked)
*
* o 1 reference as long as a recall rpc is in progress (taken when the lease
* is broken, put when the rpc exits)
*
* o 1 more ephemeral reference for each nfsd thread currently doing something
* with that delegation without holding the cl_lock
*
* If the server attempts to recall a delegation and the client doesn't do so
* before a timeout, the server may also revoke the delegation. In that case,
* the object will either be destroyed (v4.0) or moved to a per-client list of
* revoked delegations (v4.1+).
*
* This object is a superset of the nfs4_stid.
*/
struct nfs4_delegation {
struct nfs4_stid dl_stid; /* must be first field */
struct list_head dl_perfile;
struct list_head dl_perclnt;
struct list_head dl_recall_lru; /* delegation recalled */
u32 dl_type;
time_t dl_time;
/* For recall: */
int dl_retries;
struct nfsd4_callback dl_recall;
};
#define cb_to_delegation(cb) \
container_of(cb, struct nfs4_delegation, dl_recall)
/* client delegation callback info */
struct nfs4_cb_conn {
/* SETCLIENTID info */
struct sockaddr_storage cb_addr;
struct sockaddr_storage cb_saddr;
size_t cb_addrlen;
u32 cb_prog; /* used only in 4.0 case;
per-session otherwise */
u32 cb_ident; /* minorversion 0 only */
struct svc_xprt *cb_xprt; /* minorversion 1 only */
};
static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
{
return container_of(s, struct nfs4_delegation, dl_stid);
}
/* Maximum number of slots per session. 160 is useful for long haul TCP */
#define NFSD_MAX_SLOTS_PER_SESSION 160
/* Maximum number of operations per session compound */
#define NFSD_MAX_OPS_PER_COMPOUND 16
/* Maximum session per slot cache size */
#define NFSD_SLOT_CACHE_SIZE 2048
/* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */
#define NFSD_CACHE_SIZE_SLOTS_PER_SESSION 32
#define NFSD_MAX_MEM_PER_SESSION \
(NFSD_CACHE_SIZE_SLOTS_PER_SESSION * NFSD_SLOT_CACHE_SIZE)
struct nfsd4_slot {
u32 sl_seqid;
__be32 sl_status;
u32 sl_datalen;
u16 sl_opcnt;
#define NFSD4_SLOT_INUSE (1 << 0)
#define NFSD4_SLOT_CACHETHIS (1 << 1)
#define NFSD4_SLOT_INITIALIZED (1 << 2)
u8 sl_flags;
char sl_data[];
};
struct nfsd4_channel_attrs {
u32 headerpadsz;
u32 maxreq_sz;
u32 maxresp_sz;
u32 maxresp_cached;
u32 maxops;
u32 maxreqs;
u32 nr_rdma_attrs;
u32 rdma_attrs;
};
struct nfsd4_cb_sec {
u32 flavor; /* (u32)(-1) used to mean "no valid flavor" */
kuid_t uid;
kgid_t gid;
};
struct nfsd4_create_session {
clientid_t clientid;
struct nfs4_sessionid sessionid;
u32 seqid;
u32 flags;
struct nfsd4_channel_attrs fore_channel;
struct nfsd4_channel_attrs back_channel;
u32 callback_prog;
struct nfsd4_cb_sec cb_sec;
};
struct nfsd4_backchannel_ctl {
u32 bc_cb_program;
struct nfsd4_cb_sec bc_cb_sec;
};
struct nfsd4_bind_conn_to_session {
struct nfs4_sessionid sessionid;
u32 dir;
};
/* The single slot clientid cache structure */
struct nfsd4_clid_slot {
u32 sl_seqid;
__be32 sl_status;
struct nfsd4_create_session sl_cr_ses;
};
struct nfsd4_conn {
struct list_head cn_persession;
struct svc_xprt *cn_xprt;
struct svc_xpt_user cn_xpt_user;
struct nfsd4_session *cn_session;
/* CDFC4_FORE, CDFC4_BACK: */
unsigned char cn_flags;
};
/*
* Representation of a v4.1+ session. These are refcounted in a similar fashion
* to the nfs4_client. References are only taken when the server is actively
* working on the object (primarily during the processing of compounds).
*/
struct nfsd4_session {
atomic_t se_ref;
struct list_head se_hash; /* hash by sessionid */
struct list_head se_perclnt;
/* See SESSION4_PERSIST, etc. for standard flags; this is internal-only: */
#define NFS4_SESSION_DEAD 0x010
u32 se_flags;
struct nfs4_client *se_client;
struct nfs4_sessionid se_sessionid;
struct nfsd4_channel_attrs se_fchannel;
struct nfsd4_channel_attrs se_bchannel;
struct nfsd4_cb_sec se_cb_sec;
struct list_head se_conns;
u32 se_cb_prog;
u32 se_cb_seq_nr;
struct nfsd4_slot *se_slots[]; /* forward channel slots */
};
/* formatted contents of nfs4_sessionid */
struct nfsd4_sessionid {
clientid_t clientid;
u32 sequence;
u32 reserved;
};
#define HEXDIR_LEN 33 /* hex version of 16 byte md5 of cl_name plus '\0' */
/*
* struct nfs4_client - one per client. Clientids live here.
*
* The initial object created by an NFS client using SETCLIENTID (for NFSv4.0)
* or EXCHANGE_ID (for NFSv4.1+). These objects are refcounted and timestamped.
* Each nfsd_net_ns object contains a set of these and they are tracked via
* short and long form clientid. They are hashed and searched for under the
* per-nfsd_net client_lock spinlock.
*
* References to it are only held during the processing of compounds, and in
* certain other operations. In their "resting state" they have a refcount of
* 0. If they are not renewed within a lease period, they become eligible for
* destruction by the laundromat.
*
* These objects can also be destroyed prematurely by the fault injection code,
* or if the client sends certain forms of SETCLIENTID or EXCHANGE_ID updates.
* Care is taken *not* to do this however when the objects have an elevated
* refcount.
*
* o Each nfs4_client is hashed by clientid
*
* o Each nfs4_clients is also hashed by name (the opaque quantity initially
* sent by the client to identify itself).
*
* o cl_perclient list is used to ensure no dangling stateowner references
* when we expire the nfs4_client
*/
struct nfs4_client {
struct list_head cl_idhash; /* hash by cl_clientid.id */
struct rb_node cl_namenode; /* link into by-name trees */
struct list_head *cl_ownerstr_hashtbl;
struct list_head cl_openowners;
struct idr cl_stateids; /* stateid lookup */
struct list_head cl_delegations;
struct list_head cl_revoked; /* unacknowledged, revoked 4.1 state */
struct list_head cl_lru; /* tail queue */
struct xdr_netobj cl_name; /* id generated by client */
nfs4_verifier cl_verifier; /* generated by client */
time_t cl_time; /* time of last lease renewal */
struct sockaddr_storage cl_addr; /* client ipaddress */
bool cl_mach_cred; /* SP4_MACH_CRED in force */
struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* generated by server */
u32 cl_minorversion;
/* for v4.0 and v4.1 callbacks: */
struct nfs4_cb_conn cl_cb_conn;
#define NFSD4_CLIENT_CB_UPDATE (0)
#define NFSD4_CLIENT_CB_KILL (1)
#define NFSD4_CLIENT_STABLE (2) /* client on stable storage */
#define NFSD4_CLIENT_RECLAIM_COMPLETE (3) /* reclaim_complete done */
#define NFSD4_CLIENT_CONFIRMED (4) /* client is confirmed */
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
1 << NFSD4_CLIENT_CB_KILL)
unsigned long cl_flags;
struct rpc_cred *cl_cb_cred;
struct rpc_clnt *cl_cb_client;
u32 cl_cb_ident;
#define NFSD4_CB_UP 0
#define NFSD4_CB_UNKNOWN 1
#define NFSD4_CB_DOWN 2
#define NFSD4_CB_FAULT 3
int cl_cb_state;
struct nfsd4_callback cl_cb_null;
struct nfsd4_session *cl_cb_session;
struct list_head cl_callbacks; /* list of in-progress callbacks */
/* for all client information that callback code might need: */
spinlock_t cl_lock;
/* for nfs41 */
struct list_head cl_sessions;
struct nfsd4_clid_slot cl_cs_slot; /* create_session slot */
u32 cl_exchange_flags;
/* number of rpc's in progress over an associated session: */
atomic_t cl_refcount;
/* for nfs41 callbacks */
/* We currently support a single back channel with a single slot */
unsigned long cl_cb_slot_busy;
struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */
/* wait here for slots */
struct net *net;
};
/* struct nfs4_client_reset
* one per old client. Populates reset_str_hashtbl. Filled from conf_id_hashtbl
* upon lease reset, or from upcall to state_daemon (to read in state
* from non-volitile storage) upon reboot.
*/
struct nfs4_client_reclaim {
struct list_head cr_strhash; /* hash by cr_name */
struct nfs4_client *cr_clp; /* pointer to associated clp */
char cr_recdir[HEXDIR_LEN]; /* recover dir */
};
static inline void
update_stateid(stateid_t *stateid)
{
stateid->si_generation++;
/* Wraparound recommendation from 3530bis-13 9.1.3.2: */
if (stateid->si_generation == 0)
stateid->si_generation = 1;
}
/* A reasonable value for REPLAY_ISIZE was estimated as follows:
* The OPEN response, typically the largest, requires
* 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 8(verifier) +
* 4(deleg. type) + 8(deleg. stateid) + 4(deleg. recall flag) +
* 20(deleg. space limit) + ~32(deleg. ace) = 112 bytes
*/
#define NFSD4_REPLAY_ISIZE 112
/*
* Replay buffer, where the result of the last seqid-mutating operation
* is cached.
*/
struct nfs4_replay {
__be32 rp_status;
unsigned int rp_buflen;
char *rp_buf;
struct knfsd_fh rp_openfh;
struct mutex rp_mutex;
char rp_ibuf[NFSD4_REPLAY_ISIZE];
};
struct nfs4_stateowner;
struct nfs4_stateowner_operations {
void (*so_unhash)(struct nfs4_stateowner *);
void (*so_free)(struct nfs4_stateowner *);
};
/*
* A core object that represents either an open or lock owner. The object and
* lock owner objects have one of these embedded within them. Refcounts and
* other fields common to both owner types are contained within these
* structures.
*/
struct nfs4_stateowner {
struct list_head so_strhash;
struct list_head so_stateids;
struct nfs4_client *so_client;
const struct nfs4_stateowner_operations *so_ops;
/* after increment in nfsd4_bump_seqid, represents the next
* sequence id expected from the client: */
atomic_t so_count;
u32 so_seqid;
struct xdr_netobj so_owner; /* open owner name */
struct nfs4_replay so_replay;
bool so_is_open_owner;
};
/*
* When a file is opened, the client provides an open state owner opaque string
* that indicates the "owner" of that open. These objects are refcounted.
* References to it are held by each open state associated with it. This object
* is a superset of the nfs4_stateowner struct.
*/
struct nfs4_openowner {
struct nfs4_stateowner oo_owner; /* must be first field */
struct list_head oo_perclient;
/*
* We keep around openowners a little while after last close,
* which saves clients from having to confirm, and allows us to
* handle close replays if they come soon enough. The close_lru
* is a list of such openowners, to be reaped by the laundromat
* thread eventually if they remain unused:
*/
struct list_head oo_close_lru;
struct nfs4_ol_stateid *oo_last_closed_stid;
time_t oo_time; /* time of placement on so_close_lru */
#define NFS4_OO_CONFIRMED 1
unsigned char oo_flags;
};
/*
* Represents a generic "lockowner". Similar to an openowner. References to it
* are held by the lock stateids that are created on its behalf. This object is
* a superset of the nfs4_stateowner struct (or would be if it needed any extra
* fields).
*/
struct nfs4_lockowner {
struct nfs4_stateowner lo_owner; /* must be first element */
};
static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
{
return container_of(so, struct nfs4_openowner, oo_owner);
}
static inline struct nfs4_lockowner * lockowner(struct nfs4_stateowner *so)
{
return container_of(so, struct nfs4_lockowner, lo_owner);
}
/*
* nfs4_file: a file opened by some number of (open) nfs4_stateowners.
*
* These objects are global. nfsd only keeps one instance of a nfs4_file per
* inode (though it may keep multiple file descriptors open per inode). These
* are tracked in the file_hashtbl which is protected by the state_lock
* spinlock.
*/
struct nfs4_file {
atomic_t fi_ref;
spinlock_t fi_lock;
struct hlist_node fi_hash; /* hash by "struct inode *" */
struct list_head fi_stateids;
struct list_head fi_delegations;
/* One each for O_RDONLY, O_WRONLY, O_RDWR: */
struct file * fi_fds[3];
/*
* Each open or lock stateid contributes 0-4 to the counts
* below depending on which bits are set in st_access_bitmap:
* 1 to fi_access[O_RDONLY] if NFS4_SHARE_ACCES_READ is set
* + 1 to fi_access[O_WRONLY] if NFS4_SHARE_ACCESS_WRITE is set
* + 1 to both of the above if NFS4_SHARE_ACCESS_BOTH is set.
*/
atomic_t fi_access[2];
u32 fi_share_deny;
struct file *fi_deleg_file;
atomic_t fi_delegees;
struct knfsd_fh fi_fhandle;
bool fi_had_conflict;
};
/*
* A generic struct representing either a open or lock stateid. The nfs4_client
* holds a reference to each of these objects, and they in turn hold a
* reference to their respective stateowners. The client's reference is
* released in response to a close or unlock (depending on whether it's an open
* or lock stateid) or when the client is being destroyed.
*
* In the case of v4.0 open stateids, these objects are preserved for a little
* while after close in order to handle CLOSE replays. Those are eventually
* reclaimed via a LRU scheme by the laundromat.
*
* This object is a superset of the nfs4_stid. "ol" stands for "Open or Lock".
* Better suggestions welcome.
*/
struct nfs4_ol_stateid {
struct nfs4_stid st_stid; /* must be first field */
struct list_head st_perfile;
struct list_head st_perstateowner;
struct list_head st_locks;
struct nfs4_stateowner * st_stateowner;
unsigned char st_access_bmap;
unsigned char st_deny_bmap;
struct nfs4_ol_stateid * st_openstp;
};
static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s)
{
return container_of(s, struct nfs4_ol_stateid, st_stid);
}
/* flags for preprocess_seqid_op() */
#define RD_STATE 0x00000010
#define WR_STATE 0x00000020
enum nfsd4_cb_op {
NFSPROC4_CLNT_CB_NULL = 0,
NFSPROC4_CLNT_CB_RECALL,
NFSPROC4_CLNT_CB_SEQUENCE,
};
struct nfsd4_compound_state;
struct nfsd_net;
extern __be32 nfs4_preprocess_stateid_op(struct net *net,
struct nfsd4_compound_state *cstate,
stateid_t *stateid, int flags, struct file **filp);
void nfs4_put_stid(struct nfs4_stid *s);
void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *);
extern void nfs4_release_reclaim(struct nfsd_net *);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,
struct nfsd_net *nn);
extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
extern int set_callback_cred(void);
extern void nfsd4_probe_callback(struct nfs4_client *clp);
extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
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,
struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op);
extern void nfsd4_run_cb(struct nfsd4_callback *cb);
extern int nfsd4_create_callback_queue(void);
extern void nfsd4_destroy_callback_queue(void);
extern void nfsd4_shutdown_callback(struct nfs4_client *);
extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
struct nfsd_net *nn);
extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn);
/* grace period management */
void nfsd4_end_grace(struct nfsd_net *nn);
/* nfs4recover operations */
extern int nfsd4_client_tracking_init(struct net *net);
extern void nfsd4_client_tracking_exit(struct net *net);
extern void nfsd4_client_record_create(struct nfs4_client *clp);
extern void nfsd4_client_record_remove(struct nfs4_client *clp);
extern int nfsd4_client_record_check(struct nfs4_client *clp);
extern void nfsd4_record_grace_done(struct nfsd_net *nn);
/* nfs fault injection functions */
#ifdef CONFIG_NFSD_FAULT_INJECTION
int nfsd_fault_inject_init(void);
void nfsd_fault_inject_cleanup(void);
u64 nfsd_inject_print_clients(void);
u64 nfsd_inject_forget_client(struct sockaddr_storage *, size_t);
u64 nfsd_inject_forget_clients(u64);
u64 nfsd_inject_print_locks(void);
u64 nfsd_inject_forget_client_locks(struct sockaddr_storage *, size_t);
u64 nfsd_inject_forget_locks(u64);
u64 nfsd_inject_print_openowners(void);
u64 nfsd_inject_forget_client_openowners(struct sockaddr_storage *, size_t);
u64 nfsd_inject_forget_openowners(u64);
u64 nfsd_inject_print_delegations(void);
u64 nfsd_inject_forget_client_delegations(struct sockaddr_storage *, size_t);
u64 nfsd_inject_forget_delegations(u64);
u64 nfsd_inject_recall_client_delegations(struct sockaddr_storage *, size_t);
u64 nfsd_inject_recall_delegations(u64);
#else /* CONFIG_NFSD_FAULT_INJECTION */
static inline int nfsd_fault_inject_init(void) { return 0; }
static inline void nfsd_fault_inject_cleanup(void) {}
#endif /* CONFIG_NFSD_FAULT_INJECTION */
#endif /* NFSD4_STATE_H */

104
fs/nfsd/stats.c Normal file
View file

@ -0,0 +1,104 @@
/*
* procfs-based user access to knfsd statistics
*
* /proc/net/rpc/nfsd
*
* Format:
* rc <hits> <misses> <nocache>
* Statistsics for the reply cache
* fh <stale> <total-lookups> <anonlookups> <dir-not-in-dcache> <nondir-not-in-dcache>
* statistics for filehandle lookup
* io <bytes-read> <bytes-written>
* statistics for IO throughput
* th <threads> <fullcnt> <10%-20%> <20%-30%> ... <90%-100%> <100%>
* time (seconds) when nfsd thread usage above thresholds
* 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)
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/sunrpc/stats.h>
#include <net/net_namespace.h>
#include "nfsd.h"
struct nfsd_stats nfsdstats;
struct svc_stat nfsd_svcstats = {
.program = &nfsd_program,
};
static int nfsd_proc_show(struct seq_file *seq, void *v)
{
int i;
seq_printf(seq, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n",
nfsdstats.rchits,
nfsdstats.rcmisses,
nfsdstats.rcnocache,
nfsdstats.fh_stale,
nfsdstats.fh_lookup,
nfsdstats.fh_anon,
nfsdstats.fh_nocache_dir,
nfsdstats.fh_nocache_nondir,
nfsdstats.io_read,
nfsdstats.io_write);
/* thread usage: */
seq_printf(seq, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt);
for (i=0; i<10; i++) {
unsigned int jifs = nfsdstats.th_usage[i];
unsigned int sec = jifs / HZ, msec = (jifs % HZ)*1000/HZ;
seq_printf(seq, " %u.%03u", sec, msec);
}
/* 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 */
svc_seq_show(seq, &nfsd_svcstats);
#ifdef CONFIG_NFSD_V4
/* Show count for individual nfsv4 operations */
/* Writing operation numbers 0 1 2 also for maintaining uniformity */
seq_printf(seq,"proc4ops %u", LAST_NFS4_OP + 1);
for (i = 0; i <= LAST_NFS4_OP; i++)
seq_printf(seq, " %u", nfsdstats.nfs4_opcount[i]);
seq_putc(seq, '\n');
#endif
return 0;
}
static int nfsd_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, nfsd_proc_show, NULL);
}
static const struct file_operations nfsd_proc_fops = {
.owner = THIS_MODULE,
.open = nfsd_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void
nfsd_stat_init(void)
{
svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_fops);
}
void
nfsd_stat_shutdown(void)
{
svc_proc_unregister(&init_net, "nfsd");
}

43
fs/nfsd/stats.h Normal file
View file

@ -0,0 +1,43 @@
/*
* Statistics for NFS server.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _NFSD_STATS_H
#define _NFSD_STATS_H
#include <uapi/linux/nfsd/stats.h>
struct nfsd_stats {
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
};
extern struct nfsd_stats nfsdstats;
extern struct svc_stat nfsd_svcstats;
void nfsd_stat_init(void);
void nfsd_stat_shutdown(void);
#endif /* _NFSD_STATS_H */

2129
fs/nfsd/vfs.c Normal file

File diff suppressed because it is too large Load diff

129
fs/nfsd/vfs.h Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef LINUX_NFSD_VFS_H
#define LINUX_NFSD_VFS_H
#include "nfsfh.h"
#include "nfsd.h"
/*
* Flags for nfsd_permission
*/
#define NFSD_MAY_NOP 0
#define NFSD_MAY_EXEC 0x001 /* == MAY_EXEC */
#define NFSD_MAY_WRITE 0x002 /* == MAY_WRITE */
#define NFSD_MAY_READ 0x004 /* == MAY_READ */
#define NFSD_MAY_SATTR 0x008
#define NFSD_MAY_TRUNC 0x010
#define NFSD_MAY_LOCK 0x020
#define NFSD_MAY_MASK 0x03f
/* extra hints to permission and open routines: */
#define NFSD_MAY_OWNER_OVERRIDE 0x040
#define NFSD_MAY_LOCAL_ACCESS 0x080 /* for device special files */
#define NFSD_MAY_BYPASS_GSS_ON_ROOT 0x100
#define NFSD_MAY_NOT_BREAK_LEASE 0x200
#define NFSD_MAY_BYPASS_GSS 0x400
#define NFSD_MAY_READ_IF_EXEC 0x800
#define NFSD_MAY_64BIT_COOKIE 0x1000 /* 64 bit readdir cookies for >= NFSv3 */
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
/*
* Callback function for readdir
*/
typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int);
/* nfsd/vfs.c */
int nfsd_racache_init(int);
void nfsd_racache_shutdown(void);
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp);
__be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *,
const char *, unsigned int, struct svc_fh *);
__be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
const char *, unsigned int,
struct svc_export **, struct dentry **);
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
struct iattr *, int, time_t);
int nfsd_mountpoint(struct dentry *, struct svc_export *);
#ifdef CONFIG_NFSD_V4
__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
struct xdr_netobj *);
#endif /* CONFIG_NFSD_V4 */
__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 do_nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct iattr *attrs,
struct svc_fh *res, int createmode,
u32 *verifier, bool *truncp, bool *created);
__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
loff_t, unsigned long);
#endif /* CONFIG_NFSD_V3 */
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **);
void nfsd_close(struct file *);
struct raparms;
__be32 nfsd_get_tmp_read_open(struct svc_rqst *, struct svc_fh *,
struct file **, struct raparms **);
void nfsd_put_tmp_read_open(struct file *, struct raparms *);
__be32 nfsd_splice_read(struct svc_rqst *,
struct file *, loff_t, unsigned long *);
__be32 nfsd_readv(struct file *, loff_t, struct kvec *, int,
unsigned long *);
__be32 nfsd_read(struct svc_rqst *, struct svc_fh *,
loff_t, struct kvec *, int, unsigned long *);
__be32 nfsd_write(struct svc_rqst *, struct svc_fh *,struct file *,
loff_t, struct kvec *,int, unsigned long *, int *);
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
char *, int *);
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
char *name, int len, char *path,
struct svc_fh *res);
__be32 nfsd_link(struct svc_rqst *, struct svc_fh *,
char *, int, struct svc_fh *);
__be32 nfsd_rename(struct svc_rqst *,
struct svc_fh *, char *, int,
struct svc_fh *, char *, int);
__be32 nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type,
char *name, int len);
__be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *,
loff_t *, struct readdir_cd *, filldir_t);
__be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *,
struct kstatfs *, int access);
__be32 nfsd_permission(struct svc_rqst *, struct svc_export *,
struct dentry *, int);
static inline int fh_want_write(struct svc_fh *fh)
{
int ret = mnt_want_write(fh->fh_export->ex_path.mnt);
if (!ret)
fh->fh_want_write = 1;
return ret;
}
static inline void fh_drop_write(struct svc_fh *fh)
{
if (fh->fh_want_write) {
fh->fh_want_write = 0;
mnt_drop_write(fh->fh_export->ex_path.mnt);
}
}
static inline __be32 fh_getattr(struct svc_fh *fh, struct kstat *stat)
{
struct path p = {.mnt = fh->fh_export->ex_path.mnt,
.dentry = fh->fh_dentry};
return nfserrno(vfs_getattr(&p, stat));
}
#endif /* LINUX_NFSD_VFS_H */

173
fs/nfsd/xdr.h Normal file
View file

@ -0,0 +1,173 @@
/* XDR types for nfsd. This is mainly a typing exercise. */
#ifndef LINUX_NFSD_H
#define LINUX_NFSD_H
#include <linux/vfs.h>
#include "nfsd.h"
#include "nfsfh.h"
struct nfsd_fhandle {
struct svc_fh fh;
};
struct nfsd_sattrargs {
struct svc_fh fh;
struct iattr attrs;
};
struct nfsd_diropargs {
struct svc_fh fh;
char * name;
unsigned int len;
};
struct nfsd_readargs {
struct svc_fh fh;
__u32 offset;
__u32 count;
int vlen;
};
struct nfsd_writeargs {
svc_fh fh;
__u32 offset;
int len;
int vlen;
};
struct nfsd_createargs {
struct svc_fh fh;
char * name;
unsigned int len;
struct iattr attrs;
};
struct nfsd_renameargs {
struct svc_fh ffh;
char * fname;
unsigned int flen;
struct svc_fh tfh;
char * tname;
unsigned int tlen;
};
struct nfsd_readlinkargs {
struct svc_fh fh;
char * buffer;
};
struct nfsd_linkargs {
struct svc_fh ffh;
struct svc_fh tfh;
char * tname;
unsigned int tlen;
};
struct nfsd_symlinkargs {
struct svc_fh ffh;
char * fname;
unsigned int flen;
char * tname;
unsigned int tlen;
struct iattr attrs;
};
struct nfsd_readdirargs {
struct svc_fh fh;
__u32 cookie;
__u32 count;
__be32 * buffer;
};
struct nfsd_attrstat {
struct svc_fh fh;
struct kstat stat;
};
struct nfsd_diropres {
struct svc_fh fh;
struct kstat stat;
};
struct nfsd_readlinkres {
int len;
};
struct nfsd_readres {
struct svc_fh fh;
unsigned long count;
struct kstat stat;
};
struct nfsd_readdirres {
int count;
struct readdir_cd common;
__be32 * buffer;
int buflen;
__be32 * offset;
};
struct nfsd_statfsres {
struct kstatfs stats;
};
/*
* Storage requirements for XDR arguments and results.
*/
union nfsd_xdrstore {
struct nfsd_sattrargs sattr;
struct nfsd_diropargs dirop;
struct nfsd_readargs read;
struct nfsd_writeargs write;
struct nfsd_createargs create;
struct nfsd_renameargs rename;
struct nfsd_linkargs link;
struct nfsd_symlinkargs symlink;
struct nfsd_readdirargs readdir;
};
#define NFS2_SVC_XDRSIZE sizeof(union nfsd_xdrstore)
int nfssvc_decode_void(struct svc_rqst *, __be32 *, void *);
int nfssvc_decode_fhandle(struct svc_rqst *, __be32 *, struct nfsd_fhandle *);
int nfssvc_decode_sattrargs(struct svc_rqst *, __be32 *,
struct nfsd_sattrargs *);
int nfssvc_decode_diropargs(struct svc_rqst *, __be32 *,
struct nfsd_diropargs *);
int nfssvc_decode_readargs(struct svc_rqst *, __be32 *,
struct nfsd_readargs *);
int nfssvc_decode_writeargs(struct svc_rqst *, __be32 *,
struct nfsd_writeargs *);
int nfssvc_decode_createargs(struct svc_rqst *, __be32 *,
struct nfsd_createargs *);
int nfssvc_decode_renameargs(struct svc_rqst *, __be32 *,
struct nfsd_renameargs *);
int nfssvc_decode_readlinkargs(struct svc_rqst *, __be32 *,
struct nfsd_readlinkargs *);
int nfssvc_decode_linkargs(struct svc_rqst *, __be32 *,
struct nfsd_linkargs *);
int nfssvc_decode_symlinkargs(struct svc_rqst *, __be32 *,
struct nfsd_symlinkargs *);
int nfssvc_decode_readdirargs(struct svc_rqst *, __be32 *,
struct nfsd_readdirargs *);
int nfssvc_encode_void(struct svc_rqst *, __be32 *, void *);
int nfssvc_encode_attrstat(struct svc_rqst *, __be32 *, struct nfsd_attrstat *);
int nfssvc_encode_diropres(struct svc_rqst *, __be32 *, struct nfsd_diropres *);
int nfssvc_encode_readlinkres(struct svc_rqst *, __be32 *, struct nfsd_readlinkres *);
int nfssvc_encode_readres(struct svc_rqst *, __be32 *, struct nfsd_readres *);
int nfssvc_encode_statfsres(struct svc_rqst *, __be32 *, struct nfsd_statfsres *);
int nfssvc_encode_readdirres(struct svc_rqst *, __be32 *, struct nfsd_readdirres *);
int nfssvc_encode_entry(void *, const char *name,
int namlen, loff_t offset, u64 ino, unsigned int);
int nfssvc_release_fhandle(struct svc_rqst *, __be32 *, struct nfsd_fhandle *);
/* Helper functions for NFSv2 ACL code */
__be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat);
__be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp);
#endif /* LINUX_NFSD_H */

349
fs/nfsd/xdr3.h Normal file
View file

@ -0,0 +1,349 @@
/*
* XDR types for NFSv3 in nfsd.
*
* Copyright (C) 1996-1998, Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LINUX_NFSD_XDR3_H
#define _LINUX_NFSD_XDR3_H
#include "xdr.h"
struct nfsd3_sattrargs {
struct svc_fh fh;
struct iattr attrs;
int check_guard;
time_t guardtime;
};
struct nfsd3_diropargs {
struct svc_fh fh;
char * name;
unsigned int len;
};
struct nfsd3_accessargs {
struct svc_fh fh;
unsigned int access;
};
struct nfsd3_readargs {
struct svc_fh fh;
__u64 offset;
__u32 count;
int vlen;
};
struct nfsd3_writeargs {
svc_fh fh;
__u64 offset;
__u32 count;
int stable;
__u32 len;
int vlen;
};
struct nfsd3_createargs {
struct svc_fh fh;
char * name;
unsigned int len;
int createmode;
struct iattr attrs;
__be32 * verf;
};
struct nfsd3_mknodargs {
struct svc_fh fh;
char * name;
unsigned int len;
__u32 ftype;
__u32 major, minor;
struct iattr attrs;
};
struct nfsd3_renameargs {
struct svc_fh ffh;
char * fname;
unsigned int flen;
struct svc_fh tfh;
char * tname;
unsigned int tlen;
};
struct nfsd3_readlinkargs {
struct svc_fh fh;
char * buffer;
};
struct nfsd3_linkargs {
struct svc_fh ffh;
struct svc_fh tfh;
char * tname;
unsigned int tlen;
};
struct nfsd3_symlinkargs {
struct svc_fh ffh;
char * fname;
unsigned int flen;
char * tname;
unsigned int tlen;
struct iattr attrs;
};
struct nfsd3_readdirargs {
struct svc_fh fh;
__u64 cookie;
__u32 dircount;
__u32 count;
__be32 * verf;
__be32 * buffer;
};
struct nfsd3_commitargs {
struct svc_fh fh;
__u64 offset;
__u32 count;
};
struct nfsd3_getaclargs {
struct svc_fh fh;
int mask;
};
struct posix_acl;
struct nfsd3_setaclargs {
struct svc_fh fh;
int mask;
struct posix_acl *acl_access;
struct posix_acl *acl_default;
};
struct nfsd3_attrstat {
__be32 status;
struct svc_fh fh;
struct kstat stat;
};
/* LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD */
struct nfsd3_diropres {
__be32 status;
struct svc_fh dirfh;
struct svc_fh fh;
};
struct nfsd3_accessres {
__be32 status;
struct svc_fh fh;
__u32 access;
struct kstat stat;
};
struct nfsd3_readlinkres {
__be32 status;
struct svc_fh fh;
__u32 len;
};
struct nfsd3_readres {
__be32 status;
struct svc_fh fh;
unsigned long count;
int eof;
};
struct nfsd3_writeres {
__be32 status;
struct svc_fh fh;
unsigned long count;
int committed;
};
struct nfsd3_renameres {
__be32 status;
struct svc_fh ffh;
struct svc_fh tfh;
};
struct nfsd3_linkres {
__be32 status;
struct svc_fh tfh;
struct svc_fh fh;
};
struct nfsd3_readdirres {
__be32 status;
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];
struct readdir_cd common;
__be32 * buffer;
int buflen;
__be32 * offset;
__be32 * offset1;
struct svc_rqst * rqstp;
};
struct nfsd3_fsstatres {
__be32 status;
struct kstatfs stats;
__u32 invarsec;
};
struct nfsd3_fsinfores {
__be32 status;
__u32 f_rtmax;
__u32 f_rtpref;
__u32 f_rtmult;
__u32 f_wtmax;
__u32 f_wtpref;
__u32 f_wtmult;
__u32 f_dtpref;
__u64 f_maxfilesize;
__u32 f_properties;
};
struct nfsd3_pathconfres {
__be32 status;
__u32 p_link_max;
__u32 p_name_max;
__u32 p_no_trunc;
__u32 p_chown_restricted;
__u32 p_case_insensitive;
__u32 p_case_preserving;
};
struct nfsd3_commitres {
__be32 status;
struct svc_fh fh;
};
struct nfsd3_getaclres {
__be32 status;
struct svc_fh fh;
int mask;
struct posix_acl *acl_access;
struct posix_acl *acl_default;
struct kstat stat;
};
/* dummy type for release */
struct nfsd3_fhandle_pair {
__u32 dummy;
struct svc_fh fh1;
struct svc_fh fh2;
};
/*
* Storage requirements for XDR arguments and results.
*/
union nfsd3_xdrstore {
struct nfsd3_sattrargs sattrargs;
struct nfsd3_diropargs diropargs;
struct nfsd3_readargs readargs;
struct nfsd3_writeargs writeargs;
struct nfsd3_createargs createargs;
struct nfsd3_renameargs renameargs;
struct nfsd3_linkargs linkargs;
struct nfsd3_symlinkargs symlinkargs;
struct nfsd3_readdirargs readdirargs;
struct nfsd3_diropres diropres;
struct nfsd3_accessres accessres;
struct nfsd3_readlinkres readlinkres;
struct nfsd3_readres readres;
struct nfsd3_writeres writeres;
struct nfsd3_renameres renameres;
struct nfsd3_linkres linkres;
struct nfsd3_readdirres readdirres;
struct nfsd3_fsstatres fsstatres;
struct nfsd3_fsinfores fsinfores;
struct nfsd3_pathconfres pathconfres;
struct nfsd3_commitres commitres;
struct nfsd3_getaclres getaclres;
};
#define NFS3_SVC_XDRSIZE sizeof(union nfsd3_xdrstore)
int nfs3svc_decode_fhandle(struct svc_rqst *, __be32 *, struct nfsd_fhandle *);
int nfs3svc_decode_sattrargs(struct svc_rqst *, __be32 *,
struct nfsd3_sattrargs *);
int nfs3svc_decode_diropargs(struct svc_rqst *, __be32 *,
struct nfsd3_diropargs *);
int nfs3svc_decode_accessargs(struct svc_rqst *, __be32 *,
struct nfsd3_accessargs *);
int nfs3svc_decode_readargs(struct svc_rqst *, __be32 *,
struct nfsd3_readargs *);
int nfs3svc_decode_writeargs(struct svc_rqst *, __be32 *,
struct nfsd3_writeargs *);
int nfs3svc_decode_createargs(struct svc_rqst *, __be32 *,
struct nfsd3_createargs *);
int nfs3svc_decode_mkdirargs(struct svc_rqst *, __be32 *,
struct nfsd3_createargs *);
int nfs3svc_decode_mknodargs(struct svc_rqst *, __be32 *,
struct nfsd3_mknodargs *);
int nfs3svc_decode_renameargs(struct svc_rqst *, __be32 *,
struct nfsd3_renameargs *);
int nfs3svc_decode_readlinkargs(struct svc_rqst *, __be32 *,
struct nfsd3_readlinkargs *);
int nfs3svc_decode_linkargs(struct svc_rqst *, __be32 *,
struct nfsd3_linkargs *);
int nfs3svc_decode_symlinkargs(struct svc_rqst *, __be32 *,
struct nfsd3_symlinkargs *);
int nfs3svc_decode_readdirargs(struct svc_rqst *, __be32 *,
struct nfsd3_readdirargs *);
int nfs3svc_decode_readdirplusargs(struct svc_rqst *, __be32 *,
struct nfsd3_readdirargs *);
int nfs3svc_decode_commitargs(struct svc_rqst *, __be32 *,
struct nfsd3_commitargs *);
int nfs3svc_encode_voidres(struct svc_rqst *, __be32 *, void *);
int nfs3svc_encode_attrstat(struct svc_rqst *, __be32 *,
struct nfsd3_attrstat *);
int nfs3svc_encode_wccstat(struct svc_rqst *, __be32 *,
struct nfsd3_attrstat *);
int nfs3svc_encode_diropres(struct svc_rqst *, __be32 *,
struct nfsd3_diropres *);
int nfs3svc_encode_accessres(struct svc_rqst *, __be32 *,
struct nfsd3_accessres *);
int nfs3svc_encode_readlinkres(struct svc_rqst *, __be32 *,
struct nfsd3_readlinkres *);
int nfs3svc_encode_readres(struct svc_rqst *, __be32 *, struct nfsd3_readres *);
int nfs3svc_encode_writeres(struct svc_rqst *, __be32 *, struct nfsd3_writeres *);
int nfs3svc_encode_createres(struct svc_rqst *, __be32 *,
struct nfsd3_diropres *);
int nfs3svc_encode_renameres(struct svc_rqst *, __be32 *,
struct nfsd3_renameres *);
int nfs3svc_encode_linkres(struct svc_rqst *, __be32 *,
struct nfsd3_linkres *);
int nfs3svc_encode_readdirres(struct svc_rqst *, __be32 *,
struct nfsd3_readdirres *);
int nfs3svc_encode_fsstatres(struct svc_rqst *, __be32 *,
struct nfsd3_fsstatres *);
int nfs3svc_encode_fsinfores(struct svc_rqst *, __be32 *,
struct nfsd3_fsinfores *);
int nfs3svc_encode_pathconfres(struct svc_rqst *, __be32 *,
struct nfsd3_pathconfres *);
int nfs3svc_encode_commitres(struct svc_rqst *, __be32 *,
struct nfsd3_commitres *);
int nfs3svc_release_fhandle(struct svc_rqst *, __be32 *,
struct nfsd3_attrstat *);
int nfs3svc_release_fhandle2(struct svc_rqst *, __be32 *,
struct nfsd3_fhandle_pair *);
int nfs3svc_encode_entry(void *, const char *name,
int namlen, loff_t offset, u64 ino,
unsigned int);
int nfs3svc_encode_entry_plus(void *, const char *name,
int namlen, loff_t offset, u64 ino,
unsigned int);
/* Helper functions for NFSv3 ACL code */
__be32 *nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p,
struct svc_fh *fhp);
__be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp);
#endif /* _LINUX_NFSD_XDR3_H */

656
fs/nfsd/xdr4.h Normal file
View file

@ -0,0 +1,656 @@
/*
* Server-side types for NFSv4.
*
* Copyright (c) 2002 The Regents of the University of Michigan.
* All rights reserved.
*
* Kendrick Smith <kmsmith@umich.edu>
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _LINUX_NFSD_XDR4_H
#define _LINUX_NFSD_XDR4_H
#include "state.h"
#include "nfsd.h"
#define NFSD4_MAX_SEC_LABEL_LEN 2048
#define NFSD4_MAX_TAGLEN 128
#define XDR_LEN(n) (((n) + 3) & ~3)
#define CURRENT_STATE_ID_FLAG (1<<0)
#define SAVED_STATE_ID_FLAG (1<<1)
#define SET_STATE_ID(c, f) ((c)->sid_flags |= (f))
#define HAS_STATE_ID(c, f) ((c)->sid_flags & (f))
#define CLEAR_STATE_ID(c, f) ((c)->sid_flags &= ~(f))
struct nfsd4_compound_state {
struct svc_fh current_fh;
struct svc_fh save_fh;
struct nfs4_stateowner *replay_owner;
struct nfs4_client *clp;
/* For sessions DRC */
struct nfsd4_session *session;
struct nfsd4_slot *slot;
int data_offset;
size_t iovlen;
u32 minorversion;
__be32 status;
stateid_t current_stateid;
stateid_t save_stateid;
/* to indicate current and saved state id presents */
u32 sid_flags;
};
static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
{
return cs->slot != NULL;
}
struct nfsd4_change_info {
u32 atomic;
bool change_supported;
u32 before_ctime_sec;
u32 before_ctime_nsec;
u64 before_change;
u32 after_ctime_sec;
u32 after_ctime_nsec;
u64 after_change;
};
struct nfsd4_access {
u32 ac_req_access; /* request */
u32 ac_supported; /* response */
u32 ac_resp_access; /* response */
};
struct nfsd4_close {
u32 cl_seqid; /* request */
stateid_t cl_stateid; /* request+response */
};
struct nfsd4_commit {
u64 co_offset; /* request */
u32 co_count; /* request */
nfs4_verifier co_verf; /* response */
};
struct nfsd4_create {
u32 cr_namelen; /* request */
char * cr_name; /* request */
u32 cr_type; /* request */
union { /* request */
struct {
u32 datalen;
char *data;
} link; /* NF4LNK */
struct {
u32 specdata1;
u32 specdata2;
} dev; /* NF4BLK, NF4CHR */
} u;
u32 cr_bmval[3]; /* request */
struct iattr cr_iattr; /* request */
struct nfsd4_change_info cr_cinfo; /* response */
struct nfs4_acl *cr_acl;
struct xdr_netobj cr_label;
};
#define cr_datalen u.link.datalen
#define cr_data u.link.data
#define cr_specdata1 u.dev.specdata1
#define cr_specdata2 u.dev.specdata2
struct nfsd4_delegreturn {
stateid_t dr_stateid;
};
struct nfsd4_getattr {
u32 ga_bmval[3]; /* request */
struct svc_fh *ga_fhp; /* response */
};
struct nfsd4_link {
u32 li_namelen; /* request */
char * li_name; /* request */
struct nfsd4_change_info li_cinfo; /* response */
};
struct nfsd4_lock_denied {
clientid_t ld_clientid;
struct xdr_netobj ld_owner;
u64 ld_start;
u64 ld_length;
u32 ld_type;
};
struct nfsd4_lock {
/* request */
u32 lk_type;
u32 lk_reclaim; /* boolean */
u64 lk_offset;
u64 lk_length;
u32 lk_is_new;
union {
struct {
u32 open_seqid;
stateid_t open_stateid;
u32 lock_seqid;
clientid_t clientid;
struct xdr_netobj owner;
} new;
struct {
stateid_t lock_stateid;
u32 lock_seqid;
} old;
} v;
/* response */
union {
struct {
stateid_t stateid;
} ok;
struct nfsd4_lock_denied denied;
} u;
};
#define lk_new_open_seqid v.new.open_seqid
#define lk_new_open_stateid v.new.open_stateid
#define lk_new_lock_seqid v.new.lock_seqid
#define lk_new_clientid v.new.clientid
#define lk_new_owner v.new.owner
#define lk_old_lock_stateid v.old.lock_stateid
#define lk_old_lock_seqid v.old.lock_seqid
#define lk_resp_stateid u.ok.stateid
#define lk_denied u.denied
struct nfsd4_lockt {
u32 lt_type;
clientid_t lt_clientid;
struct xdr_netobj lt_owner;
u64 lt_offset;
u64 lt_length;
struct nfsd4_lock_denied lt_denied;
};
struct nfsd4_locku {
u32 lu_type;
u32 lu_seqid;
stateid_t lu_stateid;
u64 lu_offset;
u64 lu_length;
};
struct nfsd4_lookup {
u32 lo_len; /* request */
char * lo_name; /* request */
};
struct nfsd4_putfh {
u32 pf_fhlen; /* request */
char *pf_fhval; /* request */
};
struct nfsd4_open {
u32 op_claim_type; /* request */
struct xdr_netobj op_fname; /* request - everything but CLAIM_PREV */
u32 op_delegate_type; /* request - CLAIM_PREV only */
stateid_t op_delegate_stateid; /* request - response */
u32 op_why_no_deleg; /* response - DELEG_NONE_EXT only */
u32 op_create; /* request */
u32 op_createmode; /* request */
u32 op_bmval[3]; /* request */
struct iattr op_iattr; /* UNCHECKED4, GUARDED4, EXCLUSIVE4_1 */
nfs4_verifier op_verf __attribute__((aligned(32)));
/* EXCLUSIVE4 */
clientid_t op_clientid; /* request */
struct xdr_netobj op_owner; /* request */
u32 op_seqid; /* request */
u32 op_share_access; /* request */
u32 op_share_deny; /* request */
u32 op_deleg_want; /* request */
stateid_t op_stateid; /* response */
__be32 op_xdr_error; /* see nfsd4_open_omfg() */
u32 op_recall; /* recall */
struct nfsd4_change_info op_cinfo; /* response */
u32 op_rflags; /* response */
bool op_truncate; /* used during processing */
bool op_created; /* used during processing */
struct nfs4_openowner *op_openowner; /* used during processing */
struct nfs4_file *op_file; /* used during processing */
struct nfs4_ol_stateid *op_stp; /* used during processing */
struct nfs4_acl *op_acl;
struct xdr_netobj op_label;
};
struct nfsd4_open_confirm {
stateid_t oc_req_stateid /* request */;
u32 oc_seqid /* request */;
stateid_t oc_resp_stateid /* response */;
};
struct nfsd4_open_downgrade {
stateid_t od_stateid;
u32 od_seqid;
u32 od_share_access; /* request */
u32 od_deleg_want; /* request */
u32 od_share_deny; /* request */
};
struct nfsd4_read {
stateid_t rd_stateid; /* request */
u64 rd_offset; /* request */
u32 rd_length; /* request */
int rd_vlen;
struct file *rd_filp;
struct svc_rqst *rd_rqstp; /* response */
struct svc_fh * rd_fhp; /* response */
};
struct nfsd4_readdir {
u64 rd_cookie; /* request */
nfs4_verifier rd_verf; /* request */
u32 rd_dircount; /* request */
u32 rd_maxcount; /* request */
u32 rd_bmval[3]; /* request */
struct svc_rqst *rd_rqstp; /* response */
struct svc_fh * rd_fhp; /* response */
struct readdir_cd common;
struct xdr_stream *xdr;
int cookie_offset;
};
struct nfsd4_release_lockowner {
clientid_t rl_clientid;
struct xdr_netobj rl_owner;
};
struct nfsd4_readlink {
struct svc_rqst *rl_rqstp; /* request */
struct svc_fh * rl_fhp; /* request */
};
struct nfsd4_remove {
u32 rm_namelen; /* request */
char * rm_name; /* request */
struct nfsd4_change_info rm_cinfo; /* response */
};
struct nfsd4_rename {
u32 rn_snamelen; /* request */
char * rn_sname; /* request */
u32 rn_tnamelen; /* request */
char * rn_tname; /* request */
struct nfsd4_change_info rn_sinfo; /* response */
struct nfsd4_change_info rn_tinfo; /* response */
};
struct nfsd4_secinfo {
u32 si_namelen; /* request */
char *si_name; /* request */
struct svc_export *si_exp; /* response */
};
struct nfsd4_secinfo_no_name {
u32 sin_style; /* request */
struct svc_export *sin_exp; /* response */
};
struct nfsd4_setattr {
stateid_t sa_stateid; /* request */
u32 sa_bmval[3]; /* request */
struct iattr sa_iattr; /* request */
struct nfs4_acl *sa_acl;
struct xdr_netobj sa_label;
};
struct nfsd4_setclientid {
nfs4_verifier se_verf; /* request */
struct xdr_netobj se_name;
u32 se_callback_prog; /* request */
u32 se_callback_netid_len; /* request */
char * se_callback_netid_val; /* request */
u32 se_callback_addr_len; /* request */
char * se_callback_addr_val; /* request */
u32 se_callback_ident; /* request */
clientid_t se_clientid; /* response */
nfs4_verifier se_confirm; /* response */
};
struct nfsd4_setclientid_confirm {
clientid_t sc_clientid;
nfs4_verifier sc_confirm;
};
struct nfsd4_saved_compoundargs {
__be32 *p;
__be32 *end;
int pagelen;
struct page **pagelist;
};
struct nfsd4_test_stateid_id {
__be32 ts_id_status;
stateid_t ts_id_stateid;
struct list_head ts_id_list;
};
struct nfsd4_test_stateid {
u32 ts_num_ids;
struct list_head ts_stateid_list;
};
struct nfsd4_free_stateid {
stateid_t fr_stateid; /* request */
};
/* also used for NVERIFY */
struct nfsd4_verify {
u32 ve_bmval[3]; /* request */
u32 ve_attrlen; /* request */
char * ve_attrval; /* request */
};
struct nfsd4_write {
stateid_t wr_stateid; /* request */
u64 wr_offset; /* request */
u32 wr_stable_how; /* request */
u32 wr_buflen; /* request */
struct kvec wr_head;
struct page ** wr_pagelist; /* request */
u32 wr_bytes_written; /* response */
u32 wr_how_written; /* response */
nfs4_verifier wr_verifier; /* response */
};
struct nfsd4_exchange_id {
nfs4_verifier verifier;
struct xdr_netobj clname;
u32 flags;
clientid_t clientid;
u32 seqid;
int spa_how;
};
struct nfsd4_sequence {
struct nfs4_sessionid sessionid; /* request/response */
u32 seqid; /* request/response */
u32 slotid; /* request/response */
u32 maxslots; /* request/response */
u32 cachethis; /* request */
#if 0
u32 target_maxslots; /* response */
#endif /* not yet */
u32 status_flags; /* response */
};
struct nfsd4_destroy_session {
struct nfs4_sessionid sessionid;
};
struct nfsd4_destroy_clientid {
clientid_t clientid;
};
struct nfsd4_reclaim_complete {
u32 rca_one_fs;
};
struct nfsd4_seek {
/* request */
stateid_t seek_stateid;
loff_t seek_offset;
u32 seek_whence;
/* response */
u32 seek_eof;
loff_t seek_pos;
};
struct nfsd4_op {
int opnum;
__be32 status;
union {
struct nfsd4_access access;
struct nfsd4_close close;
struct nfsd4_commit commit;
struct nfsd4_create create;
struct nfsd4_delegreturn delegreturn;
struct nfsd4_getattr getattr;
struct svc_fh * getfh;
struct nfsd4_link link;
struct nfsd4_lock lock;
struct nfsd4_lockt lockt;
struct nfsd4_locku locku;
struct nfsd4_lookup lookup;
struct nfsd4_verify nverify;
struct nfsd4_open open;
struct nfsd4_open_confirm open_confirm;
struct nfsd4_open_downgrade open_downgrade;
struct nfsd4_putfh putfh;
struct nfsd4_read read;
struct nfsd4_readdir readdir;
struct nfsd4_readlink readlink;
struct nfsd4_remove remove;
struct nfsd4_rename rename;
clientid_t renew;
struct nfsd4_secinfo secinfo;
struct nfsd4_setattr setattr;
struct nfsd4_setclientid setclientid;
struct nfsd4_setclientid_confirm setclientid_confirm;
struct nfsd4_verify verify;
struct nfsd4_write write;
struct nfsd4_release_lockowner release_lockowner;
/* NFSv4.1 */
struct nfsd4_exchange_id exchange_id;
struct nfsd4_backchannel_ctl backchannel_ctl;
struct nfsd4_bind_conn_to_session bind_conn_to_session;
struct nfsd4_create_session create_session;
struct nfsd4_destroy_session destroy_session;
struct nfsd4_sequence sequence;
struct nfsd4_reclaim_complete reclaim_complete;
struct nfsd4_test_stateid test_stateid;
struct nfsd4_free_stateid free_stateid;
/* NFSv4.2 */
struct nfsd4_seek seek;
} u;
struct nfs4_replay * replay;
};
bool nfsd4_cache_this_op(struct nfsd4_op *);
/*
* Memory needed just for the duration of processing one compound:
*/
struct svcxdr_tmpbuf {
struct svcxdr_tmpbuf *next;
char buf[];
};
struct nfsd4_compoundargs {
/* scratch variables for XDR decode */
__be32 * p;
__be32 * end;
struct page ** pagelist;
int pagelen;
__be32 tmp[8];
__be32 * tmpp;
struct svcxdr_tmpbuf *to_free;
struct svc_rqst *rqstp;
u32 taglen;
char * tag;
u32 minorversion;
u32 opcnt;
struct nfsd4_op *ops;
struct nfsd4_op iops[8];
int cachetype;
};
struct nfsd4_compoundres {
/* scratch variables for XDR encode */
struct xdr_stream xdr;
struct svc_rqst * rqstp;
u32 taglen;
char * tag;
u32 opcnt;
__be32 * tagp; /* tag, opcount encode location */
struct nfsd4_compound_state cstate;
};
static inline bool nfsd4_is_solo_sequence(struct nfsd4_compoundres *resp)
{
struct nfsd4_compoundargs *args = resp->rqstp->rq_argp;
return resp->opcnt == 1 && args->ops[0].opnum == OP_SEQUENCE;
}
static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp)
{
return !(resp->cstate.slot->sl_flags & NFSD4_SLOT_CACHETHIS)
|| nfsd4_is_solo_sequence(resp);
}
static inline bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
{
struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct nfsd4_compoundargs *argp = rqstp->rq_argp;
return argp->opcnt == resp->opcnt;
}
int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op);
void warn_on_nonidempotent_op(struct nfsd4_op *op);
#define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs)
static inline void
set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp)
{
BUG_ON(!fhp->fh_pre_saved);
cinfo->atomic = fhp->fh_post_saved;
cinfo->change_supported = IS_I_VERSION(fhp->fh_dentry->d_inode);
cinfo->before_change = fhp->fh_pre_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;
}
int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *, void *);
int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *,
struct nfsd4_compoundargs *);
int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,
struct nfsd4_compoundres *);
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op);
__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry,
u32 *bmval, struct svc_rqst *, int ignore_crossmnt);
extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_setclientid *setclid);
extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_setclientid_confirm *setclientid_confirm);
extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_exchange_id *);
extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_backchannel_ctl *);
extern __be32 nfsd4_bind_conn_to_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_bind_conn_to_session *);
extern __be32 nfsd4_create_session(struct svc_rqst *,
struct nfsd4_compound_state *,
struct nfsd4_create_session *);
extern __be32 nfsd4_sequence(struct svc_rqst *,
struct nfsd4_compound_state *,
struct nfsd4_sequence *);
extern void nfsd4_sequence_done(struct nfsd4_compoundres *resp);
extern __be32 nfsd4_destroy_session(struct svc_rqst *,
struct nfsd4_compound_state *,
struct nfsd4_destroy_session *);
extern __be32 nfsd4_destroy_clientid(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_clientid *);
__be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *);
extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *,
struct nfsd4_open *open, struct nfsd_net *nn);
extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp,
struct svc_fh *current_fh, struct nfsd4_open *open);
extern void nfsd4_cstate_clear_replay(struct nfsd4_compound_state *cstate);
extern void nfsd4_cleanup_open_state(struct nfsd4_compound_state *cstate,
struct nfsd4_open *open, __be32 status);
extern __be32 nfsd4_open_confirm(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_open_confirm *oc);
extern __be32 nfsd4_close(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_close *close);
extern __be32 nfsd4_open_downgrade(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_open_downgrade *od);
extern __be32 nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *,
struct nfsd4_lock *lock);
extern __be32 nfsd4_lockt(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_lockt *lockt);
extern __be32 nfsd4_locku(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_locku *locku);
extern __be32
nfsd4_release_lockowner(struct svc_rqst *rqstp,
struct nfsd4_compound_state *,
struct nfsd4_release_lockowner *rlockowner);
extern int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp);
extern __be32 nfsd4_delegreturn(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_delegreturn *dr);
extern __be32 nfsd4_renew(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, clientid_t *clid);
extern __be32 nfsd4_test_stateid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_test_stateid *test_stateid);
extern __be32 nfsd4_free_stateid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_free_stateid *free_stateid);
extern void nfsd4_bump_seqid(struct nfsd4_compound_state *, __be32 nfserr);
#endif
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

23
fs/nfsd/xdr4cb.h Normal file
View file

@ -0,0 +1,23 @@
#define NFS4_MAXTAGLEN 20
#define NFS4_enc_cb_null_sz 0
#define NFS4_dec_cb_null_sz 0
#define cb_compound_enc_hdr_sz 4
#define cb_compound_dec_hdr_sz (3 + (NFS4_MAXTAGLEN >> 2))
#define sessionid_sz (NFS4_MAX_SESSIONID_LEN >> 2)
#define cb_sequence_enc_sz (sessionid_sz + 4 + \
1 /* no referring calls list yet */)
#define cb_sequence_dec_sz (op_dec_sz + sessionid_sz + 4)
#define op_enc_sz 1
#define op_dec_sz 2
#define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2))
#define enc_stateid_sz (NFS4_STATEID_SIZE >> 2)
#define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + enc_stateid_sz + \
enc_nfs4_fh_sz)
#define NFS4_dec_cb_recall_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \
op_dec_sz)