mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 00:38:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
106
fs/nfsd/Kconfig
Normal file
106
fs/nfsd/Kconfig
Normal 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
14
fs/nfsd/Makefile
Normal 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
59
fs/nfsd/acl.h
Normal 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
90
fs/nfsd/auth.c
Normal 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
16
fs/nfsd/auth.h
Normal 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
86
fs/nfsd/cache.h
Normal 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
28
fs/nfsd/current_stateid.h
Normal 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
1337
fs/nfsd/export.c
Normal file
File diff suppressed because it is too large
Load diff
111
fs/nfsd/export.h
Normal file
111
fs/nfsd/export.h
Normal 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
150
fs/nfsd/fault_inject.c
Normal 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
62
fs/nfsd/idmap.h
Normal 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
77
fs/nfsd/lockd.c
Normal 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
121
fs/nfsd/netns.h
Normal 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
382
fs/nfsd/nfs2acl.c
Normal 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
273
fs/nfsd/nfs3acl.c
Normal 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
891
fs/nfsd/nfs3proc.c
Normal 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
1114
fs/nfsd/nfs3xdr.c
Normal file
File diff suppressed because it is too large
Load diff
944
fs/nfsd/nfs4acl.c
Normal file
944
fs/nfsd/nfs4acl.c
Normal 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
1009
fs/nfsd/nfs4callback.c
Normal file
File diff suppressed because it is too large
Load diff
666
fs/nfsd/nfs4idmap.c
Normal file
666
fs/nfsd/nfs4idmap.c
Normal 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
2005
fs/nfsd/nfs4proc.c
Normal file
File diff suppressed because it is too large
Load diff
1550
fs/nfsd/nfs4recover.c
Normal file
1550
fs/nfsd/nfs4recover.c
Normal file
File diff suppressed because it is too large
Load diff
6596
fs/nfsd/nfs4state.c
Normal file
6596
fs/nfsd/nfs4state.c
Normal file
File diff suppressed because it is too large
Load diff
4102
fs/nfsd/nfs4xdr.c
Normal file
4102
fs/nfsd/nfs4xdr.c
Normal file
File diff suppressed because it is too large
Load diff
633
fs/nfsd/nfscache.c
Normal file
633
fs/nfsd/nfscache.c
Normal 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
1316
fs/nfsd/nfsctl.c
Normal file
File diff suppressed because it is too large
Load diff
410
fs/nfsd/nfsd.h
Normal file
410
fs/nfsd/nfsd.h
Normal 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
692
fs/nfsd/nfsfh.c
Normal 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
274
fs/nfsd/nfsfh.h
Normal 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
759
fs/nfsd/nfsproc.c
Normal 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
751
fs/nfsd/nfssvc.c
Normal 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
550
fs/nfsd/nfsxdr.c
Normal 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
601
fs/nfsd/state.h
Normal 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
104
fs/nfsd/stats.c
Normal 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
43
fs/nfsd/stats.h
Normal 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
2129
fs/nfsd/vfs.c
Normal file
File diff suppressed because it is too large
Load diff
129
fs/nfsd/vfs.h
Normal file
129
fs/nfsd/vfs.h
Normal 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
173
fs/nfsd/xdr.h
Normal 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
349
fs/nfsd/xdr3.h
Normal 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
656
fs/nfsd/xdr4.h
Normal 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
23
fs/nfsd/xdr4cb.h
Normal 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)
|
Loading…
Add table
Add a link
Reference in a new issue