Commit a6385625 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-2.6.30' of git://linux-nfs.org/~bfields/linux

* 'for-2.6.30' of git://linux-nfs.org/~bfields/linux: (81 commits)
  nfsd41: define nfsd4_set_statp as noop for !CONFIG_NFSD_V4
  nfsd41: define NFSD_DRC_SIZE_SHIFT in set_max_drc
  nfsd41: Documentation/filesystems/nfs41-server.txt
  nfsd41: CREATE_EXCLUSIVE4_1
  nfsd41: SUPPATTR_EXCLCREAT attribute
  nfsd41: support for 3-word long attribute bitmask
  nfsd: dynamically skip encoded fattr bitmap in _nfsd4_verify
  nfsd41: pass writable attrs mask to nfsd4_decode_fattr
  nfsd41: provide support for minor version 1 at rpc level
  nfsd41: control nfsv4.1 svc via /proc/fs/nfsd/versions
  nfsd41: add OPEN4_SHARE_ACCESS_WANT nfs4_stateid bmap
  nfsd41: access_valid
  nfsd41: clientid handling
  nfsd41: check encode size for sessions maxresponse cached
  nfsd41: stateid handling
  nfsd: pass nfsd4_compound_state* to nfs4_preprocess_{state,seq}id_op
  nfsd41: destroy_session operation
  nfsd41: non-page DRC for solo sequence responses
  nfsd41: Add a create session replay cache
  nfsd41: create_session operation
  ...
parents b24241a0 04826f43
Kernel NFS Server Statistics
============================
This document describes the format and semantics of the statistics
which the kernel NFS server makes available to userspace. These
statistics are available in several text form pseudo files, each of
which is described separately below.
In most cases you don't need to know these formats, as the nfsstat(8)
program from the nfs-utils distribution provides a helpful command-line
interface for extracting and printing them.
All the files described here are formatted as a sequence of text lines,
separated by newline '\n' characters. Lines beginning with a hash
'#' character are comments intended for humans and should be ignored
by parsing routines. All other lines contain a sequence of fields
separated by whitespace.
/proc/fs/nfsd/pool_stats
------------------------
This file is available in kernels from 2.6.30 onwards, if the
/proc/fs/nfsd filesystem is mounted (it almost always should be).
The first line is a comment which describes the fields present in
all the other lines. The other lines present the following data as
a sequence of unsigned decimal numeric fields. One line is shown
for each NFS thread pool.
All counters are 64 bits wide and wrap naturally. There is no way
to zero these counters, instead applications should do their own
rate conversion.
pool
The id number of the NFS thread pool to which this line applies.
This number does not change.
Thread pool ids are a contiguous set of small integers starting
at zero. The maximum value depends on the thread pool mode, but
currently cannot be larger than the number of CPUs in the system.
Note that in the default case there will be a single thread pool
which contains all the nfsd threads and all the CPUs in the system,
and thus this file will have a single line with a pool id of "0".
packets-arrived
Counts how many NFS packets have arrived. More precisely, this
is the number of times that the network stack has notified the
sunrpc server layer that new data may be available on a transport
(e.g. an NFS or UDP socket or an NFS/RDMA endpoint).
Depending on the NFS workload patterns and various network stack
effects (such as Large Receive Offload) which can combine packets
on the wire, this may be either more or less than the number
of NFS calls received (which statistic is available elsewhere).
However this is a more accurate and less workload-dependent measure
of how much CPU load is being placed on the sunrpc server layer
due to NFS network traffic.
sockets-enqueued
Counts how many times an NFS transport is enqueued to wait for
an nfsd thread to service it, i.e. no nfsd thread was considered
available.
The circumstance this statistic tracks indicates that there was NFS
network-facing work to be done but it couldn't be done immediately,
thus introducing a small delay in servicing NFS calls. The ideal
rate of change for this counter is zero; significantly non-zero
values may indicate a performance limitation.
This can happen either because there are too few nfsd threads in the
thread pool for the NFS workload (the workload is thread-limited),
or because the NFS workload needs more CPU time than is available in
the thread pool (the workload is CPU-limited). In the former case,
configuring more nfsd threads will probably improve the performance
of the NFS workload. In the latter case, the sunrpc server layer is
already choosing not to wake idle nfsd threads because there are too
many nfsd threads which want to run but cannot, so configuring more
nfsd threads will make no difference whatsoever. The overloads-avoided
statistic (see below) can be used to distinguish these cases.
threads-woken
Counts how many times an idle nfsd thread is woken to try to
receive some data from an NFS transport.
This statistic tracks the circumstance where incoming
network-facing NFS work is being handled quickly, which is a good
thing. The ideal rate of change for this counter will be close
to but less than the rate of change of the packets-arrived counter.
overloads-avoided
Counts how many times the sunrpc server layer chose not to wake an
nfsd thread, despite the presence of idle nfsd threads, because
too many nfsd threads had been recently woken but could not get
enough CPU time to actually run.
This statistic counts a circumstance where the sunrpc layer
heuristically avoids overloading the CPU scheduler with too many
runnable nfsd threads. The ideal rate of change for this counter
is zero. Significant non-zero values indicate that the workload
is CPU limited. Usually this is associated with heavy CPU usage
on all the CPUs in the nfsd thread pool.
If a sustained large overloads-avoided rate is detected on a pool,
the top(1) utility should be used to check for the following
pattern of CPU usage on all the CPUs associated with the given
nfsd thread pool.
- %us ~= 0 (as you're *NOT* running applications on your NFS server)
- %wa ~= 0
- %id ~= 0
- %sy + %hi + %si ~= 100
If this pattern is seen, configuring more nfsd threads will *not*
improve the performance of the workload. If this patten is not
seen, then something more subtle is wrong.
threads-timedout
Counts how many times an nfsd thread triggered an idle timeout,
i.e. was not woken to handle any incoming network packets for
some time.
This statistic counts a circumstance where there are more nfsd
threads configured than can be used by the NFS workload. This is
a clue that the number of nfsd threads can be reduced without
affecting performance. Unfortunately, it's only a clue and not
a strong indication, for a couple of reasons:
- Currently the rate at which the counter is incremented is quite
slow; the idle timeout is 60 minutes. Unless the NFS workload
remains constant for hours at a time, this counter is unlikely
to be providing information that is still useful.
- It is usually a wise policy to provide some slack,
i.e. configure a few more nfsds than are currently needed,
to allow for future spikes in load.
Note that incoming packets on NFS transports will be dealt with in
one of three ways. An nfsd thread can be woken (threads-woken counts
this case), or the transport can be enqueued for later attention
(sockets-enqueued counts this case), or the packet can be temporarily
deferred because the transport is currently being used by an nfsd
thread. This last case is not very interesting and is not explicitly
counted, but can be inferred from the other counters thus:
packets-deferred = packets-arrived - ( sockets-enqueued + threads-woken )
More
----
Descriptions of the other statistics file should go here.
Greg Banks <gnb@sgi.com>
26 Mar 2009
NFSv4.1 Server Implementation
Server support for minorversion 1 can be controlled using the
/proc/fs/nfsd/versions control file. The string output returned
by reading this file will contain either "+4.1" or "-4.1"
correspondingly.
Currently, server support for minorversion 1 is disabled by default.
It can be enabled at run time by writing the string "+4.1" to
the /proc/fs/nfsd/versions control file. Note that to write this
control file, the nfsd service must be taken down. Use your user-mode
nfs-utils to set this up; see rpc.nfsd(8)
The NFSv4 minorversion 1 (NFSv4.1) implementation in nfsd is based
on the latest NFSv4.1 Internet Draft:
http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-29
From the many new features in NFSv4.1 the current implementation
focuses on the mandatory-to-implement NFSv4.1 Sessions, providing
"exactly once" semantics and better control and throttling of the
resources allocated for each client.
Other NFSv4.1 features, Parallel NFS operations in particular,
are still under development out of tree.
See http://wiki.linux-nfs.org/wiki/index.php/PNFS_prototype_design
for more information.
The table below, taken from the NFSv4.1 document, lists
the operations that are mandatory to implement (REQ), optional
(OPT), and NFSv4.0 operations that are required not to implement (MNI)
in minor version 1. The first column indicates the operations that
are not supported yet by the linux server implementation.
The OPTIONAL features identified and their abbreviations are as follows:
pNFS Parallel NFS
FDELG File Delegations
DDELG Directory Delegations
The following abbreviations indicate the linux server implementation status.
I Implemented NFSv4.1 operations.
NS Not Supported.
NS* unimplemented optional feature.
P pNFS features implemented out of tree.
PNS pNFS features that are not supported yet (out of tree).
Operations
+----------------------+------------+--------------+----------------+
| Operation | REQ, REC, | Feature | Definition |
| | OPT, or | (REQ, REC, | |
| | MNI | or OPT) | |
+----------------------+------------+--------------+----------------+
| ACCESS | REQ | | Section 18.1 |
NS | BACKCHANNEL_CTL | REQ | | Section 18.33 |
NS | BIND_CONN_TO_SESSION | REQ | | Section 18.34 |
| CLOSE | REQ | | Section 18.2 |
| COMMIT | REQ | | Section 18.3 |
| CREATE | REQ | | Section 18.4 |
I | CREATE_SESSION | REQ | | Section 18.36 |
NS*| DELEGPURGE | OPT | FDELG (REQ) | Section 18.5 |
| DELEGRETURN | OPT | FDELG, | Section 18.6 |
| | | DDELG, pNFS | |
| | | (REQ) | |
NS | DESTROY_CLIENTID | REQ | | Section 18.50 |
I | DESTROY_SESSION | REQ | | Section 18.37 |
I | EXCHANGE_ID | REQ | | Section 18.35 |
NS | FREE_STATEID | REQ | | Section 18.38 |
| GETATTR | REQ | | Section 18.7 |
P | GETDEVICEINFO | OPT | pNFS (REQ) | Section 18.40 |
P | GETDEVICELIST | OPT | pNFS (OPT) | Section 18.41 |
| GETFH | REQ | | Section 18.8 |
NS*| GET_DIR_DELEGATION | OPT | DDELG (REQ) | Section 18.39 |
P | LAYOUTCOMMIT | OPT | pNFS (REQ) | Section 18.42 |
P | LAYOUTGET | OPT | pNFS (REQ) | Section 18.43 |
P | LAYOUTRETURN | OPT | pNFS (REQ) | Section 18.44 |
| LINK | OPT | | Section 18.9 |
| LOCK | REQ | | Section 18.10 |
| LOCKT | REQ | | Section 18.11 |
| LOCKU | REQ | | Section 18.12 |
| LOOKUP | REQ | | Section 18.13 |
| LOOKUPP | REQ | | Section 18.14 |
| NVERIFY | REQ | | Section 18.15 |
| OPEN | REQ | | Section 18.16 |
NS*| OPENATTR | OPT | | Section 18.17 |
| OPEN_CONFIRM | MNI | | N/A |
| OPEN_DOWNGRADE | REQ | | Section 18.18 |
| PUTFH | REQ | | Section 18.19 |
| PUTPUBFH | REQ | | Section 18.20 |
| PUTROOTFH | REQ | | Section 18.21 |
| READ | REQ | | Section 18.22 |
| READDIR | REQ | | Section 18.23 |
| READLINK | OPT | | Section 18.24 |
NS | RECLAIM_COMPLETE | REQ | | Section 18.51 |
| RELEASE_LOCKOWNER | MNI | | N/A |
| REMOVE | REQ | | Section 18.25 |
| RENAME | REQ | | Section 18.26 |
| RENEW | MNI | | N/A |
| RESTOREFH | REQ | | Section 18.27 |
| SAVEFH | REQ | | Section 18.28 |
| SECINFO | REQ | | Section 18.29 |
NS | SECINFO_NO_NAME | REC | pNFS files | Section 18.45, |
| | | layout (REQ) | Section 13.12 |
I | SEQUENCE | REQ | | Section 18.46 |
| SETATTR | REQ | | Section 18.30 |
| SETCLIENTID | MNI | | N/A |
| SETCLIENTID_CONFIRM | MNI | | N/A |
NS | SET_SSV | REQ | | Section 18.47 |
NS | TEST_STATEID | REQ | | Section 18.48 |
| VERIFY | REQ | | Section 18.31 |
NS*| WANT_DELEGATION | OPT | FDELG (OPT) | Section 18.49 |
| WRITE | REQ | | Section 18.32 |
Callback Operations
+-------------------------+-----------+-------------+---------------+
| Operation | REQ, REC, | Feature | Definition |
| | OPT, or | (REQ, REC, | |
| | MNI | or OPT) | |
+-------------------------+-----------+-------------+---------------+
| CB_GETATTR | OPT | FDELG (REQ) | Section 20.1 |
P | CB_LAYOUTRECALL | OPT | pNFS (REQ) | Section 20.3 |
NS*| CB_NOTIFY | OPT | DDELG (REQ) | Section 20.4 |
P | CB_NOTIFY_DEVICEID | OPT | pNFS (OPT) | Section 20.12 |
NS*| CB_NOTIFY_LOCK | OPT | | Section 20.11 |
NS*| CB_PUSH_DELEG | OPT | FDELG (OPT) | Section 20.5 |
| CB_RECALL | OPT | FDELG, | Section 20.2 |
| | | DDELG, pNFS | |
| | | (REQ) | |
NS*| CB_RECALL_ANY | OPT | FDELG, | Section 20.6 |
| | | DDELG, pNFS | |
| | | (REQ) | |
NS | CB_RECALL_SLOT | REQ | | Section 20.8 |
NS*| CB_RECALLABLE_OBJ_AVAIL | OPT | DDELG, pNFS | Section 20.7 |
| | | (REQ) | |
I | CB_SEQUENCE | OPT | FDELG, | Section 20.9 |
| | | DDELG, pNFS | |
| | | (REQ) | |
NS*| CB_WANTS_CANCELLED | OPT | FDELG, | Section 20.10 |
| | | DDELG, pNFS | |
| | | (REQ) | |
+-------------------------+-----------+-------------+---------------+
Implementation notes:
EXCHANGE_ID:
* only SP4_NONE state protection supported
* implementation ids are ignored
CREATE_SESSION:
* backchannel attributes are ignored
* backchannel security parameters are ignored
SEQUENCE:
* no support for dynamic slot table renegotiation (optional)
nfsv4.1 COMPOUND rules:
The following cases aren't supported yet:
* Enforcing of NFS4ERR_NOT_ONLY_OP for: BIND_CONN_TO_SESSION, CREATE_SESSION,
DESTROY_CLIENTID, DESTROY_SESSION, EXCHANGE_ID.
* DESTROY_SESSION MUST be the final operation in the COMPOUND request.
...@@ -426,8 +426,15 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ...@@ -426,8 +426,15 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
ret = nlm_granted; ret = nlm_granted;
goto out; goto out;
case -EAGAIN: case -EAGAIN:
/*
* If this is a blocking request for an
* already pending lock request then we need
* to put it back on lockd's block list
*/
if (wait)
break;
ret = nlm_lck_denied; ret = nlm_lck_denied;
break; goto out;
case FILE_LOCK_DEFERRED: case FILE_LOCK_DEFERRED:
if (wait) if (wait)
break; break;
...@@ -443,10 +450,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ...@@ -443,10 +450,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out; goto out;
} }
ret = nlm_lck_denied;
if (!wait)
goto out;
ret = nlm_lck_blocked; ret = nlm_lck_blocked;
/* Append to list of blocked */ /* Append to list of blocked */
......
config NFSD config NFSD
tristate "NFS server support" tristate "NFS server support"
depends on INET depends on INET
depends on FILE_LOCKING
select LOCKD select LOCKD
select SUNRPC select SUNRPC
select EXPORTFS select EXPORTFS
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/major.h> #include <linux/major.h>
#include <linux/magic.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
...@@ -202,6 +203,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp, ...@@ -202,6 +203,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
struct nfsd3_writeres *resp) struct nfsd3_writeres *resp)
{ {
__be32 nfserr; __be32 nfserr;
unsigned long cnt = argp->len;
dprintk("nfsd: WRITE(3) %s %d bytes at %ld%s\n", dprintk("nfsd: WRITE(3) %s %d bytes at %ld%s\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
...@@ -214,9 +216,9 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp, ...@@ -214,9 +216,9 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
nfserr = nfsd_write(rqstp, &resp->fh, NULL, nfserr = nfsd_write(rqstp, &resp->fh, NULL,
argp->offset, argp->offset,
rqstp->rq_vec, argp->vlen, rqstp->rq_vec, argp->vlen,
argp->len, &cnt,
&resp->committed); &resp->committed);
resp->count = argp->count; resp->count = cnt;
RETURN_STATUS(nfserr); RETURN_STATUS(nfserr);
} }
...@@ -569,7 +571,7 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, ...@@ -569,7 +571,7 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb; struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
/* Note that we don't care for remote fs's here */ /* Note that we don't care for remote fs's here */
if (sb->s_magic == 0x4d44 /* MSDOS_SUPER_MAGIC */) { if (sb->s_magic == MSDOS_SUPER_MAGIC) {
resp->f_properties = NFS3_FSF_BILLYBOY; resp->f_properties = NFS3_FSF_BILLYBOY;
} }
resp->f_maxfilesize = sb->s_maxbytes; resp->f_maxfilesize = sb->s_maxbytes;
...@@ -610,7 +612,7 @@ nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, ...@@ -610,7 +612,7 @@ nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
resp->p_link_max = EXT2_LINK_MAX; resp->p_link_max = EXT2_LINK_MAX;
resp->p_name_max = EXT2_NAME_LEN; resp->p_name_max = EXT2_NAME_LEN;
break; break;
case 0x4d44: /* MSDOS_SUPER_MAGIC */ case MSDOS_SUPER_MAGIC:
resp->p_case_insensitive = 1; resp->p_case_insensitive = 1;
resp->p_case_preserving = 0; resp->p_case_preserving = 0;
break; break;
......
...@@ -218,7 +218,7 @@ static int ...@@ -218,7 +218,7 @@ static int
encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec) encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
{ {
__be32 *p; __be32 *p;
int len = cb_rec->cbr_fhlen; int len = cb_rec->cbr_fh.fh_size;
RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len); RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len);
WRITE32(OP_CB_RECALL); WRITE32(OP_CB_RECALL);
...@@ -226,7 +226,7 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec) ...@@ -226,7 +226,7 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t)); WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t));
WRITE32(cb_rec->cbr_trunc); WRITE32(cb_rec->cbr_trunc);
WRITE32(len); WRITE32(len);
WRITEMEM(cb_rec->cbr_fhval, len); WRITEMEM(&cb_rec->cbr_fh.fh_base, len);
return 0; return 0;
} }
...@@ -361,9 +361,8 @@ static struct rpc_program cb_program = { ...@@ -361,9 +361,8 @@ static struct rpc_program cb_program = {
/* Reference counting, callback cleanup, etc., all look racy as heck. /* Reference counting, callback cleanup, etc., all look racy as heck.
* And why is cb_set an atomic? */ * And why is cb_set an atomic? */
static int do_probe_callback(void *data) static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
{ {
struct nfs4_client *clp = data;
struct sockaddr_in addr; struct sockaddr_in addr;
struct nfs4_callback *cb = &clp->cl_callback; struct nfs4_callback *cb = &clp->cl_callback;
struct rpc_timeout timeparms = { struct rpc_timeout timeparms = {
...@@ -384,17 +383,10 @@ static int do_probe_callback(void *data) ...@@ -384,17 +383,10 @@ static int do_probe_callback(void *data)
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
.client_name = clp->cl_principal, .client_name = clp->cl_principal,
}; };
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp,
};
struct rpc_clnt *client; struct rpc_clnt *client;
int status;
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) { if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
status = nfserr_cb_path_down; return ERR_PTR(-EINVAL);
goto out_err;
}
/* Initialize address */ /* Initialize address */
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
...@@ -404,9 +396,29 @@ static int do_probe_callback(void *data) ...@@ -404,9 +396,29 @@ static int do_probe_callback(void *data)
/* Create RPC client */ /* Create RPC client */
client = rpc_create(&args); client = rpc_create(&args);
if (IS_ERR(client))
dprintk("NFSD: couldn't create callback client: %ld\n",
PTR_ERR(client));
return client;
}
static int do_probe_callback(void *data)
{
struct nfs4_client *clp = data;
struct nfs4_callback *cb = &clp->cl_callback;
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp,
};
struct rpc_clnt *client;
int status;
client = setup_callback_client(clp);
if (IS_ERR(client)) { if (IS_ERR(client)) {
dprintk("NFSD: couldn't create callback client\n");
status = PTR_ERR(client); status = PTR_ERR(client);
dprintk("NFSD: couldn't create callback client: %d\n",
status);
goto out_err; goto out_err;
} }
...@@ -422,10 +434,10 @@ static int do_probe_callback(void *data) ...@@ -422,10 +434,10 @@ static int do_probe_callback(void *data)
out_release_client: out_release_client:
rpc_shutdown_client(client); rpc_shutdown_client(client);
out_err: out_err:
dprintk("NFSD: warning: no callback path to client %.*s\n", dprintk("NFSD: warning: no callback path to client %.*s: error %d\n",
(int)clp->cl_name.len, clp->cl_name.data); (int)clp->cl_name.len, clp->cl_name.data, status);
put_nfs4_client(clp); put_nfs4_client(clp);
return status; return 0;
} }
/* /*
...@@ -451,7 +463,6 @@ nfsd4_probe_callback(struct nfs4_client *clp) ...@@ -451,7 +463,6 @@ nfsd4_probe_callback(struct nfs4_client *clp)
/* /*
* called with dp->dl_count inc'ed. * called with dp->dl_count inc'ed.
* nfs4_lock_state() may or may not have been called.
*/ */
void void
nfsd4_cb_recall(struct nfs4_delegation *dp) nfsd4_cb_recall(struct nfs4_delegation *dp)
......
...@@ -93,6 +93,21 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o ...@@ -93,6 +93,21 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
open->op_truncate = 0; open->op_truncate = 0;
if (open->op_create) { if (open->op_create) {
/* FIXME: check session persistence and pnfs flags.
* The nfsv4.1 spec requires the following semantics:
*
* Persistent | pNFS | Server REQUIRED | Client Allowed
* Reply Cache | server | |
* -------------+--------+-----------------+--------------------
* no | no | EXCLUSIVE4_1 | EXCLUSIVE4_1
* | | | (SHOULD)
* | | and EXCLUSIVE4 | or EXCLUSIVE4
* | | | (SHOULD NOT)
* no | yes | EXCLUSIVE4_1 | EXCLUSIVE4_1
* yes | no | GUARDED4 | GUARDED4
* yes | yes | GUARDED4 | GUARDED4
*/
/* /*
* Note: create modes (UNCHECKED,GUARDED...) are the same * Note: create modes (UNCHECKED,GUARDED...) are the same
* in NFSv4 as in v3. * in NFSv4 as in v3.
...@@ -103,11 +118,13 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o ...@@ -103,11 +118,13 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
(u32 *)open->op_verf.data, (u32 *)open->op_verf.data,
&open->op_truncate, &created); &open->op_truncate, &created);
/* If we ever decide to use different attrs to store the /*
* verifier in nfsd_create_v3, then we'll need to change this * Following rfc 3530 14.2.16, use the returned bitmask
* to indicate which attributes we used to store the
* verifier:
*/ */
if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0) if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0)
open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS | open->op_bmval[1] = (FATTR4_WORD1_TIME_ACCESS |
FATTR4_WORD1_TIME_MODIFY); FATTR4_WORD1_TIME_MODIFY);
} else { } else {
status = nfsd_lookup(rqstp, current_fh, status = nfsd_lookup(rqstp, current_fh,
...@@ -118,13 +135,11 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o ...@@ -118,13 +135,11 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
goto out; goto out;
set_change_info(&open->op_cinfo, current_fh); set_change_info(&open->op_cinfo, current_fh);
/* set reply cache */
fh_dup2(current_fh, &resfh); fh_dup2(current_fh, &resfh);
open->op_stateowner->so_replay.rp_openfh_len = resfh.fh_handle.fh_size;
memcpy(open->op_stateowner->so_replay.rp_openfh,
&resfh.fh_handle.fh_base, resfh.fh_handle.fh_size);
/* set reply cache */
fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh,
&resfh.fh_handle);
if (!created) if (!created)
status = do_open_permission(rqstp, current_fh, open, status = do_open_permission(rqstp, current_fh, open,
NFSD_MAY_NOP); NFSD_MAY_NOP);
...@@ -150,10 +165,8 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ ...@@ -150,10 +165,8 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_
memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info)); memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info));
/* set replay cache */ /* set replay cache */
open->op_stateowner->so_replay.rp_openfh_len = current_fh->fh_handle.fh_size; fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh,
memcpy(open->op_stateowner->so_replay.rp_openfh, &current_fh->fh_handle);
&current_fh->fh_handle.fh_base,
current_fh->fh_handle.fh_size);
open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) &&
(open->op_iattr.ia_size == 0); (open->op_iattr.ia_size == 0);
...@@ -164,12 +177,23 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ ...@@ -164,12 +177,23 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_
return status; return status;
} }
static void
copy_clientid(clientid_t *clid, struct nfsd4_session *session)
{
struct nfsd4_sessionid *sid =
(struct nfsd4_sessionid *)session->se_sessionid.data;
clid->cl_boot = sid->clientid.cl_boot;
clid->cl_id = sid->clientid.cl_id;
}
static __be32 static __be32
nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_open *open) struct nfsd4_open *open)
{ {
__be32 status; __be32 status;
struct nfsd4_compoundres *resp;
dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n", dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n",
(int)open->op_fname.len, open->op_fname.data, (int)open->op_fname.len, open->op_fname.data,
open->op_stateowner); open->op_stateowner);
...@@ -178,16 +202,19 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -178,16 +202,19 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
return nfserr_inval; return nfserr_inval;
if (nfsd4_has_session(cstate))
copy_clientid(&open->op_clientid, cstate->session);
nfs4_lock_state(); nfs4_lock_state();
/* check seqid for replay. set nfs4_owner */ /* check seqid for replay. set nfs4_owner */
status = nfsd4_process_open1(open); resp = rqstp->rq_resp;
status = nfsd4_process_open1(&resp->cstate, open);
if (status == nfserr_replay_me) { if (status == nfserr_replay_me) {
struct nfs4_replay *rp = &open->op_stateowner->so_replay; struct nfs4_replay *rp = &open->op_stateowner->so_replay;
fh_put(&cstate->current_fh); fh_put(&cstate->current_fh);
cstate->current_fh.fh_handle.fh_size = rp->rp_openfh_len; fh_copy_shallow(&cstate->current_fh.fh_handle,
memcpy(&cstate->current_fh.fh_handle.fh_base, rp->rp_openfh, &rp->rp_openfh);
rp->rp_openfh_len);
status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP);
if (status) if (status)
dprintk("nfsd4_open: replay failed" dprintk("nfsd4_open: replay failed"
...@@ -209,10 +236,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -209,10 +236,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
switch (open->op_claim_type) { switch (open->op_claim_type) {
case NFS4_OPEN_CLAIM_DELEGATE_CUR: case NFS4_OPEN_CLAIM_DELEGATE_CUR:
status = nfserr_inval;
if (open->op_create)
goto out;
/* fall through */
case NFS4_OPEN_CLAIM_NULL: case NFS4_OPEN_CLAIM_NULL:
/* /*
* (1) set CURRENT_FH to the file being opened, * (1) set CURRENT_FH to the file being opened,
...@@ -455,8 +478,9 @@ nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -455,8 +478,9 @@ nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
return nfserr_inval; return nfserr_inval;
getattr->ga_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; getattr->ga_bmval[0] &= nfsd_suppattrs0(cstate->minorversion);
getattr->ga_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; getattr->ga_bmval[1] &= nfsd_suppattrs1(cstate->minorversion);
getattr->ga_bmval[2] &= nfsd_suppattrs2(cstate->minorversion);
getattr->ga_fhp = &cstate->current_fh; getattr->ga_fhp = &cstate->current_fh;
return nfs_ok; return nfs_ok;
...@@ -520,9 +544,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -520,9 +544,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_lock_state(); nfs4_lock_state();
/* check stateid */ /* check stateid */
if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh, if ((status = nfs4_preprocess_stateid_op(cstate, &read->rd_stateid,
&read->rd_stateid, RD_STATE, &read->rd_filp))) {
CHECK_FH | RD_STATE, &read->rd_filp))) {
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
goto out; goto out;
} }
...@@ -548,8 +571,9 @@ nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -548,8 +571,9 @@ nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
return nfserr_inval; return nfserr_inval;
readdir->rd_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; readdir->rd_bmval[0] &= nfsd_suppattrs0(cstate->minorversion);
readdir->rd_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; readdir->rd_bmval[1] &= nfsd_suppattrs1(cstate->minorversion);
readdir->rd_bmval[2] &= nfsd_suppattrs2(cstate->minorversion);
if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) || if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) ||
(cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE))) (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE)))
...@@ -653,8 +677,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -653,8 +677,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
nfs4_lock_state(); nfs4_lock_state();
status = nfs4_preprocess_stateid_op(&cstate->current_fh, status = nfs4_preprocess_stateid_op(cstate,
&setattr->sa_stateid, CHECK_FH | WR_STATE, NULL); &setattr->sa_stateid, WR_STATE, NULL);
nfs4_unlock_state(); nfs4_unlock_state();
if (status) { if (status) {
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
...@@ -685,6 +709,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -685,6 +709,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct file *filp = NULL; struct file *filp = NULL;
u32 *p; u32 *p;
__be32 status = nfs_ok; __be32 status = nfs_ok;
unsigned long cnt;
/* no need to check permission - this will be done in nfsd_write() */ /* no need to check permission - this will be done in nfsd_write() */
...@@ -692,8 +717,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -692,8 +717,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_inval; return nfserr_inval;
nfs4_lock_state(); nfs4_lock_state();
status = nfs4_preprocess_stateid_op(&cstate->current_fh, stateid, status = nfs4_preprocess_stateid_op(cstate, stateid, WR_STATE, &filp);
CHECK_FH | WR_STATE, &filp);
if (filp) if (filp)
get_file(filp); get_file(filp);
nfs4_unlock_state(); nfs4_unlock_state();
...@@ -703,7 +727,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -703,7 +727,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status; return status;
} }
write->wr_bytes_written = write->wr_buflen; cnt = write->wr_buflen;
write->wr_how_written = write->wr_stable_how; write->wr_how_written = write->wr_stable_how;
p = (u32 *)write->wr_verifier.data; p = (u32 *)write->wr_verifier.data;
*p++ = nfssvc_boot.tv_sec; *p++ = nfssvc_boot.tv_sec;
...@@ -711,10 +735,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -711,10 +735,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfsd_write(rqstp, &cstate->current_fh, filp, status = nfsd_write(rqstp, &cstate->current_fh, filp,
write->wr_offset, rqstp->rq_vec, write->wr_vlen, write->wr_offset, rqstp->rq_vec, write->wr_vlen,
write->wr_buflen, &write->wr_how_written); &cnt, &write->wr_how_written);
if (filp) if (filp)
fput(filp); fput(filp);
write->wr_bytes_written = cnt;
if (status == nfserr_symlink) if (status == nfserr_symlink)
status = nfserr_inval; status = nfserr_inval;
return status; return status;
...@@ -737,8 +763,9 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -737,8 +763,9 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status) if (status)
return status; return status;
if ((verify->ve_bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) if ((verify->ve_bmval[0] & ~nfsd_suppattrs0(cstate->minorversion))
|| (verify->ve_bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1)) || (verify->ve_bmval[1] & ~nfsd_suppattrs1(cstate->minorversion))
|| (verify->ve_bmval[2] & ~nfsd_suppattrs2(cstate->minorversion)))
return nfserr_attrnotsupp; return nfserr_attrnotsupp;
if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR) if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
|| (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1))
...@@ -766,7 +793,8 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -766,7 +793,8 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status) if (status)
goto out_kfree; goto out_kfree;
p = buf + 3; /* skip bitmap */
p = buf + 1 + ntohl(buf[0]);
status = nfserr_not_same; status = nfserr_not_same;
if (ntohl(*p++) != verify->ve_attrlen) if (ntohl(*p++) != verify->ve_attrlen)
goto out_kfree; goto out_kfree;
...@@ -813,39 +841,17 @@ static inline void nfsd4_increment_op_stats(u32 opnum) ...@@ -813,39 +841,17 @@ static inline void nfsd4_increment_op_stats(u32 opnum)
nfsdstats.nfs4_opcount[opnum]++; nfsdstats.nfs4_opcount[opnum]++;
} }
static void cstate_free(struct nfsd4_compound_state *cstate)
{
if (cstate == NULL)
return;
fh_put(&cstate->current_fh);
fh_put(&cstate->save_fh);
BUG_ON(cstate->replay_owner);
kfree(cstate);
}
static struct nfsd4_compound_state *cstate_alloc(void)
{
struct nfsd4_compound_state *cstate;
cstate = kmalloc(sizeof(struct nfsd4_compound_state), GFP_KERNEL);
if (cstate == NULL)
return NULL;
fh_init(&cstate->current_fh, NFS4_FHSIZE);
fh_init(&cstate->save_fh, NFS4_FHSIZE);
cstate->replay_owner = NULL;
return cstate;
}
typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
void *); void *);
enum nfsd4_op_flags {
ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */
ALLOWED_ON_ABSENT_FS = 2 << 0, /* ops processed on absent fs */
ALLOWED_AS_FIRST_OP = 3 << 0, /* ops reqired first in compound */
};
struct nfsd4_operation { struct nfsd4_operation {
nfsd4op_func op_func; nfsd4op_func op_func;
u32 op_flags; u32 op_flags;
/* Most ops require a valid current filehandle; a few don't: */
#define ALLOWED_WITHOUT_FH 1
/* GETATTR and ops not listed as returning NFS4ERR_MOVED: */
#define ALLOWED_ON_ABSENT_FS 2
char *op_name; char *op_name;
}; };
...@@ -853,6 +859,51 @@ static struct nfsd4_operation nfsd4_ops[]; ...@@ -853,6 +859,51 @@ static struct nfsd4_operation nfsd4_ops[];
static const char *nfsd4_op_name(unsigned opnum); static const char *nfsd4_op_name(unsigned opnum);
/*
* This is a replay of a compound for which no cache entry pages
* were used. Encode the sequence operation, and if cachethis is FALSE
* encode the uncache rep error on the next operation.
*/
static __be32
nfsd4_enc_uncached_replay(struct nfsd4_compoundargs *args,
struct nfsd4_compoundres *resp)
{
struct nfsd4_op *op;
dprintk("--> %s resp->opcnt %d ce_cachethis %u \n", __func__,
resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis);
/* Encode the replayed sequence operation */
BUG_ON(resp->opcnt != 1);
op = &args->ops[resp->opcnt - 1];
nfsd4_encode_operation(resp, op);
/*return nfserr_retry_uncached_rep in next operation. */
if (resp->cstate.slot->sl_cache_entry.ce_cachethis == 0) {
op = &args->ops[resp->opcnt++];
op->status = nfserr_retry_uncached_rep;
nfsd4_encode_operation(resp, op);
}
return op->status;
}
/*
* Enforce NFSv4.1 COMPOUND ordering rules.
*
* TODO:
* - enforce NFS4ERR_NOT_ONLY_OP,
* - DESTROY_SESSION MUST be the final operation in the COMPOUND request.
*/
static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args)
{
if (args->minorversion && args->opcnt > 0) {
struct nfsd4_op *op = &args->ops[0];
return (op->status == nfserr_op_illegal) ||
(nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP);
}
return true;
}
/* /*
* COMPOUND call. * COMPOUND call.
*/ */
...@@ -863,12 +914,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -863,12 +914,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
{ {
struct nfsd4_op *op; struct nfsd4_op *op;
struct nfsd4_operation *opdesc; struct nfsd4_operation *opdesc;
struct nfsd4_compound_state *cstate = NULL; struct nfsd4_compound_state *cstate = &resp->cstate;
int slack_bytes; int slack_bytes;
__be32 status; __be32 status;
resp->xbuf = &rqstp->rq_res; resp->xbuf = &rqstp->rq_res;
resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len; resp->p = rqstp->rq_res.head[0].iov_base +
rqstp->rq_res.head[0].iov_len;
resp->tagp = resp->p; resp->tagp = resp->p;
/* reserve space for: taglen, tag, and opcnt */ /* reserve space for: taglen, tag, and opcnt */
resp->p += 2 + XDR_QUADLEN(args->taglen); resp->p += 2 + XDR_QUADLEN(args->taglen);
...@@ -877,18 +929,25 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -877,18 +929,25 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
resp->tag = args->tag; resp->tag = args->tag;
resp->opcnt = 0; resp->opcnt = 0;
resp->rqstp = rqstp; resp->rqstp = rqstp;
resp->cstate.minorversion = args->minorversion;
resp->cstate.replay_owner = NULL;
fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
/* Use the deferral mechanism only for NFSv4.0 compounds */
rqstp->rq_usedeferral = (args->minorversion == 0);
/* /*
* According to RFC3010, this takes precedence over all other errors. * According to RFC3010, this takes precedence over all other errors.
*/ */
status = nfserr_minor_vers_mismatch; status = nfserr_minor_vers_mismatch;
if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION) if (args->minorversion > nfsd_supported_minorversion)
goto out; goto out;
status = nfserr_resource; if (!nfs41_op_ordering_ok(args)) {
cstate = cstate_alloc(); op = &args->ops[0];
if (cstate == NULL) op->status = nfserr_sequence_pos;
goto out; goto encode_op;
}
status = nfs_ok; status = nfs_ok;
while (!status && resp->opcnt < args->opcnt) { while (!status && resp->opcnt < args->opcnt) {
...@@ -897,7 +956,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -897,7 +956,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
dprintk("nfsv4 compound op #%d/%d: %d (%s)\n", dprintk("nfsv4 compound op #%d/%d: %d (%s)\n",
resp->opcnt, args->opcnt, op->opnum, resp->opcnt, args->opcnt, op->opnum,
nfsd4_op_name(op->opnum)); nfsd4_op_name(op->opnum));
/* /*
* The XDR decode routines may have pre-set op->status; * The XDR decode routines may have pre-set op->status;
* for example, if there is a miscellaneous XDR error * for example, if there is a miscellaneous XDR error
...@@ -938,6 +996,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -938,6 +996,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
BUG_ON(op->status == nfs_ok); BUG_ON(op->status == nfs_ok);
encode_op: encode_op:
/* Only from SEQUENCE or CREATE_SESSION */
if (resp->cstate.status == nfserr_replay_cache) {
dprintk("%s NFS4.1 replay from cache\n", __func__);
if (nfsd4_not_cached(resp))
status = nfsd4_enc_uncached_replay(args, resp);
else
status = op->status;
goto out;
}
if (op->status == nfserr_replay_me) { if (op->status == nfserr_replay_me) {
op->replay = &cstate->replay_owner->so_replay; op->replay = &cstate->replay_owner->so_replay;
nfsd4_encode_replay(resp, op); nfsd4_encode_replay(resp, op);
...@@ -961,15 +1028,24 @@ encode_op: ...@@ -961,15 +1028,24 @@ encode_op:
nfsd4_increment_op_stats(op->opnum); nfsd4_increment_op_stats(op->opnum);
} }
if (!rqstp->rq_usedeferral && status == nfserr_dropit) {
dprintk("%s Dropit - send NFS4ERR_DELAY\n", __func__);
status = nfserr_jukebox;
}
cstate_free(cstate); resp->cstate.status = status;
fh_put(&resp->cstate.current_fh);
fh_put(&resp->cstate.save_fh);
BUG_ON(resp->cstate.replay_owner);
out: out:
nfsd4_release_compoundargs(args); nfsd4_release_compoundargs(args);
/* Reset deferral mechanism for RPC deferrals */
rqstp->rq_usedeferral = 1;
dprintk("nfsv4 compound returned %d\n", ntohl(status)); dprintk("nfsv4 compound returned %d\n", ntohl(status));
return status; return status;
} }
static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { static struct nfsd4_operation nfsd4_ops[] = {
[OP_ACCESS] = { [OP_ACCESS] = {
.op_func = (nfsd4op_func)nfsd4_access, .op_func = (nfsd4op_func)nfsd4_access,
.op_name = "OP_ACCESS", .op_name = "OP_ACCESS",
...@@ -1045,7 +1121,7 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { ...@@ -1045,7 +1121,7 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = {
.op_name = "OP_PUTFH", .op_name = "OP_PUTFH",
}, },
[OP_PUTPUBFH] = { [OP_PUTPUBFH] = {
/* unsupported, just for future reference: */ .op_func = (nfsd4op_func)nfsd4_putrootfh,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
.op_name = "OP_PUTPUBFH", .op_name = "OP_PUTPUBFH",
}, },
...@@ -1119,6 +1195,28 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { ...@@ -1119,6 +1195,28 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = {
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
.op_name = "OP_RELEASE_LOCKOWNER", .op_name = "OP_RELEASE_LOCKOWNER",
}, },
/* NFSv4.1 operations */
[OP_EXCHANGE_ID] = {
.op_func = (nfsd4op_func)nfsd4_exchange_id,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
.op_name = "OP_EXCHANGE_ID",
},
[OP_CREATE_SESSION] = {
.op_func = (nfsd4op_func)nfsd4_create_session,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
.op_name = "OP_CREATE_SESSION",
},
[OP_DESTROY_SESSION] = {
.op_func = (nfsd4op_func)nfsd4_destroy_session,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
.op_name = "OP_DESTROY_SESSION",
},
[OP_SEQUENCE] = {
.op_func = (nfsd4op_func)nfsd4_sequence,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
.op_name = "OP_SEQUENCE",
},
}; };
static const char *nfsd4_op_name(unsigned opnum) static const char *nfsd4_op_name(unsigned opnum)
......
...@@ -182,36 +182,26 @@ out_unlock: ...@@ -182,36 +182,26 @@ out_unlock:
typedef int (recdir_func)(struct dentry *, struct dentry *); typedef int (recdir_func)(struct dentry *, struct dentry *);
struct dentry_list { struct name_list {
struct dentry *dentry; char name[HEXDIR_LEN];
struct list_head list; struct list_head list;
}; };
struct dentry_list_arg {
struct list_head dentries;
struct dentry *parent;
};
static int static int
nfsd4_build_dentrylist(void *arg, const char *name, int namlen, nfsd4_build_namelist(void *arg, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type) loff_t offset, u64 ino, unsigned int d_type)
{ {
struct dentry_list_arg *dla = arg; struct list_head *names = arg;
struct list_head *dentries = &dla->dentries; struct name_list *entry;
struct dentry *parent = dla->parent;
struct dentry *dentry;
struct dentry_list *child;
if (name && isdotent(name, namlen)) if (namlen != HEXDIR_LEN - 1)
return 0; return 0;
dentry = lookup_one_len(name, parent, namlen); entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
if (IS_ERR(dentry)) if (entry == NULL)
return PTR_ERR(dentry);
child = kmalloc(sizeof(*child), GFP_KERNEL);
if (child == NULL)
return -ENOMEM; return -ENOMEM;
child->dentry = dentry; memcpy(entry->name, name, HEXDIR_LEN - 1);
list_add(&child->list, dentries); entry->name[HEXDIR_LEN - 1] = '\0';
list_add(&entry->list, names);
return 0; return 0;
} }
...@@ -220,11 +210,9 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f) ...@@ -220,11 +210,9 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
{ {
const struct cred *original_cred; const struct cred *original_cred;
struct file *filp; struct file *filp;
struct dentry_list_arg dla = { LIST_HEAD(names);
.parent = dir, struct name_list *entry;
}; struct dentry *dentry;
struct list_head *dentries = &dla.dentries;
struct dentry_list *child;
int status; int status;
if (!rec_dir_init) if (!rec_dir_init)
...@@ -233,31 +221,34 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f) ...@@ -233,31 +221,34 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
status = nfs4_save_creds(&original_cred); status = nfs4_save_creds(&original_cred);
if (status < 0) if (status < 0)
return status; return status;
INIT_LIST_HEAD(dentries);
filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY, filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY,
current_cred()); current_cred());
status = PTR_ERR(filp); status = PTR_ERR(filp);
if (IS_ERR(filp)) if (IS_ERR(filp))
goto out; goto out;
INIT_LIST_HEAD(dentries); status = vfs_readdir(filp, nfsd4_build_namelist, &names);
status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
fput(filp); fput(filp);
while (!list_empty(dentries)) { while (!list_empty(&names)) {
child = list_entry(dentries->next, struct dentry_list, list); entry = list_entry(names.next, struct name_list, list);
status = f(dir, child->dentry);
dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
if (IS_ERR(dentry)) {
status = PTR_ERR(dentry);
goto out;
}
status = f(dir, dentry);
dput(dentry);
if (status) if (status)
goto out; goto out;
list_del(&child->list); list_del(&entry->list);
dput(child->dentry); kfree(entry);
kfree(child);
} }
out: out:
while (!list_empty(dentries)) { while (!list_empty(&names)) {
child = list_entry(dentries->next, struct dentry_list, list); entry = list_entry(names.next, struct name_list, list);
list_del(&child->list); list_del(&entry->list);
dput(child->dentry); kfree(entry);
kfree(child);
} }
nfs4_reset_creds(original_cred); nfs4_reset_creds(original_cred);
return status; return status;
...@@ -353,7 +344,8 @@ purge_old(struct dentry *parent, struct dentry *child) ...@@ -353,7 +344,8 @@ purge_old(struct dentry *parent, struct dentry *child)
{ {
int status; int status;
if (nfs4_has_reclaimed_state(child->d_name.name)) /* note: we currently use this path only for minorversion 0 */
if (nfs4_has_reclaimed_state(child->d_name.name, false))
return 0; return 0;
status = nfsd4_clear_clid_dir(parent, child); status = nfsd4_clear_clid_dir(parent, child);
......
This diff is collapsed.
This diff is collapsed.
...@@ -60,6 +60,7 @@ enum { ...@@ -60,6 +60,7 @@ enum {
NFSD_FO_UnlockFS, NFSD_FO_UnlockFS,
NFSD_Threads, NFSD_Threads,
NFSD_Pool_Threads, NFSD_Pool_Threads,
NFSD_Pool_Stats,
NFSD_Versions, NFSD_Versions,
NFSD_Ports, NFSD_Ports,
NFSD_MaxBlkSize, NFSD_MaxBlkSize,
...@@ -172,6 +173,16 @@ static const struct file_operations exports_operations = { ...@@ -172,6 +173,16 @@ static const struct file_operations exports_operations = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
extern int nfsd_pool_stats_open(struct inode *inode, struct file *file);
static struct file_operations pool_stats_operations = {
.open = nfsd_pool_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.owner = THIS_MODULE,
};
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
/* /*
* payload - write methods * payload - write methods
...@@ -781,8 +792,9 @@ out_free: ...@@ -781,8 +792,9 @@ out_free:
static ssize_t __write_versions(struct file *file, char *buf, size_t size) static ssize_t __write_versions(struct file *file, char *buf, size_t size)
{ {
char *mesg = buf; char *mesg = buf;
char *vers, sign; char *vers, *minorp, sign;
int len, num; int len, num;
unsigned minor;
ssize_t tlen = 0; ssize_t tlen = 0;
char *sep; char *sep;
...@@ -803,9 +815,20 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -803,9 +815,20 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
do { do {
sign = *vers; sign = *vers;
if (sign == '+' || sign == '-') if (sign == '+' || sign == '-')
num = simple_strtol((vers+1), NULL, 0); num = simple_strtol((vers+1), &minorp, 0);
else else
num = simple_strtol(vers, NULL, 0); num = simple_strtol(vers, &minorp, 0);
if (*minorp == '.') {
if (num < 4)
return -EINVAL;
minor = simple_strtoul(minorp+1, NULL, 0);
if (minor == 0)
return -EINVAL;
if (nfsd_minorversion(minor, sign == '-' ?
NFSD_CLEAR : NFSD_SET) < 0)
return -EINVAL;
goto next;
}
switch(num) { switch(num) {
case 2: case 2:
case 3: case 3:
...@@ -815,6 +838,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -815,6 +838,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
default: default:
return -EINVAL; return -EINVAL;
} }
next:
vers += len + 1; vers += len + 1;
tlen += len; tlen += len;
} while ((len = qword_get(&mesg, vers, size)) > 0); } while ((len = qword_get(&mesg, vers, size)) > 0);
...@@ -833,6 +857,13 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -833,6 +857,13 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
num); num);
sep = " "; sep = " ";
} }
if (nfsd_vers(4, NFSD_AVAIL))
for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION; minor++)
len += sprintf(buf+len, " %c4.%u",
(nfsd_vers(4, NFSD_TEST) &&
nfsd_minorversion(minor, NFSD_TEST)) ?
'+' : '-',
minor);
len += sprintf(buf+len, "\n"); len += sprintf(buf+len, "\n");
return len; return len;
} }
...@@ -1248,6 +1279,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) ...@@ -1248,6 +1279,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
[NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
......
...@@ -180,6 +180,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp, ...@@ -180,6 +180,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
{ {
__be32 nfserr; __be32 nfserr;
int stable = 1; int stable = 1;
unsigned long cnt = argp->len;
dprintk("nfsd: WRITE %s %d bytes at %d\n", dprintk("nfsd: WRITE %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
...@@ -188,7 +189,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp, ...@@ -188,7 +189,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL,
argp->offset, argp->offset,
rqstp->rq_vec, argp->vlen, rqstp->rq_vec, argp->vlen,
argp->len, &cnt,
&stable); &stable);
return nfsd_return_attrs(nfserr, resp); return nfsd_return_attrs(nfserr, resp);
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/fs_struct.h> #include <linux/fs_struct.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/swap.h>
#include <linux/sunrpc/types.h> #include <linux/sunrpc/types.h>
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
...@@ -40,9 +41,6 @@ ...@@ -40,9 +41,6 @@
extern struct svc_program nfsd_program; extern struct svc_program nfsd_program;
static int nfsd(void *vrqstp); static int nfsd(void *vrqstp);
struct timeval nfssvc_boot; struct timeval nfssvc_boot;
static atomic_t nfsd_busy;
static unsigned long nfsd_last_call;
static DEFINE_SPINLOCK(nfsd_call_lock);
/* /*
* nfsd_mutex protects nfsd_serv -- both the pointer itself and the members * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members
...@@ -123,6 +121,8 @@ struct svc_program nfsd_program = { ...@@ -123,6 +121,8 @@ struct svc_program nfsd_program = {
}; };
u32 nfsd_supported_minorversion;
int nfsd_vers(int vers, enum vers_op change) int nfsd_vers(int vers, enum vers_op change)
{ {
if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS)
...@@ -149,6 +149,28 @@ int nfsd_vers(int vers, enum vers_op change) ...@@ -149,6 +149,28 @@ int nfsd_vers(int vers, enum vers_op change)
} }
return 0; 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_minorversion = minorversion;
break;
case NFSD_CLEAR:
if (minorversion == 0)
return -1;
nfsd_supported_minorversion = minorversion - 1;
break;
case NFSD_TEST:
return minorversion <= nfsd_supported_minorversion;
case NFSD_AVAIL:
return minorversion <= NFSD_SUPPORTED_MINOR_VERSION;
}
return 0;
}
/* /*
* Maximum number of nfsd processes * Maximum number of nfsd processes
*/ */
...@@ -200,6 +222,28 @@ void nfsd_reset_versions(void) ...@@ -200,6 +222,28 @@ void nfsd_reset_versions(void)
} }
} }
/*
* 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)
{
/* The percent of nr_free_buffer_pages used by the V4.1 server DRC */
#define NFSD_DRC_SIZE_SHIFT 7
nfsd_serv->sv_drc_max_pages = nr_free_buffer_pages()
>> NFSD_DRC_SIZE_SHIFT;
nfsd_serv->sv_drc_pages_used = 0;
dprintk("%s svc_drc_max_pages %u\n", __func__,
nfsd_serv->sv_drc_max_pages);
}
int nfsd_create_serv(void) int nfsd_create_serv(void)
{ {
...@@ -227,11 +271,12 @@ int nfsd_create_serv(void) ...@@ -227,11 +271,12 @@ int nfsd_create_serv(void)
nfsd_max_blksize /= 2; nfsd_max_blksize /= 2;
} }
atomic_set(&nfsd_busy, 0);
nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
nfsd_last_thread, nfsd, THIS_MODULE); nfsd_last_thread, nfsd, THIS_MODULE);
if (nfsd_serv == NULL) if (nfsd_serv == NULL)
err = -ENOMEM; err = -ENOMEM;
else
set_max_drc();
do_gettimeofday(&nfssvc_boot); /* record boot time */ do_gettimeofday(&nfssvc_boot); /* record boot time */
return err; return err;
...@@ -375,26 +420,6 @@ nfsd_svc(unsigned short port, int nrservs) ...@@ -375,26 +420,6 @@ nfsd_svc(unsigned short port, int nrservs)
return error; return error;
} }
static inline void
update_thread_usage(int busy_threads)
{
unsigned long prev_call;
unsigned long diff;
int decile;
spin_lock(&nfsd_call_lock);
prev_call = nfsd_last_call;
nfsd_last_call = jiffies;
decile = busy_threads*10/nfsdstats.th_cnt;
if (decile>0 && decile <= 10) {
diff = nfsd_last_call - prev_call;
if ( (nfsdstats.th_usage[decile-1] += diff) >= NFSD_USAGE_WRAP)
nfsdstats.th_usage[decile-1] -= NFSD_USAGE_WRAP;
if (decile == 10)
nfsdstats.th_fullcnt++;
}
spin_unlock(&nfsd_call_lock);
}
/* /*
* This is the NFS server kernel thread * This is the NFS server kernel thread
...@@ -460,8 +485,6 @@ nfsd(void *vrqstp) ...@@ -460,8 +485,6 @@ nfsd(void *vrqstp)
continue; continue;
} }
update_thread_usage(atomic_read(&nfsd_busy));
atomic_inc(&nfsd_busy);
/* Lock the export hash tables for reading. */ /* Lock the export hash tables for reading. */
exp_readlock(); exp_readlock();
...@@ -470,8 +493,6 @@ nfsd(void *vrqstp) ...@@ -470,8 +493,6 @@ nfsd(void *vrqstp)
/* Unlock export hash tables */ /* Unlock export hash tables */
exp_readunlock(); exp_readunlock();
update_thread_usage(atomic_read(&nfsd_busy));
atomic_dec(&nfsd_busy);
} }
/* Clear signals before calling svc_exit_thread() */ /* Clear signals before calling svc_exit_thread() */
...@@ -539,6 +560,10 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) ...@@ -539,6 +560,10 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
+ rqstp->rq_res.head[0].iov_len; + rqstp->rq_res.head[0].iov_len;
rqstp->rq_res.head[0].iov_len += sizeof(__be32); rqstp->rq_res.head[0].iov_len += sizeof(__be32);
/* NFSv4.1 DRC requires statp */
if (rqstp->rq_vers == 4)
nfsd4_set_statp(rqstp, statp);
/* Now call the procedure handler, and encode NFS status. */ /* Now call the procedure handler, and encode NFS status. */
nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
nfserr = map_new_errors(rqstp->rq_vers, nfserr); nfserr = map_new_errors(rqstp->rq_vers, nfserr);
...@@ -570,3 +595,10 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) ...@@ -570,3 +595,10 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1);
return 1; return 1;
} }
int nfsd_pool_stats_open(struct inode *inode, struct file *file)
{
if (nfsd_serv == NULL)
return -ENODEV;
return svc_pool_stats_open(nfsd_serv, file);
}
...@@ -366,8 +366,9 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, ...@@ -366,8 +366,9 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
} }
/* Revoke setuid/setgid on chown */ /* Revoke setuid/setgid on chown */
if (((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) || if (!S_ISDIR(inode->i_mode) &&
((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)) { (((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) ||
((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid))) {
iap->ia_valid |= ATTR_KILL_PRIV; iap->ia_valid |= ATTR_KILL_PRIV;
if (iap->ia_valid & ATTR_MODE) { if (iap->ia_valid & ATTR_MODE) {
/* we're setting mode too, just clear the s*id bits */ /* we're setting mode too, just clear the s*id bits */
...@@ -960,7 +961,7 @@ static void kill_suid(struct dentry *dentry) ...@@ -960,7 +961,7 @@ static void kill_suid(struct dentry *dentry)
static __be32 static __be32
nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, loff_t offset, struct kvec *vec, int vlen,
unsigned long cnt, int *stablep) unsigned long *cnt, int *stablep)
{ {
struct svc_export *exp; struct svc_export *exp;
struct dentry *dentry; struct dentry *dentry;
...@@ -974,7 +975,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -974,7 +975,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
err = nfserr_perm; err = nfserr_perm;
if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
(!lock_may_write(file->f_path.dentry->d_inode, offset, cnt))) (!lock_may_write(file->f_path.dentry->d_inode, offset, *cnt)))
goto out; goto out;
#endif #endif
...@@ -1009,7 +1010,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -1009,7 +1010,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
set_fs(oldfs); set_fs(oldfs);
if (host_err >= 0) { if (host_err >= 0) {
nfsdstats.io_write += cnt; nfsdstats.io_write += host_err;
fsnotify_modify(file->f_path.dentry); fsnotify_modify(file->f_path.dentry);
} }
...@@ -1054,9 +1055,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -1054,9 +1055,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
} }
dprintk("nfsd: write complete host_err=%d\n", host_err); dprintk("nfsd: write complete host_err=%d\n", host_err);
if (host_err >= 0) if (host_err >= 0) {
err = 0; err = 0;
else *cnt = host_err;
} else
err = nfserrno(host_err); err = nfserrno(host_err);
out: out:
return err; return err;
...@@ -1098,7 +1100,7 @@ out: ...@@ -1098,7 +1100,7 @@ out:
*/ */
__be32 __be32
nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, unsigned long cnt, loff_t offset, struct kvec *vec, int vlen, unsigned long *cnt,
int *stablep) int *stablep)
{ {
__be32 err = 0; __be32 err = 0;
...@@ -1179,6 +1181,21 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp, ...@@ -1179,6 +1181,21 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp,
return 0; return 0;
} }
/* HPUX client sometimes creates a file in mode 000, and sets size to 0.
* setting size to 0 may fail for some specific file systems by the permission
* checking which requires WRITE permission but the mode is 000.
* we ignore the resizing(to 0) on the just new created file, since the size is
* 0 after file created.
*
* call this only after vfs_create() is called.
* */
static void
nfsd_check_ignore_resizing(struct iattr *iap)
{
if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
iap->ia_valid &= ~ATTR_SIZE;
}
/* /*
* Create a file (regular, directory, device, fifo); UNIX sockets * Create a file (regular, directory, device, fifo); UNIX sockets
* not yet implemented. * not yet implemented.
...@@ -1274,6 +1291,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1274,6 +1291,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
switch (type) { switch (type) {
case S_IFREG: case S_IFREG:
host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL); host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL);
if (!host_err)
nfsd_check_ignore_resizing(iap);
break; break;
case S_IFDIR: case S_IFDIR:
host_err = vfs_mkdir(dirp, dchild, iap->ia_mode); host_err = vfs_mkdir(dirp, dchild, iap->ia_mode);
...@@ -1427,6 +1446,8 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1427,6 +1446,8 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
/* setattr will sync the child (or not) */ /* setattr will sync the child (or not) */
} }
nfsd_check_ignore_resizing(iap);
if (createmode == NFS3_CREATE_EXCLUSIVE) { if (createmode == NFS3_CREATE_EXCLUSIVE) {
/* Cram the verifier into atime/mtime */ /* Cram the verifier into atime/mtime */
iap->ia_valid = ATTR_MTIME|ATTR_ATIME iap->ia_valid = ATTR_MTIME|ATTR_ATIME
......
...@@ -25,13 +25,13 @@ struct svc_rqst; ...@@ -25,13 +25,13 @@ struct svc_rqst;
#define NLM_MAXCOOKIELEN 32 #define NLM_MAXCOOKIELEN 32
#define NLM_MAXSTRLEN 1024 #define NLM_MAXSTRLEN 1024
#define nlm_granted __constant_htonl(NLM_LCK_GRANTED) #define nlm_granted cpu_to_be32(NLM_LCK_GRANTED)
#define nlm_lck_denied __constant_htonl(NLM_LCK_DENIED) #define nlm_lck_denied cpu_to_be32(NLM_LCK_DENIED)
#define nlm_lck_denied_nolocks __constant_htonl(NLM_LCK_DENIED_NOLOCKS) #define nlm_lck_denied_nolocks cpu_to_be32(NLM_LCK_DENIED_NOLOCKS)
#define nlm_lck_blocked __constant_htonl(NLM_LCK_BLOCKED) #define nlm_lck_blocked cpu_to_be32(NLM_LCK_BLOCKED)
#define nlm_lck_denied_grace_period __constant_htonl(NLM_LCK_DENIED_GRACE_PERIOD) #define nlm_lck_denied_grace_period cpu_to_be32(NLM_LCK_DENIED_GRACE_PERIOD)
#define nlm_drop_reply __constant_htonl(30000) #define nlm_drop_reply cpu_to_be32(30000)
/* Lock info passed via NLM */ /* Lock info passed via NLM */
struct nlm_lock { struct nlm_lock {
......
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
#include <linux/lockd/xdr.h> #include <linux/lockd/xdr.h>
/* error codes new to NLMv4 */ /* error codes new to NLMv4 */
#define nlm4_deadlock __constant_htonl(NLM_DEADLCK) #define nlm4_deadlock cpu_to_be32(NLM_DEADLCK)
#define nlm4_rofs __constant_htonl(NLM_ROFS) #define nlm4_rofs cpu_to_be32(NLM_ROFS)
#define nlm4_stale_fh __constant_htonl(NLM_STALE_FH) #define nlm4_stale_fh cpu_to_be32(NLM_STALE_FH)
#define nlm4_fbig __constant_htonl(NLM_FBIG) #define nlm4_fbig cpu_to_be32(NLM_FBIG)
#define nlm4_failed __constant_htonl(NLM_FAILED) #define nlm4_failed cpu_to_be32(NLM_FAILED)
......
...@@ -109,7 +109,6 @@ ...@@ -109,7 +109,6 @@
NFSERR_FILE_OPEN = 10046, /* v4 */ NFSERR_FILE_OPEN = 10046, /* v4 */
NFSERR_ADMIN_REVOKED = 10047, /* v4 */ NFSERR_ADMIN_REVOKED = 10047, /* v4 */
NFSERR_CB_PATH_DOWN = 10048, /* v4 */ NFSERR_CB_PATH_DOWN = 10048, /* v4 */
NFSERR_REPLAY_ME = 10049 /* v4 */
}; };
/* NFSv2 file types - beware, these are not the same in NFSv3 */ /* NFSv2 file types - beware, these are not the same in NFSv3 */
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define NFS4_FHSIZE 128 #define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX #define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX #define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_MAX_SESSIONID_LEN 16
#define NFS4_ACCESS_READ 0x0001 #define NFS4_ACCESS_READ 0x0001
#define NFS4_ACCESS_LOOKUP 0x0002 #define NFS4_ACCESS_LOOKUP 0x0002
...@@ -38,6 +39,7 @@ ...@@ -38,6 +39,7 @@
#define NFS4_OPEN_RESULT_CONFIRM 0x0002 #define NFS4_OPEN_RESULT_CONFIRM 0x0002
#define NFS4_OPEN_RESULT_LOCKTYPE_POSIX 0x0004 #define NFS4_OPEN_RESULT_LOCKTYPE_POSIX 0x0004
#define NFS4_SHARE_ACCESS_MASK 0x000F
#define NFS4_SHARE_ACCESS_READ 0x0001 #define NFS4_SHARE_ACCESS_READ 0x0001
#define NFS4_SHARE_ACCESS_WRITE 0x0002 #define NFS4_SHARE_ACCESS_WRITE 0x0002
#define NFS4_SHARE_ACCESS_BOTH 0x0003 #define NFS4_SHARE_ACCESS_BOTH 0x0003
...@@ -45,6 +47,19 @@ ...@@ -45,6 +47,19 @@
#define NFS4_SHARE_DENY_WRITE 0x0002 #define NFS4_SHARE_DENY_WRITE 0x0002
#define NFS4_SHARE_DENY_BOTH 0x0003 #define NFS4_SHARE_DENY_BOTH 0x0003
/* nfs41 */
#define NFS4_SHARE_WANT_MASK 0xFF00
#define NFS4_SHARE_WANT_NO_PREFERENCE 0x0000
#define NFS4_SHARE_WANT_READ_DELEG 0x0100
#define NFS4_SHARE_WANT_WRITE_DELEG 0x0200
#define NFS4_SHARE_WANT_ANY_DELEG 0x0300
#define NFS4_SHARE_WANT_NO_DELEG 0x0400
#define NFS4_SHARE_WANT_CANCEL 0x0500
#define NFS4_SHARE_WHEN_MASK 0xF0000
#define NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL 0x10000
#define NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED 0x20000
#define NFS4_SET_TO_SERVER_TIME 0 #define NFS4_SET_TO_SERVER_TIME 0
#define NFS4_SET_TO_CLIENT_TIME 1 #define NFS4_SET_TO_CLIENT_TIME 1
...@@ -88,6 +103,31 @@ ...@@ -88,6 +103,31 @@
#define NFS4_ACE_GENERIC_EXECUTE 0x001200A0 #define NFS4_ACE_GENERIC_EXECUTE 0x001200A0
#define NFS4_ACE_MASK_ALL 0x001F01FF #define NFS4_ACE_MASK_ALL 0x001F01FF
#define EXCHGID4_FLAG_SUPP_MOVED_REFER 0x00000001
#define EXCHGID4_FLAG_SUPP_MOVED_MIGR 0x00000002
#define EXCHGID4_FLAG_USE_NON_PNFS 0x00010000
#define EXCHGID4_FLAG_USE_PNFS_MDS 0x00020000
#define EXCHGID4_FLAG_USE_PNFS_DS 0x00040000
#define EXCHGID4_FLAG_UPD_CONFIRMED_REC_A 0x40000000
#define EXCHGID4_FLAG_CONFIRMED_R 0x80000000
/*
* Since the validity of these bits depends on whether
* they're set in the argument or response, have separate
* invalid flag masks for arg (_A) and resp (_R).
*/
#define EXCHGID4_FLAG_MASK_A 0x40070003
#define EXCHGID4_FLAG_MASK_R 0x80070003
#define SEQ4_STATUS_CB_PATH_DOWN 0x00000001
#define SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING 0x00000002
#define SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED 0x00000004
#define SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED 0x00000008
#define SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED 0x00000010
#define SEQ4_STATUS_ADMIN_STATE_REVOKED 0x00000020
#define SEQ4_STATUS_RECALLABLE_STATE_REVOKED 0x00000040
#define SEQ4_STATUS_LEASE_MOVED 0x00000080
#define SEQ4_STATUS_RESTART_RECLAIM_NEEDED 0x00000100
#define NFS4_MAX_UINT64 (~(u64)0) #define NFS4_MAX_UINT64 (~(u64)0)
enum nfs4_acl_whotype { enum nfs4_acl_whotype {
...@@ -154,6 +194,28 @@ enum nfs_opnum4 { ...@@ -154,6 +194,28 @@ enum nfs_opnum4 {
OP_VERIFY = 37, OP_VERIFY = 37,
OP_WRITE = 38, OP_WRITE = 38,
OP_RELEASE_LOCKOWNER = 39, OP_RELEASE_LOCKOWNER = 39,
/* nfs41 */
OP_BACKCHANNEL_CTL = 40,
OP_BIND_CONN_TO_SESSION = 41,
OP_EXCHANGE_ID = 42,
OP_CREATE_SESSION = 43,
OP_DESTROY_SESSION = 44,
OP_FREE_STATEID = 45,
OP_GET_DIR_DELEGATION = 46,
OP_GETDEVICEINFO = 47,
OP_GETDEVICELIST = 48,
OP_LAYOUTCOMMIT = 49,
OP_LAYOUTGET = 50,
OP_LAYOUTRETURN = 51,
OP_SECINFO_NO_NAME = 52,
OP_SEQUENCE = 53,
OP_SET_SSV = 54,
OP_TEST_STATEID = 55,
OP_WANT_DELEGATION = 56,
OP_DESTROY_CLIENTID = 57,
OP_RECLAIM_COMPLETE = 58,
OP_ILLEGAL = 10044, OP_ILLEGAL = 10044,
}; };
...@@ -230,7 +292,48 @@ enum nfsstat4 { ...@@ -230,7 +292,48 @@ enum nfsstat4 {
NFS4ERR_DEADLOCK = 10045, NFS4ERR_DEADLOCK = 10045,
NFS4ERR_FILE_OPEN = 10046, NFS4ERR_FILE_OPEN = 10046,
NFS4ERR_ADMIN_REVOKED = 10047, NFS4ERR_ADMIN_REVOKED = 10047,
NFS4ERR_CB_PATH_DOWN = 10048 NFS4ERR_CB_PATH_DOWN = 10048,
/* nfs41 */
NFS4ERR_BADIOMODE = 10049,
NFS4ERR_BADLAYOUT = 10050,
NFS4ERR_BAD_SESSION_DIGEST = 10051,
NFS4ERR_BADSESSION = 10052,
NFS4ERR_BADSLOT = 10053,
NFS4ERR_COMPLETE_ALREADY = 10054,
NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
NFS4ERR_DELEG_ALREADY_WANTED = 10056,
NFS4ERR_BACK_CHAN_BUSY = 10057, /* backchan reqs outstanding */
NFS4ERR_LAYOUTTRYLATER = 10058,
NFS4ERR_LAYOUTUNAVAILABLE = 10059,
NFS4ERR_NOMATCHING_LAYOUT = 10060,
NFS4ERR_RECALLCONFLICT = 10061,
NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
NFS4ERR_SEQ_MISORDERED = 10063, /* unexpected seq.id in req */
NFS4ERR_SEQUENCE_POS = 10064, /* [CB_]SEQ. op not 1st op */
NFS4ERR_REQ_TOO_BIG = 10065, /* request too big */
NFS4ERR_REP_TOO_BIG = 10066, /* reply too big */
NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067, /* rep. not all cached */
NFS4ERR_RETRY_UNCACHED_REP = 10068, /* retry & rep. uncached */
NFS4ERR_UNSAFE_COMPOUND = 10069, /* retry/recovery too hard */
NFS4ERR_TOO_MANY_OPS = 10070, /* too many ops in [CB_]COMP */
NFS4ERR_OP_NOT_IN_SESSION = 10071, /* op needs [CB_]SEQ. op */
NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
/* Error 10073 is unused. */
NFS4ERR_CLIENTID_BUSY = 10074, /* clientid has state */
NFS4ERR_PNFS_IO_HOLE = 10075, /* IO to _SPARSE file hole */
NFS4ERR_SEQ_FALSE_RETRY = 10076, /* retry not origional */
NFS4ERR_BAD_HIGH_SLOT = 10077, /* sequence arg bad */
NFS4ERR_DEADSESSION = 10078, /* persistent session dead */
NFS4ERR_ENCR_ALG_UNSUPP = 10079, /* SSV alg mismatch */
NFS4ERR_PNFS_NO_LAYOUT = 10080, /* direct I/O with no layout */
NFS4ERR_NOT_ONLY_OP = 10081, /* bad compound */
NFS4ERR_WRONG_CRED = 10082, /* permissions:state change */
NFS4ERR_WRONG_TYPE = 10083, /* current operation mismatch */
NFS4ERR_DIRDELEG_UNAVAIL = 10084, /* no directory delegation */
NFS4ERR_REJECT_DELEG = 10085, /* on callback */
NFS4ERR_RETURNCONFLICT = 10086, /* outstanding layoutreturn */
NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */
}; };
/* /*
...@@ -265,7 +368,13 @@ enum opentype4 { ...@@ -265,7 +368,13 @@ enum opentype4 {
enum createmode4 { enum createmode4 {
NFS4_CREATE_UNCHECKED = 0, NFS4_CREATE_UNCHECKED = 0,
NFS4_CREATE_GUARDED = 1, NFS4_CREATE_GUARDED = 1,
NFS4_CREATE_EXCLUSIVE = 2 NFS4_CREATE_EXCLUSIVE = 2,
/*
* New to NFSv4.1. If session is persistent,
* GUARDED4 MUST be used. Otherwise, use
* EXCLUSIVE4_1 instead of EXCLUSIVE4.
*/
NFS4_CREATE_EXCLUSIVE4_1 = 3
}; };
enum limit_by4 { enum limit_by4 {
...@@ -301,6 +410,8 @@ enum lock_type4 { ...@@ -301,6 +410,8 @@ enum lock_type4 {
#define FATTR4_WORD0_UNIQUE_HANDLES (1UL << 9) #define FATTR4_WORD0_UNIQUE_HANDLES (1UL << 9)
#define FATTR4_WORD0_LEASE_TIME (1UL << 10) #define FATTR4_WORD0_LEASE_TIME (1UL << 10)
#define FATTR4_WORD0_RDATTR_ERROR (1UL << 11) #define FATTR4_WORD0_RDATTR_ERROR (1UL << 11)
/* Mandatory in NFSv4.1 */
#define FATTR4_WORD2_SUPPATTR_EXCLCREAT (1UL << 11)
/* Recommended Attributes */ /* Recommended Attributes */
#define FATTR4_WORD0_ACL (1UL << 12) #define FATTR4_WORD0_ACL (1UL << 12)
...@@ -391,6 +502,29 @@ enum { ...@@ -391,6 +502,29 @@ enum {
NFSPROC4_CLNT_GETACL, NFSPROC4_CLNT_GETACL,
NFSPROC4_CLNT_SETACL, NFSPROC4_CLNT_SETACL,
NFSPROC4_CLNT_FS_LOCATIONS, NFSPROC4_CLNT_FS_LOCATIONS,
/* nfs41 */
NFSPROC4_CLNT_EXCHANGE_ID,
NFSPROC4_CLNT_CREATE_SESSION,
NFSPROC4_CLNT_DESTROY_SESSION,
NFSPROC4_CLNT_SEQUENCE,
NFSPROC4_CLNT_GET_LEASE_TIME,
};
/* nfs41 types */
struct nfs4_sessionid {
unsigned char data[NFS4_MAX_SESSIONID_LEN];
};
/* Create Session Flags */
#define SESSION4_PERSIST 0x001
#define SESSION4_BACK_CHAN 0x002
#define SESSION4_RDMA 0x004
enum state_protect_how4 {
SP4_NONE = 0,
SP4_MACH_CRED = 1,
SP4_SSV = 2
}; };
#endif #endif
......
...@@ -76,4 +76,12 @@ void nfsd_reply_cache_shutdown(void); ...@@ -76,4 +76,12 @@ void nfsd_reply_cache_shutdown(void);
int nfsd_cache_lookup(struct svc_rqst *, int); int nfsd_cache_lookup(struct svc_rqst *, int);
void nfsd_cache_update(struct svc_rqst *, int, __be32 *); void nfsd_cache_update(struct svc_rqst *, int, __be32 *);
#ifdef CONFIG_NFSD_V4
void nfsd4_set_statp(struct svc_rqst *rqstp, __be32 *statp);
#else /* CONFIG_NFSD_V4 */
static inline void nfsd4_set_statp(struct svc_rqst *rqstp, __be32 *statp)
{
}
#endif /* CONFIG_NFSD_V4 */
#endif /* NFSCACHE_H */ #endif /* NFSCACHE_H */
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
/* /*
* nfsd version * nfsd version
*/ */
#define NFSD_SUPPORTED_MINOR_VERSION 0 #define NFSD_SUPPORTED_MINOR_VERSION 1
/* /*
* Flags for nfsd_permission * Flags for nfsd_permission
...@@ -53,6 +53,7 @@ typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int); ...@@ -53,6 +53,7 @@ typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int);
extern struct svc_program nfsd_program; extern struct svc_program nfsd_program;
extern struct svc_version nfsd_version2, nfsd_version3, extern struct svc_version nfsd_version2, nfsd_version3,
nfsd_version4; nfsd_version4;
extern u32 nfsd_supported_minorversion;
extern struct mutex nfsd_mutex; extern struct mutex nfsd_mutex;
extern struct svc_serv *nfsd_serv; extern struct svc_serv *nfsd_serv;
...@@ -105,7 +106,7 @@ void nfsd_close(struct file *); ...@@ -105,7 +106,7 @@ void nfsd_close(struct file *);
__be32 nfsd_read(struct svc_rqst *, struct svc_fh *, struct file *, __be32 nfsd_read(struct svc_rqst *, struct svc_fh *, struct file *,
loff_t, struct kvec *, int, unsigned long *); loff_t, struct kvec *, int, unsigned long *);
__be32 nfsd_write(struct svc_rqst *, struct svc_fh *,struct file *, __be32 nfsd_write(struct svc_rqst *, struct svc_fh *,struct file *,
loff_t, struct kvec *,int, unsigned long, int *); loff_t, struct kvec *,int, unsigned long *, int *);
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *, __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
char *, int *); char *, int *);
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *, __be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
...@@ -149,6 +150,7 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *); ...@@ -149,6 +150,7 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *);
enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL }; enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
int nfsd_vers(int vers, enum vers_op change); int nfsd_vers(int vers, enum vers_op change);
int nfsd_minorversion(u32 minorversion, enum vers_op change);
void nfsd_reset_versions(void); void nfsd_reset_versions(void);
int nfsd_create_serv(void); int nfsd_create_serv(void);
...@@ -186,78 +188,119 @@ void nfsd_lockd_shutdown(void); ...@@ -186,78 +188,119 @@ void nfsd_lockd_shutdown(void);
/* /*
* These macros provide pre-xdr'ed values for faster operation. * These macros provide pre-xdr'ed values for faster operation.
*/ */
#define nfs_ok __constant_htonl(NFS_OK) #define nfs_ok cpu_to_be32(NFS_OK)
#define nfserr_perm __constant_htonl(NFSERR_PERM) #define nfserr_perm cpu_to_be32(NFSERR_PERM)
#define nfserr_noent __constant_htonl(NFSERR_NOENT) #define nfserr_noent cpu_to_be32(NFSERR_NOENT)
#define nfserr_io __constant_htonl(NFSERR_IO) #define nfserr_io cpu_to_be32(NFSERR_IO)
#define nfserr_nxio __constant_htonl(NFSERR_NXIO) #define nfserr_nxio cpu_to_be32(NFSERR_NXIO)
#define nfserr_eagain __constant_htonl(NFSERR_EAGAIN) #define nfserr_eagain cpu_to_be32(NFSERR_EAGAIN)
#define nfserr_acces __constant_htonl(NFSERR_ACCES) #define nfserr_acces cpu_to_be32(NFSERR_ACCES)
#define nfserr_exist __constant_htonl(NFSERR_EXIST) #define nfserr_exist cpu_to_be32(NFSERR_EXIST)
#define nfserr_xdev __constant_htonl(NFSERR_XDEV) #define nfserr_xdev cpu_to_be32(NFSERR_XDEV)
#define nfserr_nodev __constant_htonl(NFSERR_NODEV) #define nfserr_nodev cpu_to_be32(NFSERR_NODEV)
#define nfserr_notdir __constant_htonl(NFSERR_NOTDIR) #define nfserr_notdir cpu_to_be32(NFSERR_NOTDIR)
#define nfserr_isdir __constant_htonl(NFSERR_ISDIR) #define nfserr_isdir cpu_to_be32(NFSERR_ISDIR)
#define nfserr_inval __constant_htonl(NFSERR_INVAL) #define nfserr_inval cpu_to_be32(NFSERR_INVAL)
#define nfserr_fbig __constant_htonl(NFSERR_FBIG) #define nfserr_fbig cpu_to_be32(NFSERR_FBIG)
#define nfserr_nospc __constant_htonl(NFSERR_NOSPC) #define nfserr_nospc cpu_to_be32(NFSERR_NOSPC)
#define nfserr_rofs __constant_htonl(NFSERR_ROFS) #define nfserr_rofs cpu_to_be32(NFSERR_ROFS)
#define nfserr_mlink __constant_htonl(NFSERR_MLINK) #define nfserr_mlink cpu_to_be32(NFSERR_MLINK)
#define nfserr_opnotsupp __constant_htonl(NFSERR_OPNOTSUPP) #define nfserr_opnotsupp cpu_to_be32(NFSERR_OPNOTSUPP)
#define nfserr_nametoolong __constant_htonl(NFSERR_NAMETOOLONG) #define nfserr_nametoolong cpu_to_be32(NFSERR_NAMETOOLONG)
#define nfserr_notempty __constant_htonl(NFSERR_NOTEMPTY) #define nfserr_notempty cpu_to_be32(NFSERR_NOTEMPTY)
#define nfserr_dquot __constant_htonl(NFSERR_DQUOT) #define nfserr_dquot cpu_to_be32(NFSERR_DQUOT)
#define nfserr_stale __constant_htonl(NFSERR_STALE) #define nfserr_stale cpu_to_be32(NFSERR_STALE)
#define nfserr_remote __constant_htonl(NFSERR_REMOTE) #define nfserr_remote cpu_to_be32(NFSERR_REMOTE)
#define nfserr_wflush __constant_htonl(NFSERR_WFLUSH) #define nfserr_wflush cpu_to_be32(NFSERR_WFLUSH)
#define nfserr_badhandle __constant_htonl(NFSERR_BADHANDLE) #define nfserr_badhandle cpu_to_be32(NFSERR_BADHANDLE)
#define nfserr_notsync __constant_htonl(NFSERR_NOT_SYNC) #define nfserr_notsync cpu_to_be32(NFSERR_NOT_SYNC)
#define nfserr_badcookie __constant_htonl(NFSERR_BAD_COOKIE) #define nfserr_badcookie cpu_to_be32(NFSERR_BAD_COOKIE)
#define nfserr_notsupp __constant_htonl(NFSERR_NOTSUPP) #define nfserr_notsupp cpu_to_be32(NFSERR_NOTSUPP)
#define nfserr_toosmall __constant_htonl(NFSERR_TOOSMALL) #define nfserr_toosmall cpu_to_be32(NFSERR_TOOSMALL)
#define nfserr_serverfault __constant_htonl(NFSERR_SERVERFAULT) #define nfserr_serverfault cpu_to_be32(NFSERR_SERVERFAULT)
#define nfserr_badtype __constant_htonl(NFSERR_BADTYPE) #define nfserr_badtype cpu_to_be32(NFSERR_BADTYPE)
#define nfserr_jukebox __constant_htonl(NFSERR_JUKEBOX) #define nfserr_jukebox cpu_to_be32(NFSERR_JUKEBOX)
#define nfserr_denied __constant_htonl(NFSERR_DENIED) #define nfserr_denied cpu_to_be32(NFSERR_DENIED)
#define nfserr_deadlock __constant_htonl(NFSERR_DEADLOCK) #define nfserr_deadlock cpu_to_be32(NFSERR_DEADLOCK)
#define nfserr_expired __constant_htonl(NFSERR_EXPIRED) #define nfserr_expired cpu_to_be32(NFSERR_EXPIRED)
#define nfserr_bad_cookie __constant_htonl(NFSERR_BAD_COOKIE) #define nfserr_bad_cookie cpu_to_be32(NFSERR_BAD_COOKIE)
#define nfserr_same __constant_htonl(NFSERR_SAME) #define nfserr_same cpu_to_be32(NFSERR_SAME)
#define nfserr_clid_inuse __constant_htonl(NFSERR_CLID_INUSE) #define nfserr_clid_inuse cpu_to_be32(NFSERR_CLID_INUSE)
#define nfserr_stale_clientid __constant_htonl(NFSERR_STALE_CLIENTID) #define nfserr_stale_clientid cpu_to_be32(NFSERR_STALE_CLIENTID)
#define nfserr_resource __constant_htonl(NFSERR_RESOURCE) #define nfserr_resource cpu_to_be32(NFSERR_RESOURCE)
#define nfserr_moved __constant_htonl(NFSERR_MOVED) #define nfserr_moved cpu_to_be32(NFSERR_MOVED)
#define nfserr_nofilehandle __constant_htonl(NFSERR_NOFILEHANDLE) #define nfserr_nofilehandle cpu_to_be32(NFSERR_NOFILEHANDLE)
#define nfserr_minor_vers_mismatch __constant_htonl(NFSERR_MINOR_VERS_MISMATCH) #define nfserr_minor_vers_mismatch cpu_to_be32(NFSERR_MINOR_VERS_MISMATCH)
#define nfserr_share_denied __constant_htonl(NFSERR_SHARE_DENIED) #define nfserr_share_denied cpu_to_be32(NFSERR_SHARE_DENIED)
#define nfserr_stale_stateid __constant_htonl(NFSERR_STALE_STATEID) #define nfserr_stale_stateid cpu_to_be32(NFSERR_STALE_STATEID)
#define nfserr_old_stateid __constant_htonl(NFSERR_OLD_STATEID) #define nfserr_old_stateid cpu_to_be32(NFSERR_OLD_STATEID)
#define nfserr_bad_stateid __constant_htonl(NFSERR_BAD_STATEID) #define nfserr_bad_stateid cpu_to_be32(NFSERR_BAD_STATEID)
#define nfserr_bad_seqid __constant_htonl(NFSERR_BAD_SEQID) #define nfserr_bad_seqid cpu_to_be32(NFSERR_BAD_SEQID)
#define nfserr_symlink __constant_htonl(NFSERR_SYMLINK) #define nfserr_symlink cpu_to_be32(NFSERR_SYMLINK)
#define nfserr_not_same __constant_htonl(NFSERR_NOT_SAME) #define nfserr_not_same cpu_to_be32(NFSERR_NOT_SAME)
#define nfserr_restorefh __constant_htonl(NFSERR_RESTOREFH) #define nfserr_restorefh cpu_to_be32(NFSERR_RESTOREFH)
#define nfserr_attrnotsupp __constant_htonl(NFSERR_ATTRNOTSUPP) #define nfserr_attrnotsupp cpu_to_be32(NFSERR_ATTRNOTSUPP)
#define nfserr_bad_xdr __constant_htonl(NFSERR_BAD_XDR) #define nfserr_bad_xdr cpu_to_be32(NFSERR_BAD_XDR)
#define nfserr_openmode __constant_htonl(NFSERR_OPENMODE) #define nfserr_openmode cpu_to_be32(NFSERR_OPENMODE)
#define nfserr_locks_held __constant_htonl(NFSERR_LOCKS_HELD) #define nfserr_locks_held cpu_to_be32(NFSERR_LOCKS_HELD)
#define nfserr_op_illegal __constant_htonl(NFSERR_OP_ILLEGAL) #define nfserr_op_illegal cpu_to_be32(NFSERR_OP_ILLEGAL)
#define nfserr_grace __constant_htonl(NFSERR_GRACE) #define nfserr_grace cpu_to_be32(NFSERR_GRACE)
#define nfserr_no_grace __constant_htonl(NFSERR_NO_GRACE) #define nfserr_no_grace cpu_to_be32(NFSERR_NO_GRACE)
#define nfserr_reclaim_bad __constant_htonl(NFSERR_RECLAIM_BAD) #define nfserr_reclaim_bad cpu_to_be32(NFSERR_RECLAIM_BAD)
#define nfserr_badname __constant_htonl(NFSERR_BADNAME) #define nfserr_badname cpu_to_be32(NFSERR_BADNAME)
#define nfserr_cb_path_down __constant_htonl(NFSERR_CB_PATH_DOWN) #define nfserr_cb_path_down cpu_to_be32(NFSERR_CB_PATH_DOWN)
#define nfserr_locked __constant_htonl(NFSERR_LOCKED) #define nfserr_locked cpu_to_be32(NFSERR_LOCKED)
#define nfserr_wrongsec __constant_htonl(NFSERR_WRONGSEC) #define nfserr_wrongsec cpu_to_be32(NFSERR_WRONGSEC)
#define nfserr_replay_me __constant_htonl(NFSERR_REPLAY_ME) #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)
/* error codes for internal use */ /* error codes for internal use */
/* if a request fails due to kmalloc failure, it gets dropped. /* if a request fails due to kmalloc failure, it gets dropped.
* Client should resend eventually * Client should resend eventually
*/ */
#define nfserr_dropit __constant_htonl(30000) #define nfserr_dropit cpu_to_be32(30000)
/* end-of-file indicator in readdir */ /* end-of-file indicator in readdir */
#define nfserr_eof __constant_htonl(30001) #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 '..' */ /* Check for dir entries '.' and '..' */
#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.')) #define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
...@@ -300,7 +343,7 @@ extern struct timeval nfssvc_boot; ...@@ -300,7 +343,7 @@ extern struct timeval nfssvc_boot;
* TIME_BACKUP (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) * TIME_CREATE (unlikely to be supported any time soon)
*/ */
#define NFSD_SUPPORTED_ATTRS_WORD0 \ #define NFSD4_SUPPORTED_ATTRS_WORD0 \
(FATTR4_WORD0_SUPPORTED_ATTRS | FATTR4_WORD0_TYPE | FATTR4_WORD0_FH_EXPIRE_TYPE \ (FATTR4_WORD0_SUPPORTED_ATTRS | FATTR4_WORD0_TYPE | FATTR4_WORD0_FH_EXPIRE_TYPE \
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE | FATTR4_WORD0_LINK_SUPPORT \ | FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE | FATTR4_WORD0_LINK_SUPPORT \
| FATTR4_WORD0_SYMLINK_SUPPORT | FATTR4_WORD0_NAMED_ATTR | FATTR4_WORD0_FSID \ | FATTR4_WORD0_SYMLINK_SUPPORT | FATTR4_WORD0_NAMED_ATTR | FATTR4_WORD0_FSID \
...@@ -312,7 +355,7 @@ extern struct timeval nfssvc_boot; ...@@ -312,7 +355,7 @@ extern struct timeval nfssvc_boot;
| FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \ | FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \
| FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL) | FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL)
#define NFSD_SUPPORTED_ATTRS_WORD1 \ #define NFSD4_SUPPORTED_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_NO_TRUNC | FATTR4_WORD1_NUMLINKS \ (FATTR4_WORD1_MODE | FATTR4_WORD1_NO_TRUNC | FATTR4_WORD1_NUMLINKS \
| FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \ | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \
| FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \ | FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \
...@@ -320,6 +363,35 @@ extern struct timeval nfssvc_boot; ...@@ -320,6 +363,35 @@ extern struct timeval nfssvc_boot;
| FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA \ | FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA \
| FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID) | FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
#define NFSD4_SUPPORTED_ATTRS_WORD2 0
#define NFSD4_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)
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)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD2
: NFSD4_SUPPORTED_ATTRS_WORD2;
}
/* These will return ERR_INVAL if specified in GETATTR or READDIR. */ /* These will return ERR_INVAL if specified in GETATTR or READDIR. */
#define NFSD_WRITEONLY_ATTRS_WORD1 \ #define NFSD_WRITEONLY_ATTRS_WORD1 \
(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) (FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
...@@ -330,6 +402,19 @@ extern struct timeval nfssvc_boot; ...@@ -330,6 +402,19 @@ extern struct timeval nfssvc_boot;
#define NFSD_WRITEABLE_ATTRS_WORD1 \ #define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
#define NFSD_WRITEABLE_ATTRS_WORD2 0
#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
#endif /* CONFIG_NFSD_V4 */ #endif /* CONFIG_NFSD_V4 */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment