diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-22 09:44:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-22 09:44:27 -0700 |
commit | 24717cfbbbbfa415d1e3dca0f21c417e5faf8208 (patch) | |
tree | 8fafaa269b3e5fde06ebc9bedd8c7a9ffd20b816 | |
parent | 9b06f57b9edb2d67471e626b3ebd247826729a7f (diff) | |
parent | 0cfcd405e758ba1d277e58436fb32f06888c3e41 (diff) | |
download | linux-24717cfbbbbfa415d1e3dca0f21c417e5faf8208.tar.bz2 |
Merge tag 'nfsd-5.10' of git://linux-nfs.org/~bfields/linux
Pull nfsd updates from Bruce Fields:
"The one new feature this time, from Anna Schumaker, is READ_PLUS,
which has the same arguments as READ but allows the server to return
an array of data and hole extents.
Otherwise it's a lot of cleanup and bugfixes"
* tag 'nfsd-5.10' of git://linux-nfs.org/~bfields/linux: (43 commits)
NFSv4.2: Fix NFS4ERR_STALE error when doing inter server copy
SUNRPC: fix copying of multiple pages in gss_read_proxy_verf()
sunrpc: raise kernel RPC channel buffer size
svcrdma: fix bounce buffers for unaligned offsets and multiple pages
nfsd: remove unneeded break
net/sunrpc: Fix return value for sysctl sunrpc.transports
NFSD: Encode a full READ_PLUS reply
NFSD: Return both a hole and a data segment
NFSD: Add READ_PLUS hole segment encoding
NFSD: Add READ_PLUS data support
NFSD: Hoist status code encoding into XDR encoder functions
NFSD: Map nfserr_wrongsec outside of nfsd_dispatch
NFSD: Remove the RETURN_STATUS() macro
NFSD: Call NFSv2 encoders on error returns
NFSD: Fix .pc_release method for NFSv2
NFSD: Remove vestigial typedefs
NFSD: Refactor nfsd_dispatch() error paths
NFSD: Clean up nfsd_dispatch() variables
NFSD: Clean up stale comments in nfsd_dispatch()
NFSD: Clean up switch statement in nfsd_dispatch()
...
44 files changed, 1490 insertions, 1368 deletions
diff --git a/Documentation/admin-guide/nfs/fault_injection.rst b/Documentation/admin-guide/nfs/fault_injection.rst deleted file mode 100644 index eb029c0c15ce..000000000000 --- a/Documentation/admin-guide/nfs/fault_injection.rst +++ /dev/null @@ -1,70 +0,0 @@ -=================== -NFS Fault Injection -=================== - -Fault injection is a method for forcing errors that may not normally occur, or -may be difficult to reproduce. Forcing these errors in a controlled environment -can help the developer find and fix bugs before their code is shipped in a -production system. Injecting an error on the Linux NFS server will allow us to -observe how the client reacts and if it manages to recover its state correctly. - -NFSD_FAULT_INJECTION must be selected when configuring the kernel to use this -feature. - - -Using Fault Injection -===================== -On the client, mount the fault injection server through NFS v4.0+ and do some -work over NFS (open files, take locks, ...). - -On the server, mount the debugfs filesystem to <debug_dir> and ls -<debug_dir>/nfsd. This will show a list of files that will be used for -injecting faults on the NFS server. As root, write a number n to the file -corresponding to the action you want the server to take. The server will then -process the first n items it finds. So if you want to forget 5 locks, echo '5' -to <debug_dir>/nfsd/forget_locks. A value of 0 will tell the server to forget -all corresponding items. A log message will be created containing the number -of items forgotten (check dmesg). - -Go back to work on the client and check if the client recovered from the error -correctly. - - -Available Faults -================ -forget_clients: - The NFS server keeps a list of clients that have placed a mount call. If - this list is cleared, the server will have no knowledge of who the client - is, forcing the client to reauthenticate with the server. - -forget_openowners: - The NFS server keeps a list of what files are currently opened and who - they were opened by. Clearing this list will force the client to reopen - its files. - -forget_locks: - The NFS server keeps a list of what files are currently locked in the VFS. - Clearing this list will force the client to reclaim its locks (files are - unlocked through the VFS as they are cleared from this list). - -forget_delegations: - A delegation is used to assure the client that a file, or part of a file, - has not changed since the delegation was awarded. Clearing this list will - force the client to reacquire its delegation before accessing the file - again. - -recall_delegations: - Delegations can be recalled by the server when another client attempts to - access a file. This test will notify the client that its delegation has - been revoked, forcing the client to reacquire the delegation before using - the file again. - - -tools/nfs/inject_faults.sh script -================================= -This script has been created to ease the fault injection process. This script -will detect the mounted debugfs directory and write to the files located there -based on the arguments passed by the user. For example, running -`inject_faults.sh forget_locks 1` as root will instruct the server to forget -one lock. Running `inject_faults forget_locks` will instruct the server to -forgetall locks. diff --git a/Documentation/admin-guide/nfs/index.rst b/Documentation/admin-guide/nfs/index.rst index 6b5a3c90fac5..3601a708f333 100644 --- a/Documentation/admin-guide/nfs/index.rst +++ b/Documentation/admin-guide/nfs/index.rst @@ -12,4 +12,3 @@ NFS nfs-idmapper pnfs-block-server pnfs-scsi-server - fault_injection diff --git a/Documentation/filesystems/nfs/rpc-server-gss.rst b/Documentation/filesystems/nfs/rpc-server-gss.rst index abed4a2b1b82..ccaea9e7cea2 100644 --- a/Documentation/filesystems/nfs/rpc-server-gss.rst +++ b/Documentation/filesystems/nfs/rpc-server-gss.rst @@ -13,10 +13,9 @@ RPCGSS is specified in a few IETF documents: - RFC2203 v1: https://tools.ietf.org/rfc/rfc2203.txt - RFC5403 v2: https://tools.ietf.org/rfc/rfc5403.txt -and there is a 3rd version being proposed: +There is a third version that we don't currently implement: - - https://tools.ietf.org/id/draft-williams-rpcsecgssv3.txt - (At draft n. 02 at the time of writing) + - RFC7861 v3: https://tools.ietf.org/rfc/rfc7861.txt Background ========== diff --git a/MAINTAINERS b/MAINTAINERS index 60726c82292e..8b82a02e609c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9556,6 +9556,7 @@ F: include/linux/sunrpc/ F: include/uapi/linux/nfsd/ F: include/uapi/linux/sunrpc/ F: net/sunrpc/ +F: Documentation/filesystems/nfs/ KERNEL SELFTEST FRAMEWORK M: Shuah Khan <shuah@kernel.org> @@ -12337,6 +12338,7 @@ F: include/linux/sunrpc/ F: include/uapi/linux/nfs* F: include/uapi/linux/sunrpc/ F: net/sunrpc/ +F: Documentation/filesystems/nfs/ NILFS2 FILESYSTEM M: Ryusuke Konishi <konishi.ryusuke@gmail.com> diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index e4d3f783e06a..fa41dda39925 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -486,65 +486,215 @@ nlm4svc_proc_granted_res(struct svc_rqst *rqstp) return rpc_success; } +static __be32 +nlm4svc_proc_unused(struct svc_rqst *rqstp) +{ + return rpc_proc_unavail; +} + /* * NLM Server procedures. */ -#define nlm4svc_encode_norep nlm4svc_encode_void -#define nlm4svc_decode_norep nlm4svc_decode_void -#define nlm4svc_decode_testres nlm4svc_decode_void -#define nlm4svc_decode_lockres nlm4svc_decode_void -#define nlm4svc_decode_unlockres nlm4svc_decode_void -#define nlm4svc_decode_cancelres nlm4svc_decode_void -#define nlm4svc_decode_grantedres nlm4svc_decode_void - -#define nlm4svc_proc_none nlm4svc_proc_null -#define nlm4svc_proc_test_res nlm4svc_proc_null -#define nlm4svc_proc_lock_res nlm4svc_proc_null -#define nlm4svc_proc_cancel_res nlm4svc_proc_null -#define nlm4svc_proc_unlock_res nlm4svc_proc_null - struct nlm_void { int dummy; }; -#define PROC(name, xargt, xrest, argt, rest, respsize) \ - { .pc_func = nlm4svc_proc_##name, \ - .pc_decode = nlm4svc_decode_##xargt, \ - .pc_encode = nlm4svc_encode_##xrest, \ - .pc_release = NULL, \ - .pc_argsize = sizeof(struct nlm_##argt), \ - .pc_ressize = sizeof(struct nlm_##rest), \ - .pc_xdrressize = respsize, \ - } #define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */ #define No (1+1024/4) /* netobj */ #define St 1 /* status */ #define Rg 4 /* range (offset + length) */ -const struct svc_procedure nlmsvc_procedures4[] = { - PROC(null, void, void, void, void, 1), - PROC(test, testargs, testres, args, res, Ck+St+2+No+Rg), - PROC(lock, lockargs, res, args, res, Ck+St), - PROC(cancel, cancargs, res, args, res, Ck+St), - PROC(unlock, unlockargs, res, args, res, Ck+St), - PROC(granted, testargs, res, args, res, Ck+St), - PROC(test_msg, testargs, norep, args, void, 1), - PROC(lock_msg, lockargs, norep, args, void, 1), - PROC(cancel_msg, cancargs, norep, args, void, 1), - PROC(unlock_msg, unlockargs, norep, args, void, 1), - PROC(granted_msg, testargs, norep, args, void, 1), - PROC(test_res, testres, norep, res, void, 1), - PROC(lock_res, lockres, norep, res, void, 1), - PROC(cancel_res, cancelres, norep, res, void, 1), - PROC(unlock_res, unlockres, norep, res, void, 1), - PROC(granted_res, res, norep, res, void, 1), - /* statd callback */ - PROC(sm_notify, reboot, void, reboot, void, 1), - PROC(none, void, void, void, void, 0), - PROC(none, void, void, void, void, 0), - PROC(none, void, void, void, void, 0), - PROC(share, shareargs, shareres, args, res, Ck+St+1), - PROC(unshare, shareargs, shareres, args, res, Ck+St+1), - PROC(nm_lock, lockargs, res, args, res, Ck+St), - PROC(free_all, notify, void, args, void, 1), +const struct svc_procedure nlmsvc_procedures4[24] = { + [NLMPROC_NULL] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_TEST] = { + .pc_func = nlm4svc_proc_test, + .pc_decode = nlm4svc_decode_testargs, + .pc_encode = nlm4svc_encode_testres, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St+2+No+Rg, + }, + [NLMPROC_LOCK] = { + .pc_func = nlm4svc_proc_lock, + .pc_decode = nlm4svc_decode_lockargs, + .pc_encode = nlm4svc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_CANCEL] = { + .pc_func = nlm4svc_proc_cancel, + .pc_decode = nlm4svc_decode_cancargs, + .pc_encode = nlm4svc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_UNLOCK] = { + .pc_func = nlm4svc_proc_unlock, + .pc_decode = nlm4svc_decode_unlockargs, + .pc_encode = nlm4svc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_GRANTED] = { + .pc_func = nlm4svc_proc_granted, + .pc_decode = nlm4svc_decode_testargs, + .pc_encode = nlm4svc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_TEST_MSG] = { + .pc_func = nlm4svc_proc_test_msg, + .pc_decode = nlm4svc_decode_testargs, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_LOCK_MSG] = { + .pc_func = nlm4svc_proc_lock_msg, + .pc_decode = nlm4svc_decode_lockargs, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_CANCEL_MSG] = { + .pc_func = nlm4svc_proc_cancel_msg, + .pc_decode = nlm4svc_decode_cancargs, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_UNLOCK_MSG] = { + .pc_func = nlm4svc_proc_unlock_msg, + .pc_decode = nlm4svc_decode_unlockargs, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_GRANTED_MSG] = { + .pc_func = nlm4svc_proc_granted_msg, + .pc_decode = nlm4svc_decode_testargs, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_TEST_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_LOCK_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_CANCEL_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_UNLOCK_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_GRANTED_RES] = { + .pc_func = nlm4svc_proc_granted_res, + .pc_decode = nlm4svc_decode_res, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_NSM_NOTIFY] = { + .pc_func = nlm4svc_proc_sm_notify, + .pc_decode = nlm4svc_decode_reboot, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_reboot), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [17] = { + .pc_func = nlm4svc_proc_unused, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = 0, + }, + [18] = { + .pc_func = nlm4svc_proc_unused, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = 0, + }, + [19] = { + .pc_func = nlm4svc_proc_unused, + .pc_decode = nlm4svc_decode_void, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = 0, + }, + [NLMPROC_SHARE] = { + .pc_func = nlm4svc_proc_share, + .pc_decode = nlm4svc_decode_shareargs, + .pc_encode = nlm4svc_encode_shareres, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St+1, + }, + [NLMPROC_UNSHARE] = { + .pc_func = nlm4svc_proc_unshare, + .pc_decode = nlm4svc_decode_shareargs, + .pc_encode = nlm4svc_encode_shareres, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St+1, + }, + [NLMPROC_NM_LOCK] = { + .pc_func = nlm4svc_proc_nm_lock, + .pc_decode = nlm4svc_decode_lockargs, + .pc_encode = nlm4svc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_FREE_ALL] = { + .pc_func = nlm4svc_proc_free_all, + .pc_decode = nlm4svc_decode_notify, + .pc_encode = nlm4svc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, }; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index d0bb7a6bf005..50855f2c1f4b 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -529,66 +529,214 @@ nlmsvc_proc_granted_res(struct svc_rqst *rqstp) return rpc_success; } +static __be32 +nlmsvc_proc_unused(struct svc_rqst *rqstp) +{ + return rpc_proc_unavail; +} + /* * NLM Server procedures. */ -#define nlmsvc_encode_norep nlmsvc_encode_void -#define nlmsvc_decode_norep nlmsvc_decode_void -#define nlmsvc_decode_testres nlmsvc_decode_void -#define nlmsvc_decode_lockres nlmsvc_decode_void -#define nlmsvc_decode_unlockres nlmsvc_decode_void -#define nlmsvc_decode_cancelres nlmsvc_decode_void -#define nlmsvc_decode_grantedres nlmsvc_decode_void - -#define nlmsvc_proc_none nlmsvc_proc_null -#define nlmsvc_proc_test_res nlmsvc_proc_null -#define nlmsvc_proc_lock_res nlmsvc_proc_null -#define nlmsvc_proc_cancel_res nlmsvc_proc_null -#define nlmsvc_proc_unlock_res nlmsvc_proc_null - struct nlm_void { int dummy; }; -#define PROC(name, xargt, xrest, argt, rest, respsize) \ - { .pc_func = nlmsvc_proc_##name, \ - .pc_decode = nlmsvc_decode_##xargt, \ - .pc_encode = nlmsvc_encode_##xrest, \ - .pc_release = NULL, \ - .pc_argsize = sizeof(struct nlm_##argt), \ - .pc_ressize = sizeof(struct nlm_##rest), \ - .pc_xdrressize = respsize, \ - } - #define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */ #define St 1 /* status */ #define No (1+1024/4) /* Net Obj */ #define Rg 2 /* range - offset + size */ -const struct svc_procedure nlmsvc_procedures[] = { - PROC(null, void, void, void, void, 1), - PROC(test, testargs, testres, args, res, Ck+St+2+No+Rg), - PROC(lock, lockargs, res, args, res, Ck+St), - PROC(cancel, cancargs, res, args, res, Ck+St), - PROC(unlock, unlockargs, res, args, res, Ck+St), - PROC(granted, testargs, res, args, res, Ck+St), - PROC(test_msg, testargs, norep, args, void, 1), - PROC(lock_msg, lockargs, norep, args, void, 1), - PROC(cancel_msg, cancargs, norep, args, void, 1), - PROC(unlock_msg, unlockargs, norep, args, void, 1), - PROC(granted_msg, testargs, norep, args, void, 1), - PROC(test_res, testres, norep, res, void, 1), - PROC(lock_res, lockres, norep, res, void, 1), - PROC(cancel_res, cancelres, norep, res, void, 1), - PROC(unlock_res, unlockres, norep, res, void, 1), - PROC(granted_res, res, norep, res, void, 1), - /* statd callback */ - PROC(sm_notify, reboot, void, reboot, void, 1), - PROC(none, void, void, void, void, 1), - PROC(none, void, void, void, void, 1), - PROC(none, void, void, void, void, 1), - PROC(share, shareargs, shareres, args, res, Ck+St+1), - PROC(unshare, shareargs, shareres, args, res, Ck+St+1), - PROC(nm_lock, lockargs, res, args, res, Ck+St), - PROC(free_all, notify, void, args, void, 0), - +const struct svc_procedure nlmsvc_procedures[24] = { + [NLMPROC_NULL] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_TEST] = { + .pc_func = nlmsvc_proc_test, + .pc_decode = nlmsvc_decode_testargs, + .pc_encode = nlmsvc_encode_testres, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St+2+No+Rg, + }, + [NLMPROC_LOCK] = { + .pc_func = nlmsvc_proc_lock, + .pc_decode = nlmsvc_decode_lockargs, + .pc_encode = nlmsvc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_CANCEL] = { + .pc_func = nlmsvc_proc_cancel, + .pc_decode = nlmsvc_decode_cancargs, + .pc_encode = nlmsvc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_UNLOCK] = { + .pc_func = nlmsvc_proc_unlock, + .pc_decode = nlmsvc_decode_unlockargs, + .pc_encode = nlmsvc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_GRANTED] = { + .pc_func = nlmsvc_proc_granted, + .pc_decode = nlmsvc_decode_testargs, + .pc_encode = nlmsvc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_TEST_MSG] = { + .pc_func = nlmsvc_proc_test_msg, + .pc_decode = nlmsvc_decode_testargs, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_LOCK_MSG] = { + .pc_func = nlmsvc_proc_lock_msg, + .pc_decode = nlmsvc_decode_lockargs, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_CANCEL_MSG] = { + .pc_func = nlmsvc_proc_cancel_msg, + .pc_decode = nlmsvc_decode_cancargs, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_UNLOCK_MSG] = { + .pc_func = nlmsvc_proc_unlock_msg, + .pc_decode = nlmsvc_decode_unlockargs, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_GRANTED_MSG] = { + .pc_func = nlmsvc_proc_granted_msg, + .pc_decode = nlmsvc_decode_testargs, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_TEST_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_LOCK_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_CANCEL_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_UNLOCK_RES] = { + .pc_func = nlmsvc_proc_null, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_GRANTED_RES] = { + .pc_func = nlmsvc_proc_granted_res, + .pc_decode = nlmsvc_decode_res, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_res), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_NSM_NOTIFY] = { + .pc_func = nlmsvc_proc_sm_notify, + .pc_decode = nlmsvc_decode_reboot, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_reboot), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [17] = { + .pc_func = nlmsvc_proc_unused, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [18] = { + .pc_func = nlmsvc_proc_unused, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [19] = { + .pc_func = nlmsvc_proc_unused, + .pc_decode = nlmsvc_decode_void, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_void), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = St, + }, + [NLMPROC_SHARE] = { + .pc_func = nlmsvc_proc_share, + .pc_decode = nlmsvc_decode_shareargs, + .pc_encode = nlmsvc_encode_shareres, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St+1, + }, + [NLMPROC_UNSHARE] = { + .pc_func = nlmsvc_proc_unshare, + .pc_decode = nlmsvc_decode_shareargs, + .pc_encode = nlmsvc_encode_shareres, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St+1, + }, + [NLMPROC_NM_LOCK] = { + .pc_func = nlmsvc_proc_nm_lock, + .pc_decode = nlmsvc_decode_lockargs, + .pc_encode = nlmsvc_encode_res, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_res), + .pc_xdrressize = Ck+St, + }, + [NLMPROC_FREE_ALL] = { + .pc_func = nlmsvc_proc_free_all, + .pc_decode = nlmsvc_decode_notify, + .pc_encode = nlmsvc_encode_void, + .pc_argsize = sizeof(struct nlm_args), + .pc_ressize = sizeof(struct nlm_void), + .pc_xdrressize = 0, + }, }; diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index 91be7f628e4a..9d354de613da 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -9,6 +9,7 @@ #include <linux/falloc.h> #include <linux/mount.h> #include <linux/nfs_fs.h> +#include <linux/nfs_ssc.h> #include "delegation.h" #include "internal.h" #include "iostat.h" @@ -315,9 +316,8 @@ out: static int read_name_gen = 1; #define SSC_READ_NAME_BODY "ssc_read_%d" -struct file * -nfs42_ssc_open(struct vfsmount *ss_mnt, struct nfs_fh *src_fh, - nfs4_stateid *stateid) +static struct file *__nfs42_ssc_open(struct vfsmount *ss_mnt, + struct nfs_fh *src_fh, nfs4_stateid *stateid) { struct nfs_fattr fattr; struct file *filep, *res; @@ -399,14 +399,40 @@ out_filep: fput(filep); goto out_free_name; } -EXPORT_SYMBOL_GPL(nfs42_ssc_open); -void nfs42_ssc_close(struct file *filep) + +static void __nfs42_ssc_close(struct file *filep) { struct nfs_open_context *ctx = nfs_file_open_context(filep); ctx->state->flags = 0; } -EXPORT_SYMBOL_GPL(nfs42_ssc_close); + +static const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = { + .sco_open = __nfs42_ssc_open, + .sco_close = __nfs42_ssc_close, +}; + +/** + * nfs42_ssc_register_ops - Wrapper to register NFS_V4 ops in nfs_common + * + * Return values: + * None + */ +void nfs42_ssc_register_ops(void) +{ + nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl); +} + +/** + * nfs42_ssc_unregister_ops - wrapper to un-register NFS_V4 ops in nfs_common + * + * Return values: + * None. + */ +void nfs42_ssc_unregister_ops(void) +{ + nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl); +} #endif /* CONFIG_NFS_V4_2 */ const struct file_operations nfs4_file_operations = { diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 0c1ab846b83d..93f5c1678ec2 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -7,6 +7,7 @@ #include <linux/mount.h> #include <linux/nfs4_mount.h> #include <linux/nfs_fs.h> +#include <linux/nfs_ssc.h> #include "delegation.h" #include "internal.h" #include "nfs4_fs.h" @@ -279,6 +280,9 @@ static int __init init_nfs_v4(void) if (err) goto out2; +#ifdef CONFIG_NFS_V4_2 + nfs42_ssc_register_ops(); +#endif register_nfs_version(&nfs_v4); return 0; out2: @@ -297,6 +301,7 @@ static void __exit exit_nfs_v4(void) unregister_nfs_version(&nfs_v4); #ifdef CONFIG_NFS_V4_2 nfs4_xattr_cache_exit(); + nfs42_ssc_unregister_ops(); #endif nfs4_unregister_sysctl(); nfs_idmap_quit(); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 78c46a517fcf..4034102010f0 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -57,6 +57,7 @@ #include <linux/rcupdate.h> #include <linux/uaccess.h> +#include <linux/nfs_ssc.h> #include "nfs4_fs.h" #include "callback.h" @@ -85,6 +86,10 @@ const struct super_operations nfs_sops = { }; EXPORT_SYMBOL_GPL(nfs_sops); +static const struct nfs_ssc_client_ops nfs_ssc_clnt_ops_tbl = { + .sco_sb_deactive = nfs_sb_deactive, +}; + #if IS_ENABLED(CONFIG_NFS_V4) static int __init register_nfs4_fs(void) { @@ -106,6 +111,16 @@ static void unregister_nfs4_fs(void) } #endif +static void nfs_ssc_register_ops(void) +{ + nfs_ssc_register(&nfs_ssc_clnt_ops_tbl); +} + +static void nfs_ssc_unregister_ops(void) +{ + nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl); +} + static struct shrinker acl_shrinker = { .count_objects = nfs_access_cache_count, .scan_objects = nfs_access_cache_scan, @@ -133,6 +148,7 @@ int __init register_nfs_fs(void) ret = register_shrinker(&acl_shrinker); if (ret < 0) goto error_3; + nfs_ssc_register_ops(); return 0; error_3: nfs_unregister_sysctl(); @@ -152,6 +168,7 @@ void __exit unregister_nfs_fs(void) unregister_shrinker(&acl_shrinker); nfs_unregister_sysctl(); unregister_nfs4_fs(); + nfs_ssc_unregister_ops(); unregister_filesystem(&nfs_fs_type); } diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile index 4bebe834c009..fa82f5aaa6d9 100644 --- a/fs/nfs_common/Makefile +++ b/fs/nfs_common/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o nfs_acl-objs := nfsacl.o obj-$(CONFIG_GRACE_PERIOD) += grace.o +obj-$(CONFIG_GRACE_PERIOD) += nfs_ssc.o diff --git a/fs/nfs_common/nfs_ssc.c b/fs/nfs_common/nfs_ssc.c new file mode 100644 index 000000000000..f43bbb373913 --- /dev/null +++ b/fs/nfs_common/nfs_ssc.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * fs/nfs_common/nfs_ssc_comm.c + * + * Helper for knfsd's SSC to access ops in NFS client modules + * + * Author: Dai Ngo <dai.ngo@oracle.com> + * + * Copyright (c) 2020, Oracle and/or its affiliates. + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/nfs_ssc.h> +#include "../nfs/nfs4_fs.h" + +MODULE_LICENSE("GPL"); + +struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl; +EXPORT_SYMBOL_GPL(nfs_ssc_client_tbl); + +#ifdef CONFIG_NFS_V4_2 +/** + * nfs42_ssc_register - install the NFS_V4 client ops in the nfs_ssc_client_tbl + * @ops: NFS_V4 ops to be installed + * + * Return values: + * None + */ +void nfs42_ssc_register(const struct nfs4_ssc_client_ops *ops) +{ + nfs_ssc_client_tbl.ssc_nfs4_ops = ops; +} +EXPORT_SYMBOL_GPL(nfs42_ssc_register); + +/** + * nfs42_ssc_unregister - uninstall the NFS_V4 client ops from + * the nfs_ssc_client_tbl + * @ops: ops to be uninstalled + * + * Return values: + * None + */ +void nfs42_ssc_unregister(const struct nfs4_ssc_client_ops *ops) +{ + if (nfs_ssc_client_tbl.ssc_nfs4_ops != ops) + return; + + nfs_ssc_client_tbl.ssc_nfs4_ops = NULL; +} +EXPORT_SYMBOL_GPL(nfs42_ssc_unregister); +#endif /* CONFIG_NFS_V4_2 */ + +#ifdef CONFIG_NFS_V4_2 +/** + * nfs_ssc_register - install the NFS_FS client ops in the nfs_ssc_client_tbl + * @ops: NFS_FS ops to be installed + * + * Return values: + * None + */ +void nfs_ssc_register(const struct nfs_ssc_client_ops *ops) +{ + nfs_ssc_client_tbl.ssc_nfs_ops = ops; +} +EXPORT_SYMBOL_GPL(nfs_ssc_register); + +/** + * nfs_ssc_unregister - uninstall the NFS_FS client ops from + * the nfs_ssc_client_tbl + * @ops: ops to be uninstalled + * + * Return values: + * None + */ +void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops) +{ + if (nfs_ssc_client_tbl.ssc_nfs_ops != ops) + return; + nfs_ssc_client_tbl.ssc_nfs_ops = NULL; +} +EXPORT_SYMBOL_GPL(nfs_ssc_unregister); + +#else +void nfs_ssc_register(const struct nfs_ssc_client_ops *ops) +{ +} +EXPORT_SYMBOL_GPL(nfs_ssc_register); + +void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops) +{ +} +EXPORT_SYMBOL_GPL(nfs_ssc_unregister); +#endif /* CONFIG_NFS_V4_2 */ diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index 99d2cae91bd6..dbbc583d6273 100644 --- a/fs/nfsd/Kconfig +++ b/fs/nfsd/Kconfig @@ -136,7 +136,7 @@ config NFSD_FLEXFILELAYOUT config NFSD_V4_2_INTER_SSC bool "NFSv4.2 inter server to server COPY" - depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2 && NFS_FS=y + depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2 help This option enables support for NFSv4.2 inter server to server copy where the destination server calls the NFSv4.2 @@ -156,13 +156,3 @@ config NFSD_V4_SECURITY_LABEL 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 && DEBUG_FS && BROKEN - 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. diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile index 6a40b1afe703..3f0983e93a99 100644 --- a/fs/nfsd/Makefile +++ b/fs/nfsd/Makefile @@ -13,7 +13,6 @@ nfsd-y += trace.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 filecache.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 diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index cb777fe82988..21e404e7cb68 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1002,7 +1002,7 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp) if (nfsd4_spo_must_allow(rqstp)) return 0; - return nfserr_wrongsec; + return rqstp->rq_vers < 4 ? nfserr_acces : nfserr_wrongsec; } /* diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index c8b9d2667ee6..3c6c2f7d1688 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -889,7 +889,7 @@ nfsd_file_find_locked(struct inode *inode, unsigned int may_flags, hlist_for_each_entry_rcu(nf, &nfsd_file_hashtbl[hashval].nfb_head, nf_node, lockdep_is_held(&nfsd_file_hashtbl[hashval].nfb_lock)) { - if ((need & nf->nf_may) != need) + if (nf->nf_may != need) continue; if (nf->nf_inode != inode) continue; diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index cbab1d2d8a75..6a900f770dd2 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -14,7 +14,6 @@ #include "vfs.h" #define NFSDDBG_FACILITY NFSDDBG_PROC -#define RETURN_STATUS(st) { resp->status = (st); return (st); } /* * NULL call. @@ -22,7 +21,7 @@ static __be32 nfsacld_proc_null(struct svc_rqst *rqstp) { - return nfs_ok; + return rpc_success; } /* @@ -35,24 +34,25 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp) 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); + resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + if (resp->status != nfs_ok) + goto out; inode = d_inode(fh->fh_dentry); - if (argp->mask & ~NFS_ACL_MASK) - RETURN_STATUS(nfserr_inval); + if (argp->mask & ~NFS_ACL_MASK) { + resp->status = nfserr_inval; + goto out; + } resp->mask = argp->mask; - nfserr = fh_getattr(fh, &resp->stat); - if (nfserr) - RETURN_STATUS(nfserr); + resp->status = fh_getattr(fh, &resp->stat); + if (resp->status != nfs_ok) + goto out; if (resp->mask & (NFS_ACL|NFS_ACLCNT)) { acl = get_acl(inode, ACL_TYPE_ACCESS); @@ -61,7 +61,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp) acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); } if (IS_ERR(acl)) { - nfserr = nfserrno(PTR_ERR(acl)); + resp->status = nfserrno(PTR_ERR(acl)); goto fail; } resp->acl_access = acl; @@ -71,19 +71,20 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp) of a non-directory! */ acl = get_acl(inode, ACL_TYPE_DEFAULT); if (IS_ERR(acl)) { - nfserr = nfserrno(PTR_ERR(acl)); + resp->status = nfserrno(PTR_ERR(acl)); goto fail; } resp->acl_default = acl; } /* resp->acl_{access,default} are released in nfssvc_release_getacl. */ - RETURN_STATUS(0); +out: + return rpc_success; fail: posix_acl_release(resp->acl_access); posix_acl_release(resp->acl_default); - RETURN_STATUS(nfserr); + goto out; } /* @@ -95,14 +96,13 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) struct nfsd_attrstat *resp = rqstp->rq_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) + resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); + if (resp->status != nfs_ok) goto out; inode = d_inode(fh->fh_dentry); @@ -124,19 +124,20 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) fh_drop_write(fh); - nfserr = fh_getattr(fh, &resp->stat); + resp->status = 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; + return rpc_success; + out_drop_lock: fh_unlock(fh); fh_drop_write(fh); out_errno: - nfserr = nfserrno(error); + resp->status = nfserrno(error); goto out; } @@ -147,15 +148,16 @@ static __be32 nfsacld_proc_getattr(struct svc_rqst *rqstp) { struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd_attrstat *resp = rqstp->rq_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; + resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + if (resp->status != nfs_ok) + goto out; + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; } /* @@ -165,7 +167,6 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp) { struct nfsd3_accessargs *argp = rqstp->rq_argp; struct nfsd3_accessres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: ACCESS(2acl) %s 0x%x\n", SVCFH_fmt(&argp->fh), @@ -173,16 +174,22 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp) 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; + resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL); + if (resp->status != nfs_ok) + goto out; + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; } /* * XDR decode functions */ +static int nfsaclsvc_decode_voidarg(struct svc_rqst *rqstp, __be32 *p) +{ + return 1; +} + static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_getaclargs *argp = rqstp->rq_argp; @@ -268,6 +275,10 @@ static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p) int n; int w; + *p++ = resp->status; + if (resp->status != nfs_ok) + return xdr_ressize_check(rqstp, p); + /* * Since this is version 2, the check for nfserr in * nfsd_dispatch actually ensures the following cannot happen. @@ -307,7 +318,12 @@ static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd_attrstat *resp = rqstp->rq_resp; + *p++ = resp->status; + if (resp->status != nfs_ok) + goto out; + p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat); +out: return xdr_ressize_check(rqstp, p); } @@ -316,8 +332,13 @@ static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_accessres *resp = rqstp->rq_resp; + *p++ = resp->status; + if (resp->status != nfs_ok) + goto out; + p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat); *p++ = htonl(resp->access); +out: return xdr_ressize_check(rqstp, p); } @@ -347,36 +368,63 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp) fh_put(&resp->fh); } -#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) \ -{ \ - .pc_func = nfsacld_proc_##name, \ - .pc_decode = nfsaclsvc_decode_##argt##args, \ - .pc_encode = nfsaclsvc_encode_##rest##res, \ - .pc_release = nfsaclsvc_release_##relt, \ - .pc_argsize = sizeof(struct nfsd3_##argt##args), \ - .pc_ressize = sizeof(struct nfsd3_##rest##res), \ - .pc_cachetype = cache, \ - .pc_xdrressize = 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 const 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), +static const struct svc_procedure nfsd_acl_procedures2[5] = { + [ACLPROC2_NULL] = { + .pc_func = nfsacld_proc_null, + .pc_decode = nfsaclsvc_decode_voidarg, + .pc_encode = nfsaclsvc_encode_voidres, + .pc_argsize = sizeof(struct nfsd3_voidargs), + .pc_ressize = sizeof(struct nfsd3_voidargs), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST, + }, + [ACLPROC2_GETACL] = { + .pc_func = nfsacld_proc_getacl, + .pc_decode = nfsaclsvc_decode_getaclargs, + .pc_encode = nfsaclsvc_encode_getaclres, + .pc_release = nfsaclsvc_release_getacl, + .pc_argsize = sizeof(struct nfsd3_getaclargs), + .pc_ressize = sizeof(struct nfsd3_getaclres), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST+1+2*(1+ACL), + }, + [ACLPROC2_SETACL] = { + .pc_func = nfsacld_proc_setacl, + .pc_decode = nfsaclsvc_decode_setaclargs, + .pc_encode = nfsaclsvc_encode_attrstatres, + .pc_release = nfsaclsvc_release_attrstat, + .pc_argsize = sizeof(struct nfsd3_setaclargs), + .pc_ressize = sizeof(struct nfsd_attrstat), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST+AT, + }, + [ACLPROC2_GETATTR] = { + .pc_func = nfsacld_proc_getattr, + .pc_decode = nfsaclsvc_decode_fhandleargs, + .pc_encode = nfsaclsvc_encode_attrstatres, + .pc_release = nfsaclsvc_release_attrstat, + .pc_argsize = sizeof(struct nfsd_fhandle), + .pc_ressize = sizeof(struct nfsd_attrstat), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST+AT, + }, + [ACLPROC2_ACCESS] = { + .pc_func = nfsacld_proc_access, + .pc_decode = nfsaclsvc_decode_accessargs, + .pc_encode = nfsaclsvc_encode_accessres, + .pc_release = nfsaclsvc_release_access, + .pc_argsize = sizeof(struct nfsd3_accessargs), + .pc_ressize = sizeof(struct nfsd3_accessres), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST+AT+1, + }, }; static unsigned int nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)]; diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 13bca4a2f89d..34a394e50e1d 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -13,15 +13,13 @@ #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) { - return nfs_ok; + return rpc_success; } /* @@ -34,17 +32,18 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp) 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); + resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + if (resp->status != nfs_ok) + goto out; inode = d_inode(fh->fh_dentry); - if (argp->mask & ~NFS_ACL_MASK) - RETURN_STATUS(nfserr_inval); + if (argp->mask & ~NFS_ACL_MASK) { + resp->status = nfserr_inval; + goto out; + } resp->mask = argp->mask; if (resp->mask & (NFS_ACL|NFS_ACLCNT)) { @@ -54,7 +53,7 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp) acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); } if (IS_ERR(acl)) { - nfserr = nfserrno(PTR_ERR(acl)); + resp->status = nfserrno(PTR_ERR(acl)); goto fail; } resp->acl_access = acl; @@ -64,19 +63,20 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp) of a non-directory! */ acl = get_acl(inode, ACL_TYPE_DEFAULT); if (IS_ERR(acl)) { - nfserr = nfserrno(PTR_ERR(acl)); + resp->status = nfserrno(PTR_ERR(acl)); goto fail; } resp->acl_default = acl; } /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */ - RETURN_STATUS(0); +out: + return rpc_success; fail: posix_acl_release(resp->acl_access); posix_acl_release(resp->acl_default); - RETURN_STATUS(nfserr); + goto out; } /* @@ -88,12 +88,11 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp) struct nfsd3_attrstat *resp = rqstp->rq_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) + resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); + if (resp->status != nfs_ok) goto out; inode = d_inode(fh->fh_dentry); @@ -113,13 +112,13 @@ out_drop_lock: fh_unlock(fh); fh_drop_write(fh); out_errno: - nfserr = nfserrno(error); + resp->status = 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); + return rpc_success; } /* @@ -174,6 +173,7 @@ static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p) struct nfsd3_getaclres *resp = rqstp->rq_resp; struct dentry *dentry = resp->fh.fh_dentry; + *p++ = resp->status; p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0 && dentry && d_really_is_positive(dentry)) { struct inode *inode = d_inode(dentry); @@ -218,8 +218,8 @@ static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_attrstat *resp = rqstp->rq_resp; + *p++ = resp->status; p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh); - return xdr_ressize_check(rqstp, p); } @@ -235,33 +235,43 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp) posix_acl_release(resp->acl_default); } -#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) \ -{ \ - .pc_func = nfsd3_proc_##name, \ - .pc_decode = nfs3svc_decode_##argt##args, \ - .pc_encode = nfs3svc_encode_##rest##res, \ - .pc_release = nfs3svc_release_##relt, \ - .pc_argsize = sizeof(struct nfsd3_##argt##args), \ - .pc_ressize = sizeof(struct nfsd3_##rest##res), \ - .pc_cachetype = cache, \ - .pc_xdrressize = 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 const 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), +static const struct svc_procedure nfsd_acl_procedures3[3] = { + [ACLPROC3_NULL] = { + .pc_func = nfsd3_proc_null, + .pc_decode = nfs3svc_decode_voidarg, + .pc_encode = nfs3svc_encode_voidres, + .pc_argsize = sizeof(struct nfsd3_voidargs), + .pc_ressize = sizeof(struct nfsd3_voidargs), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST, + }, + [ACLPROC3_GETACL] = { + .pc_func = nfsd3_proc_getacl, + .pc_decode = nfs3svc_decode_getaclargs, + .pc_encode = nfs3svc_encode_getaclres, + .pc_release = nfs3svc_release_getacl, + .pc_argsize = sizeof(struct nfsd3_getaclargs), + .pc_ressize = sizeof(struct nfsd3_getaclres), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST+1+2*(1+ACL), + }, + [ACLPROC3_SETACL] = { + .pc_func = nfsd3_proc_setacl, + .pc_decode = nfs3svc_decode_setaclargs, + .pc_encode = nfs3svc_encode_setaclres, + .pc_release = nfs3svc_release_fhandle, + .pc_argsize = sizeof(struct nfsd3_setaclargs), + .pc_ressize = sizeof(struct nfsd3_attrstat), + .pc_cachetype = RC_NOCACHE, + .pc_xdrressize = ST+pAT, + }, }; static unsigned int nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)]; diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 288bc76b4574..14468613d150 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -15,8 +15,6 @@ #define NFSDDBG_FACILITY NFSDDBG_PROC -#define RETURN_STATUS(st) { resp->status = (st); return (st); } - static int nfs3_ftypes[] = { 0, /* NF3NON */ S_IFREG, /* NF3REG */ @@ -34,7 +32,7 @@ static int nfs3_ftypes[] = { static __be32 nfsd3_proc_null(struct svc_rqst *rqstp) { - return nfs_ok; + return rpc_success; } /* @@ -45,20 +43,19 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp) { struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd3_attrstat *resp = rqstp->rq_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); + resp->status = fh_verify(rqstp, &resp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); + if (resp->status != nfs_ok) + goto out; + + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; } /* @@ -69,15 +66,14 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp) { struct nfsd3_sattrargs *argp = rqstp->rq_argp; struct nfsd3_attrstat *resp = rqstp->rq_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); + resp->status = nfsd_setattr(rqstp, &resp->fh, &argp->attrs, + argp->check_guard, argp->guardtime); + return rpc_success; } /* @@ -88,7 +84,6 @@ nfsd3_proc_lookup(struct svc_rqst *rqstp) { struct nfsd3_diropargs *argp = rqstp->rq_argp; struct nfsd3_diropres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: LOOKUP(3) %s %.*s\n", SVCFH_fmt(&argp->fh), @@ -98,11 +93,10 @@ nfsd3_proc_lookup(struct svc_rqst *rqstp) 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); + resp->status = nfsd_lookup(rqstp, &resp->dirfh, + argp->name, argp->len, + &resp->fh); + return rpc_success; } /* @@ -113,7 +107,6 @@ nfsd3_proc_access(struct svc_rqst *rqstp) { struct nfsd3_accessargs *argp = rqstp->rq_argp; struct nfsd3_accessres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: ACCESS(3) %s 0x%x\n", SVCFH_fmt(&argp->fh), @@ -121,8 +114,8 @@ nfsd3_proc_access(struct svc_rqst *rqstp) fh_copy(&resp->fh, &argp->fh); resp->access = argp->access; - nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL); - RETURN_STATUS(nfserr); + resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL); + return rpc_success; } /* @@ -133,15 +126,14 @@ nfsd3_proc_readlink(struct svc_rqst *rqstp) { struct nfsd3_readlinkargs *argp = rqstp->rq_argp; struct nfsd3_readlinkres *resp = rqstp->rq_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); + resp->status = nfsd_readlink(rqstp, &resp->fh, argp->buffer, &resp->len); + return rpc_success; } /* @@ -152,7 +144,6 @@ nfsd3_proc_read(struct svc_rqst *rqstp) { struct nfsd3_readargs *argp = rqstp->rq_argp; struct nfsd3_readres *resp = rqstp->rq_resp; - __be32 nfserr; u32 max_blocksize = svc_max_payload(rqstp); unsigned long cnt = min(argp->count, max_blocksize); @@ -169,12 +160,10 @@ nfsd3_proc_read(struct svc_rqst *rqstp) 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, - &resp->eof); - RETURN_STATUS(nfserr); + resp->status = nfsd_read(rqstp, &resp->fh, argp->offset, + rqstp->rq_vec, argp->vlen, &resp->count, + &resp->eof); + return rpc_success; } /* @@ -185,7 +174,6 @@ nfsd3_proc_write(struct svc_rqst *rqstp) { struct nfsd3_writeargs *argp = rqstp->rq_argp; struct nfsd3_writeres *resp = rqstp->rq_resp; - __be32 nfserr; unsigned long cnt = argp->len; unsigned int nvecs; @@ -199,13 +187,16 @@ nfsd3_proc_write(struct svc_rqst *rqstp) resp->committed = argp->stable; nvecs = svc_fill_write_vector(rqstp, rqstp->rq_arg.pages, &argp->first, cnt); - if (!nvecs) - RETURN_STATUS(nfserr_io); - nfserr = nfsd_write(rqstp, &resp->fh, argp->offset, - rqstp->rq_vec, nvecs, &cnt, - resp->committed, resp->verf); + if (!nvecs) { + resp->status = nfserr_io; + goto out; + } + resp->status = nfsd_write(rqstp, &resp->fh, argp->offset, + rqstp->rq_vec, nvecs, &cnt, + resp->committed, resp->verf); resp->count = cnt; - RETURN_STATUS(nfserr); +out: + return rpc_success; } /* @@ -220,7 +211,6 @@ nfsd3_proc_create(struct svc_rqst *rqstp) struct nfsd3_diropres *resp = rqstp->rq_resp; svc_fh *dirfhp, *newfhp = NULL; struct iattr *attr; - __be32 nfserr; dprintk("nfsd: CREATE(3) %s %.*s\n", SVCFH_fmt(&argp->fh), @@ -241,11 +231,10 @@ nfsd3_proc_create(struct svc_rqst *rqstp) } /* 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); + resp->status = do_nfsd_create(rqstp, dirfhp, argp->name, argp->len, + attr, newfhp, argp->createmode, + (u32 *)argp->verf, NULL, NULL); + return rpc_success; } /* @@ -256,7 +245,6 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp) { struct nfsd3_createargs *argp = rqstp->rq_argp; struct nfsd3_diropres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: MKDIR(3) %s %.*s\n", SVCFH_fmt(&argp->fh), @@ -266,10 +254,10 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp) 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); + resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, + &argp->attrs, S_IFDIR, 0, &resp->fh); fh_unlock(&resp->dirfh); - RETURN_STATUS(nfserr); + return rpc_success; } static __be32 @@ -277,18 +265,23 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp) { struct nfsd3_symlinkargs *argp = rqstp->rq_argp; struct nfsd3_diropres *resp = rqstp->rq_resp; - __be32 nfserr; - if (argp->tlen == 0) - RETURN_STATUS(nfserr_inval); - if (argp->tlen > NFS3_MAXPATHLEN) - RETURN_STATUS(nfserr_nametoolong); + if (argp->tlen == 0) { + resp->status = nfserr_inval; + goto out; + } + if (argp->tlen > NFS3_MAXPATHLEN) { + resp->status = nfserr_nametoolong; + goto out; + } argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first, page_address(rqstp->rq_arg.pages[0]), argp->tlen); - if (IS_ERR(argp->tname)) - RETURN_STATUS(nfserrno(PTR_ERR(argp->tname))); + if (IS_ERR(argp->tname)) { + resp->status = nfserrno(PTR_ERR(argp->tname)); + goto out; + } dprintk("nfsd: SYMLINK(3) %s %.*s -> %.*s\n", SVCFH_fmt(&argp->ffh), @@ -297,10 +290,11 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp) 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); + resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, + argp->flen, argp->tname, &resp->fh); kfree(argp->tname); - RETURN_STATUS(nfserr); +out: + return rpc_success; } /* @@ -311,7 +305,6 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp) { struct nfsd3_mknodargs *argp = rqstp->rq_argp; struct nfsd3_diropres *resp = rqstp->rq_resp; - __be32 nfserr; int type; dev_t rdev = 0; @@ -323,22 +316,28 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp) 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 == 0 || argp->ftype >= NF3BAD) { + resp->status = nfserr_inval; + goto out; + } 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); + MINOR(rdev) != argp->minor) { + resp->status = nfserr_inval; + goto out; + } + } else if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO) { + resp->status = nfserr_inval; + goto out; + } type = nfs3_ftypes[argp->ftype]; - nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, - &argp->attrs, type, rdev, &resp->fh); + resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, + &argp->attrs, type, rdev, &resp->fh); fh_unlock(&resp->dirfh); - RETURN_STATUS(nfserr); +out: + return rpc_success; } /* @@ -349,7 +348,6 @@ nfsd3_proc_remove(struct svc_rqst *rqstp) { struct nfsd3_diropargs *argp = rqstp->rq_argp; struct nfsd3_attrstat *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: REMOVE(3) %s %.*s\n", SVCFH_fmt(&argp->fh), @@ -358,9 +356,10 @@ nfsd3_proc_remove(struct svc_rqst *rqstp) /* 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); + resp->status = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, + argp->name, argp->len); fh_unlock(&resp->fh); - RETURN_STATUS(nfserr); + return rpc_success; } /* @@ -371,7 +370,6 @@ nfsd3_proc_rmdir(struct svc_rqst *rqstp) { struct nfsd3_diropargs *argp = rqstp->rq_argp; struct nfsd3_attrstat *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: RMDIR(3) %s %.*s\n", SVCFH_fmt(&argp->fh), @@ -379,9 +377,10 @@ nfsd3_proc_rmdir(struct svc_rqst *rqstp) argp->name); fh_copy(&resp->fh, &argp->fh); - nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len); + resp->status = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, + argp->name, argp->len); fh_unlock(&resp->fh); - RETURN_STATUS(nfserr); + return rpc_success; } static __be32 @@ -389,7 +388,6 @@ nfsd3_proc_rename(struct svc_rqst *rqstp) { struct nfsd3_renameargs *argp = rqstp->rq_argp; struct nfsd3_renameres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: RENAME(3) %s %.*s ->\n", SVCFH_fmt(&argp->ffh), @@ -402,9 +400,9 @@ nfsd3_proc_rename(struct svc_rqst *rqstp) 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); + resp->status = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen, + &resp->tfh, argp->tname, argp->tlen); + return rpc_success; } static __be32 @@ -412,7 +410,6 @@ nfsd3_proc_link(struct svc_rqst *rqstp) { struct nfsd3_linkargs *argp = rqstp->rq_argp; struct nfsd3_linkres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: LINK(3) %s ->\n", SVCFH_fmt(&argp->ffh)); @@ -423,9 +420,9 @@ nfsd3_proc_link(struct svc_rqst *rqstp) 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); + resp->status = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen, + &resp->fh); + return rpc_success; } /* @@ -436,7 +433,6 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp) { struct nfsd3_readdirargs *argp = rqstp->rq_argp; struct nfsd3_readdirres *resp = rqstp->rq_resp; - __be32 nfserr; int count = 0; struct page **p; caddr_t page_addr = NULL; @@ -456,8 +452,8 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp) 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); + resp->status = nfsd_readdir(rqstp, &resp->fh, (loff_t *)&argp->cookie, + &resp->common, nfs3svc_encode_entry); memcpy(resp->verf, argp->verf, 8); count = 0; for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) { @@ -485,7 +481,7 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp) resp->offset = NULL; } - RETURN_STATUS(nfserr); + return rpc_success; } /* @@ -497,7 +493,6 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp) { struct nfsd3_readdirargs *argp = rqstp->rq_argp; struct nfsd3_readdirres *resp = rqstp->rq_resp; - __be32 nfserr; int count = 0; loff_t offset; struct page **p; @@ -520,17 +515,17 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp) resp->rqstp = rqstp; offset = argp->cookie; - nfserr = fh_verify(rqstp, &resp->fh, S_IFDIR, NFSD_MAY_NOP); - if (nfserr) - RETURN_STATUS(nfserr); + resp->status = fh_verify(rqstp, &resp->fh, S_IFDIR, NFSD_MAY_NOP); + if (resp->status != nfs_ok) + goto out; - if (resp->fh.fh_export->ex_flags & NFSEXP_NOREADDIRPLUS) - RETURN_STATUS(nfserr_notsupp); + if (resp->fh.fh_export->ex_flags & NFSEXP_NOREADDIRPLUS) { + resp->status = nfserr_notsupp; + goto out; + } - nfserr = nfsd_readdir(rqstp, &resp->fh, - &offset, - &resp->common, - nfs3svc_encode_entry_plus); + resp->status = 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); @@ -555,7 +550,8 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp) resp->offset = NULL; } - RETURN_STATUS(nfserr); +out: + return rpc_success; } /* @@ -566,14 +562,13 @@ nfsd3_proc_fsstat(struct svc_rqst *rqstp) { struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd3_fsstatres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: FSSTAT(3) %s\n", SVCFH_fmt(&argp->fh)); - nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0); + resp->status = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0); fh_put(&argp->fh); - RETURN_STATUS(nfserr); + return rpc_success; } /* @@ -584,7 +579,6 @@ nfsd3_proc_fsinfo(struct svc_rqst *rqstp) { struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd3_fsinfores *resp = rqstp->rq_resp; - __be32 nfserr; u32 max_blocksize = svc_max_payload(rqstp); dprintk("nfsd: FSINFO(3) %s\n", @@ -600,13 +594,13 @@ nfsd3_proc_fsinfo(struct svc_rqst *rqstp) 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); + resp->status = 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) { + if (resp->status == nfs_ok) { struct super_block *sb = argp->fh.fh_dentry->d_sb; /* Note that we don't care for remote fs's here */ @@ -617,7 +611,7 @@ nfsd3_proc_fsinfo(struct svc_rqst *rqstp) } fh_put(&argp->fh); - RETURN_STATUS(nfserr); + return rpc_success; } /* @@ -628,7 +622,6 @@ nfsd3_proc_pathconf(struct svc_rqst *rqstp) { struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd3_pathconfres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: PATHCONF(3) %s\n", SVCFH_fmt(&argp->fh)); @@ -641,9 +634,9 @@ nfsd3_proc_pathconf(struct svc_rqst *rqstp) resp->p_case_insensitive = 0; resp->p_case_preserving = 1; - nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); + resp->status = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); - if (nfserr == 0) { + if (resp->status == nfs_ok) { struct super_block *sb = argp->fh.fh_dentry->d_sb; /* Note that we don't care for remote fs's here */ @@ -660,10 +653,9 @@ nfsd3_proc_pathconf(struct svc_rqst *rqstp) } fh_put(&argp->fh); - RETURN_STATUS(nfserr); + return rpc_success; } - /* * Commit a file (range) to stable storage. */ @@ -672,21 +664,22 @@ nfsd3_proc_commit(struct svc_rqst *rqstp) { struct nfsd3_commitargs *argp = rqstp->rq_argp; struct nfsd3_commitres *resp = rqstp->rq_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); + if (argp->offset > NFS_OFFSET_MAX) { + resp->status = nfserr_inval; + goto out; + } fh_copy(&resp->fh, &argp->fh); - nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count, - resp->verf); - - RETURN_STATUS(nfserr); + resp->status = nfsd_commit(rqstp, &resp->fh, argp->offset, + argp->count, resp->verf); +out: + return rpc_success; } @@ -716,6 +709,7 @@ struct nfsd3_voidargs { int dummy; }; static const struct svc_procedure nfsd_procedures3[22] = { [NFS3PROC_NULL] = { .pc_func = nfsd3_proc_null, + .pc_decode = nfs3svc_decode_voidarg, .pc_encode = nfs3svc_encode_voidres, .pc_argsize = sizeof(struct nfsd3_voidargs), .pc_ressize = sizeof(struct nfsd3_voidres), diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index aae514d40b64..9c23b6acf234 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -305,6 +305,12 @@ void fill_post_wcc(struct svc_fh *fhp) * XDR decode functions */ int +nfs3svc_decode_voidarg(struct svc_rqst *rqstp, __be32 *p) +{ + return 1; +} + +int nfs3svc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p) { struct nfsd_fhandle *args = rqstp->rq_argp; @@ -635,10 +641,7 @@ nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p) /* * XDR encode functions */ -/* - * There must be an encoding function for void results so svc_process - * will work properly. - */ + int nfs3svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p) { @@ -651,6 +654,7 @@ nfs3svc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_attrstat *resp = rqstp->rq_resp; + *p++ = resp->status; if (resp->status == 0) { lease_get_mtime(d_inode(resp->fh.fh_dentry), &resp->stat.mtime); @@ -665,6 +669,7 @@ nfs3svc_encode_wccstat(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_attrstat *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_wcc_data(rqstp, p, &resp->fh); return xdr_ressize_check(rqstp, p); } @@ -675,6 +680,7 @@ nfs3svc_encode_diropres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_diropres *resp = rqstp->rq_resp; + *p++ = resp->status; if (resp->status == 0) { p = encode_fh(p, &resp->fh); p = encode_post_op_attr(rqstp, p, &resp->fh); @@ -689,6 +695,7 @@ nfs3svc_encode_accessres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_accessres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0) *p++ = htonl(resp->access); @@ -701,6 +708,7 @@ nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_readlinkres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0) { *p++ = htonl(resp->len); @@ -723,6 +731,7 @@ nfs3svc_encode_readres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_readres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0) { *p++ = htonl(resp->count); @@ -748,6 +757,7 @@ nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_writeres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_wcc_data(rqstp, p, &resp->fh); if (resp->status == 0) { *p++ = htonl(resp->count); @@ -764,6 +774,7 @@ nfs3svc_encode_createres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_diropres *resp = rqstp->rq_resp; + *p++ = resp->status; if (resp->status == 0) { *p++ = xdr_one; p = encode_fh(p, &resp->fh); @@ -779,6 +790,7 @@ nfs3svc_encode_renameres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_renameres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_wcc_data(rqstp, p, &resp->ffh); p = encode_wcc_data(rqstp, p, &resp->tfh); return xdr_ressize_check(rqstp, p); @@ -790,6 +802,7 @@ nfs3svc_encode_linkres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_linkres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_post_op_attr(rqstp, p, &resp->fh); p = encode_wcc_data(rqstp, p, &resp->tfh); return xdr_ressize_check(rqstp, p); @@ -801,6 +814,7 @@ nfs3svc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_readdirres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0) { @@ -1053,6 +1067,7 @@ nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, __be32 *p) struct kstatfs *s = &resp->stats; u64 bs = s->f_bsize; + *p++ = resp->status; *p++ = xdr_zero; /* no post_op_attr */ if (resp->status == 0) { @@ -1073,6 +1088,7 @@ nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_fsinfores *resp = rqstp->rq_resp; + *p++ = resp->status; *p++ = xdr_zero; /* no post_op_attr */ if (resp->status == 0) { @@ -1118,6 +1134,7 @@ nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd3_commitres *resp = rqstp->rq_resp; + *p++ = resp->status; p = encode_wcc_data(rqstp, p, &resp->fh); /* Write verifier */ if (resp->status == 0) { diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index eaf50eafa935..ad2fa1a8e7ad 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -38,6 +38,7 @@ #include <linux/slab.h> #include <linux/kthread.h> #include <linux/sunrpc/addr.h> +#include <linux/nfs_ssc.h> #include "idmap.h" #include "cache.h" @@ -1247,7 +1248,7 @@ out_err: static void nfsd4_interssc_disconnect(struct vfsmount *ss_mnt) { - nfs_sb_deactive(ss_mnt->mnt_sb); + nfs_do_sb_deactive(ss_mnt->mnt_sb); mntput(ss_mnt); } @@ -2165,7 +2166,7 @@ nfsd4_removexattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, static __be32 nfsd4_proc_null(struct svc_rqst *rqstp) { - return nfs_ok; + return rpc_success; } static inline void nfsd4_increment_op_stats(u32 opnum) @@ -2457,15 +2458,14 @@ encode_op: nfsd4_increment_op_stats(op->opnum); } - cstate->status = status; fh_put(current_fh); fh_put(save_fh); BUG_ON(cstate->replay_owner); out: + cstate->status = status; /* Reset deferral mechanism for RPC deferrals */ set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags); - dprintk("nfsv4 compound returned %d\n", ntohl(status)); - return status; + return rpc_success; } #define op_encode_hdr_size (2) @@ -2591,6 +2591,20 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) return (op_encode_hdr_size + 2 + XDR_QUADLEN(rlen)) * sizeof(__be32); } +static inline u32 nfsd4_read_plus_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) +{ + u32 maxcount = svc_max_payload(rqstp); + u32 rlen = min(op->u.read.rd_length, maxcount); + /* + * If we detect that the file changed during hole encoding, then we + * recover by encoding the remaining reply as data. This means we need + * to set aside enough room to encode two data segments. + */ + u32 seg_len = 2 * (1 + 2 + 1); + + return (op_encode_hdr_size + 2 + seg_len + XDR_QUADLEN(rlen)) * sizeof(__be32); +} + static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) { u32 maxcount = 0, rlen = 0; @@ -3163,6 +3177,13 @@ static const struct nfsd4_operation nfsd4_ops[] = { .op_name = "OP_COPY", .op_rsize_bop = nfsd4_copy_rsize, }, + [OP_READ_PLUS] = { + .op_func = nfsd4_read, + .op_release = nfsd4_read_release, + .op_name = "OP_READ_PLUS", + .op_rsize_bop = nfsd4_read_plus_rsize, + .op_get_currentstateid = nfsd4_get_readstateid, + }, [OP_SEEK] = { .op_func = nfsd4_seek, .op_name = "OP_SEEK", @@ -3231,7 +3252,7 @@ bool nfsd4_spo_must_allow(struct svc_rqst *rqstp) if (!cstate->minorversion) return false; - if (cstate->spo_must_allowed == true) + if (cstate->spo_must_allowed) return true; opiter = resp->opcnt; @@ -3279,6 +3300,7 @@ struct nfsd4_voidargs { int dummy; }; static const struct svc_procedure nfsd_procedures4[2] = { [NFSPROC4_NULL] = { .pc_func = nfsd4_proc_null, + .pc_decode = nfs4svc_decode_voidarg, .pc_encode = nfs4svc_encode_voidres, .pc_argsize = sizeof(struct nfsd4_voidargs), .pc_ressize = sizeof(struct nfsd4_voidres), diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c09a2a4281ec..d7f27ed6b794 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4513,7 +4513,8 @@ static int nfsd4_cb_recall_done(struct nfsd4_callback *cb, { struct nfs4_delegation *dp = cb_to_delegation(cb); - if (dp->dl_stid.sc_type == NFS4_CLOSED_DELEG_STID) + if (dp->dl_stid.sc_type == NFS4_CLOSED_DELEG_STID || + dp->dl_stid.sc_type == NFS4_REVOKED_DELEG_STID) return 1; switch (task->tk_status) { @@ -4597,7 +4598,8 @@ static bool nfsd_breaker_owns_lease(struct file_lock *fl) if (!i_am_nfsd()) return NULL; rqst = kthread_data(current); - if (!rqst->rq_lease_breaker) + /* Note rq_prog == NFS_ACL_PROGRAM is also possible: */ + if (rqst->rq_prog != NFS_PROGRAM || rqst->rq_vers < 4) return NULL; clp = *(rqst->rq_lease_breaker); return dl->dl_stid.sc_client == clp; @@ -4954,7 +4956,6 @@ static int nfsd4_check_conflicting_opens(struct nfs4_client *clp, writes--; if (fp->fi_fds[O_RDWR]) writes--; - WARN_ON_ONCE(writes < 0); if (writes > 0) return -EAGAIN; spin_lock(&fp->fi_lock); @@ -5126,7 +5127,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, memcpy(&open->op_delegate_stateid, &dp->dl_stid.sc_stateid, sizeof(dp->dl_stid.sc_stateid)); - trace_nfsd_deleg_open(&dp->dl_stid.sc_stateid); + trace_nfsd_deleg_read(&dp->dl_stid.sc_stateid); open->op_delegate_type = NFS4_OPEN_DELEGATE_READ; nfs4_put_stid(&dp->dl_stid); return; @@ -5243,7 +5244,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf nfs4_open_delegation(current_fh, open, stp); nodeleg: status = nfs_ok; - trace_nfsd_deleg_none(&stp->st_stid.sc_stateid); + trace_nfsd_open(&stp->st_stid.sc_stateid); out: /* 4.1 client trying to upgrade/downgrade delegation? */ if (open->op_delegate_type == NFS4_OPEN_DELEGATE_NONE && dp && @@ -5722,7 +5723,6 @@ nfs4_find_file(struct nfs4_stid *s, int flags) return find_readable_file(s->sc_file); else return find_writeable_file(s->sc_file); - break; } return NULL; @@ -7253,599 +7253,6 @@ nfs4_check_open_reclaim(clientid_t *clid, return nfs_ok; } -#ifdef CONFIG_NFSD_FAULT_INJECTION -static inline void -put_client(struct nfs4_client *clp) -{ - atomic_dec(&clp->cl_rpc_users); -} - -static struct nfs4_client * -nfsd_find_client(struct sockaddr_storage *addr, size_t addr_size) -{ - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - - if (!nfsd_netns_ready(nn)) - return NULL; - - list_for_each_entry(clp, &nn->client_lru, cl_lru) { - if (memcmp(&clp->cl_addr, addr, addr_size) == 0) - return clp; - } - return NULL; -} - -u64 -nfsd_inject_print_clients(void) -{ - struct nfs4_client *clp; - u64 count = 0; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - char buf[INET6_ADDRSTRLEN]; - - if (!nfsd_netns_ready(nn)) - return 0; - - spin_lock(&nn->client_lock); - list_for_each_entry(clp, &nn->client_lru, cl_lru) { - rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); - pr_info("NFS Client: %s\n", buf); - ++count; - } - spin_unlock(&nn->client_lock); - - return count; -} - -u64 -nfsd_inject_forget_client(struct sockaddr_storage *addr, size_t addr_size) -{ - u64 count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - clp = nfsd_find_client(addr, addr_size); - if (clp) { - if (mark_client_expired_locked(clp) == nfs_ok) - ++count; - else - clp = NULL; - } - spin_unlock(&nn->client_lock); - - if (clp) - expire_client(clp); - - return count; -} - -u64 -nfsd_inject_forget_clients(u64 max) -{ - u64 count = 0; - struct nfs4_client *clp, *next; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { - if (mark_client_expired_locked(clp) == nfs_ok) { - list_add(&clp->cl_lru, &reaplist); - if (max != 0 && ++count >= max) - break; - } - } - spin_unlock(&nn->client_lock); - - list_for_each_entry_safe(clp, next, &reaplist, cl_lru) - expire_client(clp); - - return count; -} - -static void nfsd_print_count(struct nfs4_client *clp, unsigned int count, - const char *type) -{ - char buf[INET6_ADDRSTRLEN]; - rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); - printk(KERN_INFO "NFS Client: %s has %u %s\n", buf, count, type); -} - -static void -nfsd_inject_add_lock_to_list(struct nfs4_ol_stateid *lst, - struct list_head *collect) -{ - struct nfs4_client *clp = lst->st_stid.sc_client; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - - if (!collect) - return; - - lockdep_assert_held(&nn->client_lock); - atomic_inc(&clp->cl_rpc_users); - list_add(&lst->st_locks, collect); -} - -static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, - struct list_head *collect, - bool (*func)(struct nfs4_ol_stateid *)) -{ - struct nfs4_openowner *oop; - struct nfs4_ol_stateid *stp, *st_next; - struct nfs4_ol_stateid *lst, *lst_next; - u64 count = 0; - - spin_lock(&clp->cl_lock); - list_for_each_entry(oop, &clp->cl_openowners, oo_perclient) { - list_for_each_entry_safe(stp, st_next, - &oop->oo_owner.so_stateids, st_perstateowner) { - list_for_each_entry_safe(lst, lst_next, - &stp->st_locks, st_locks) { - if (func) { - if (func(lst)) - nfsd_inject_add_lock_to_list(lst, - collect); - } - ++count; - /* - * Despite the fact that these functions deal - * with 64-bit integers for "count", we must - * ensure that it doesn't blow up the - * clp->cl_rpc_users. Throw a warning if we - * start to approach INT_MAX here. - */ - WARN_ON_ONCE(count == (INT_MAX / 2)); - if (count == max) - goto out; - } - } - } -out: - spin_unlock(&clp->cl_lock); - - return count; -} - -static u64 -nfsd_collect_client_locks(struct nfs4_client *clp, struct list_head *collect, - u64 max) -{ - return nfsd_foreach_client_lock(clp, max, collect, unhash_lock_stateid); -} - -static u64 -nfsd_print_client_locks(struct nfs4_client *clp) -{ - u64 count = nfsd_foreach_client_lock(clp, 0, NULL, NULL); - nfsd_print_count(clp, count, "locked files"); - return count; -} - -u64 -nfsd_inject_print_locks(void) -{ - struct nfs4_client *clp; - u64 count = 0; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - - if (!nfsd_netns_ready(nn)) - return 0; - - spin_lock(&nn->client_lock); - list_for_each_entry(clp, &nn->client_lru, cl_lru) - count += nfsd_print_client_locks(clp); - spin_unlock(&nn->client_lock); - - return count; -} - -static void -nfsd_reap_locks(struct list_head *reaplist) -{ - struct nfs4_client *clp; - struct nfs4_ol_stateid *stp, *next; - - list_for_each_entry_safe(stp, next, reaplist, st_locks) { - list_del_init(&stp->st_locks); - clp = stp->st_stid.sc_client; - nfs4_put_stid(&stp->st_stid); - put_client(clp); - } -} - -u64 -nfsd_inject_forget_client_locks(struct sockaddr_storage *addr, size_t addr_size) -{ - unsigned int count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - clp = nfsd_find_client(addr, addr_size); - if (clp) - count = nfsd_collect_client_locks(clp, &reaplist, 0); - spin_unlock(&nn->client_lock); - nfsd_reap_locks(&reaplist); - return count; -} - -u64 -nfsd_inject_forget_locks(u64 max) -{ - u64 count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - list_for_each_entry(clp, &nn->client_lru, cl_lru) { - count += nfsd_collect_client_locks(clp, &reaplist, max - count); - if (max != 0 && count >= max) - break; - } - spin_unlock(&nn->client_lock); - nfsd_reap_locks(&reaplist); - return count; -} - -static u64 -nfsd_foreach_client_openowner(struct nfs4_client *clp, u64 max, - struct list_head *collect, - void (*func)(struct nfs4_openowner *)) -{ - struct nfs4_openowner *oop, *next; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - u64 count = 0; - - lockdep_assert_held(&nn->client_lock); - - spin_lock(&clp->cl_lock); - list_for_each_entry_safe(oop, next, &clp->cl_openowners, oo_perclient) { - if (func) { - func(oop); - if (collect) { - atomic_inc(&clp->cl_rpc_users); - list_add(&oop->oo_perclient, collect); - } - } - ++count; - /* - * Despite the fact that these functions deal with - * 64-bit integers for "count", we must ensure that - * it doesn't blow up the clp->cl_rpc_users. Throw a - * warning if we start to approach INT_MAX here. - */ - WARN_ON_ONCE(count == (INT_MAX / 2)); - if (count == max) - break; - } - spin_unlock(&clp->cl_lock); - - return count; -} - -static u64 -nfsd_print_client_openowners(struct nfs4_client *clp) -{ - u64 count = nfsd_foreach_client_openowner(clp, 0, NULL, NULL); - - nfsd_print_count(clp, count, "openowners"); - return count; -} - -static u64 -nfsd_collect_client_openowners(struct nfs4_client *clp, - struct list_head *collect, u64 max) -{ - return nfsd_foreach_client_openowner(clp, max, collect, - unhash_openowner_locked); -} - -u64 -nfsd_inject_print_openowners(void) -{ - struct nfs4_client *clp; - u64 count = 0; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - - if (!nfsd_netns_ready(nn)) - return 0; - - spin_lock(&nn->client_lock); - list_for_each_entry(clp, &nn->client_lru, cl_lru) - count += nfsd_print_client_openowners(clp); - spin_unlock(&nn->client_lock); - - return count; -} - -static void -nfsd_reap_openowners(struct list_head *reaplist) -{ - struct nfs4_client *clp; - struct nfs4_openowner *oop, *next; - - list_for_each_entry_safe(oop, next, reaplist, oo_perclient) { - list_del_init(&oop->oo_perclient); - clp = oop->oo_owner.so_client; - release_openowner(oop); - put_client(clp); - } -} - -u64 -nfsd_inject_forget_client_openowners(struct sockaddr_storage *addr, - size_t addr_size) -{ - unsigned int count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - clp = nfsd_find_client(addr, addr_size); - if (clp) - count = nfsd_collect_client_openowners(clp, &reaplist, 0); - spin_unlock(&nn->client_lock); - nfsd_reap_openowners(&reaplist); - return count; -} - -u64 -nfsd_inject_forget_openowners(u64 max) -{ - u64 count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - list_for_each_entry(clp, &nn->client_lru, cl_lru) { - count += nfsd_collect_client_openowners(clp, &reaplist, - max - count); - if (max != 0 && count >= max) - break; - } - spin_unlock(&nn->client_lock); - nfsd_reap_openowners(&reaplist); - return count; -} - -static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max, - struct list_head *victims) -{ - struct nfs4_delegation *dp, *next; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - u64 count = 0; - - lockdep_assert_held(&nn->client_lock); - - spin_lock(&state_lock); - list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) { - if (victims) { - /* - * It's not safe to mess with delegations that have a - * non-zero dl_time. They might have already been broken - * and could be processed by the laundromat outside of - * the state_lock. Just leave them be. - */ - if (dp->dl_time != 0) - continue; - - atomic_inc(&clp->cl_rpc_users); - WARN_ON(!unhash_delegation_locked(dp)); - list_add(&dp->dl_recall_lru, victims); - } - ++count; - /* - * Despite the fact that these functions deal with - * 64-bit integers for "count", we must ensure that - * it doesn't blow up the clp->cl_rpc_users. Throw a - * warning if we start to approach INT_MAX here. - */ - WARN_ON_ONCE(count == (INT_MAX / 2)); - if (count == max) - break; - } - spin_unlock(&state_lock); - return count; -} - -static u64 -nfsd_print_client_delegations(struct nfs4_client *clp) -{ - u64 count = nfsd_find_all_delegations(clp, 0, NULL); - - nfsd_print_count(clp, count, "delegations"); - return count; -} - -u64 -nfsd_inject_print_delegations(void) -{ - struct nfs4_client *clp; - u64 count = 0; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - - if (!nfsd_netns_ready(nn)) - return 0; - - spin_lock(&nn->client_lock); - list_for_each_entry(clp, &nn->client_lru, cl_lru) - count += nfsd_print_client_delegations(clp); - spin_unlock(&nn->client_lock); - - return count; -} - -static void -nfsd_forget_delegations(struct list_head *reaplist) -{ - struct nfs4_client *clp; - struct nfs4_delegation *dp, *next; - - list_for_each_entry_safe(dp, next, reaplist, dl_recall_lru) { - list_del_init(&dp->dl_recall_lru); - clp = dp->dl_stid.sc_client; - revoke_delegation(dp); - put_client(clp); - } -} - -u64 -nfsd_inject_forget_client_delegations(struct sockaddr_storage *addr, - size_t addr_size) -{ - u64 count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - clp = nfsd_find_client(addr, addr_size); - if (clp) - count = nfsd_find_all_delegations(clp, 0, &reaplist); - spin_unlock(&nn->client_lock); - - nfsd_forget_delegations(&reaplist); - return count; -} - -u64 -nfsd_inject_forget_delegations(u64 max) -{ - u64 count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - list_for_each_entry(clp, &nn->client_lru, cl_lru) { - count += nfsd_find_all_delegations(clp, max - count, &reaplist); - if (max != 0 && count >= max) - break; - } - spin_unlock(&nn->client_lock); - nfsd_forget_delegations(&reaplist); - return count; -} - -static void -nfsd_recall_delegations(struct list_head *reaplist) -{ - struct nfs4_client *clp; - struct nfs4_delegation *dp, *next; - - list_for_each_entry_safe(dp, next, reaplist, dl_recall_lru) { - list_del_init(&dp->dl_recall_lru); - clp = dp->dl_stid.sc_client; - - trace_nfsd_deleg_recall(&dp->dl_stid.sc_stateid); - - /* - * We skipped all entries that had a zero dl_time before, - * so we can now reset the dl_time back to 0. If a delegation - * break comes in now, then it won't make any difference since - * we're recalling it either way. - */ - spin_lock(&state_lock); - dp->dl_time = 0; - spin_unlock(&state_lock); - nfsd_break_one_deleg(dp); - put_client(clp); - } -} - -u64 -nfsd_inject_recall_client_delegations(struct sockaddr_storage *addr, - size_t addr_size) -{ - u64 count = 0; - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - clp = nfsd_find_client(addr, addr_size); - if (clp) - count = nfsd_find_all_delegations(clp, 0, &reaplist); - spin_unlock(&nn->client_lock); - - nfsd_recall_delegations(&reaplist); - return count; -} - -u64 -nfsd_inject_recall_delegations(u64 max) -{ - u64 count = 0; - struct nfs4_client *clp, *next; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, - nfsd_net_id); - LIST_HEAD(reaplist); - - if (!nfsd_netns_ready(nn)) - return count; - - spin_lock(&nn->client_lock); - list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { - count += nfsd_find_all_delegations(clp, max - count, &reaplist); - if (max != 0 && ++count >= max) - break; - } - spin_unlock(&nn->client_lock); - nfsd_recall_delegations(&reaplist); - return count; -} -#endif /* CONFIG_NFSD_FAULT_INJECTION */ - /* * Since the lifetime of a delegation isn't limited to that of an open, a * client may quite reasonably hang on to a delegation as long as it has diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 259d5ad0e3f4..833a2c64dfe8 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1855,7 +1855,7 @@ static __be32 nfsd4_decode_copy_notify(struct nfsd4_compoundargs *argp, struct nfsd4_copy_notify *cn) { - int status; + __be32 status; status = nfsd4_decode_stateid(argp, &cn->cpn_src_stateid); if (status) @@ -2173,7 +2173,7 @@ static const nfsd4_dec nfsd4_dec_ops[] = { [OP_LAYOUTSTATS] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_OFFLOAD_CANCEL] = (nfsd4_dec)nfsd4_decode_offload_status, [OP_OFFLOAD_STATUS] = (nfsd4_dec)nfsd4_decode_offload_status, - [OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_notsupp, + [OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_read, [OP_SEEK] = (nfsd4_dec)nfsd4_decode_seek, [OP_WRITE_SAME] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_CLONE] = (nfsd4_dec)nfsd4_decode_clone, @@ -2261,7 +2261,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) */ cachethis |= nfsd4_cache_this_op(op); - if (op->opnum == OP_READ) { + if (op->opnum == OP_READ || op->opnum == OP_READ_PLUS) { readcount++; readbytes += nfsd4_max_reply(argp->rqstp, op); } else @@ -3814,36 +3814,14 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, { struct xdr_stream *xdr = &resp->xdr; u32 eof; - int v; int starting_len = xdr->buf->len - 8; - long len; - int thislen; __be32 nfserr; __be32 tmp; - __be32 *p; int pad; - /* - * svcrdma requires every READ payload to start somewhere - * in xdr->pages. - */ - if (xdr->iov == xdr->buf->head) { - xdr->iov = NULL; - xdr->end = xdr->p; - } - - len = maxcount; - v = 0; - while (len) { - thislen = min_t(long, len, PAGE_SIZE); - p = xdr_reserve_space(xdr, thislen); - WARN_ON_ONCE(!p); - resp->rqstp->rq_vec[v].iov_base = p; - resp->rqstp->rq_vec[v].iov_len = thislen; - v++; - len -= thislen; - } - read->rd_vlen = v; + read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, maxcount); + if (read->rd_vlen < 0) + return nfserr_resource; nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen, &maxcount, @@ -4619,6 +4597,149 @@ nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr, return nfserr_resource; p = xdr_encode_hyper(p, os->count); *p++ = cpu_to_be32(0); + return nfserr; +} + +static __be32 +nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, + struct nfsd4_read *read, + unsigned long *maxcount, u32 *eof, + loff_t *pos) +{ + struct xdr_stream *xdr = &resp->xdr; + struct file *file = read->rd_nf->nf_file; + int starting_len = xdr->buf->len; + loff_t hole_pos; + __be32 nfserr; + __be32 *p, tmp; + __be64 tmp64; + + hole_pos = pos ? *pos : vfs_llseek(file, read->rd_offset, SEEK_HOLE); + if (hole_pos > read->rd_offset) + *maxcount = min_t(unsigned long, *maxcount, hole_pos - read->rd_offset); + *maxcount = min_t(unsigned long, *maxcount, (xdr->buf->buflen - xdr->buf->len)); + + /* Content type, offset, byte count */ + p = xdr_reserve_space(xdr, 4 + 8 + 4); + if (!p) + return nfserr_resource; + + read->rd_vlen = xdr_reserve_space_vec(xdr, resp->rqstp->rq_vec, *maxcount); + if (read->rd_vlen < 0) + return nfserr_resource; + + nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset, + resp->rqstp->rq_vec, read->rd_vlen, maxcount, eof); + if (nfserr) + return nfserr; + + tmp = htonl(NFS4_CONTENT_DATA); + write_bytes_to_xdr_buf(xdr->buf, starting_len, &tmp, 4); + tmp64 = cpu_to_be64(read->rd_offset); + write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp64, 8); + tmp = htonl(*maxcount); + write_bytes_to_xdr_buf(xdr->buf, starting_len + 12, &tmp, 4); + return nfs_ok; +} + +static __be32 +nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp, + struct nfsd4_read *read, + unsigned long *maxcount, u32 *eof) +{ + struct file *file = read->rd_nf->nf_file; + loff_t data_pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); + loff_t f_size = i_size_read(file_inode(file)); + unsigned long count; + __be32 *p; + + if (data_pos == -ENXIO) + data_pos = f_size; + else if (data_pos <= read->rd_offset || (data_pos < f_size && data_pos % PAGE_SIZE)) + return nfsd4_encode_read_plus_data(resp, read, maxcount, eof, &f_size); + count = data_pos - read->rd_offset; + + /* Content type, offset, byte count */ + p = xdr_reserve_space(&resp->xdr, 4 + 8 + 8); + if (!p) + return nfserr_resource; + + *p++ = htonl(NFS4_CONTENT_HOLE); + p = xdr_encode_hyper(p, read->rd_offset); + p = xdr_encode_hyper(p, count); + + *eof = (read->rd_offset + count) >= f_size; + *maxcount = min_t(unsigned long, count, *maxcount); + return nfs_ok; +} + +static __be32 +nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, + struct nfsd4_read *read) +{ + unsigned long maxcount, count; + struct xdr_stream *xdr = &resp->xdr; + struct file *file; + int starting_len = xdr->buf->len; + int last_segment = xdr->buf->len; + int segments = 0; + __be32 *p, tmp; + bool is_data; + loff_t pos; + u32 eof; + + if (nfserr) + return nfserr; + file = read->rd_nf->nf_file; + + /* eof flag, segment count */ + p = xdr_reserve_space(xdr, 4 + 4); + if (!p) + return nfserr_resource; + xdr_commit_encode(xdr); + + maxcount = svc_max_payload(resp->rqstp); + maxcount = min_t(unsigned long, maxcount, + (xdr->buf->buflen - xdr->buf->len)); + maxcount = min_t(unsigned long, maxcount, read->rd_length); + count = maxcount; + + eof = read->rd_offset >= i_size_read(file_inode(file)); + if (eof) + goto out; + + pos = vfs_llseek(file, read->rd_offset, SEEK_HOLE); + is_data = pos > read->rd_offset; + + while (count > 0 && !eof) { + maxcount = count; + if (is_data) + nfserr = nfsd4_encode_read_plus_data(resp, read, &maxcount, &eof, + segments == 0 ? &pos : NULL); + else + nfserr = nfsd4_encode_read_plus_hole(resp, read, &maxcount, &eof); + if (nfserr) + goto out; + count -= maxcount; + read->rd_offset += maxcount; + is_data = !is_data; + last_segment = xdr->buf->len; + segments++; + } + +out: + if (nfserr && segments == 0) + xdr_truncate_encode(xdr, starting_len); + else { + tmp = htonl(eof); + write_bytes_to_xdr_buf(xdr->buf, starting_len, &tmp, 4); + tmp = htonl(segments); + write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4); + if (nfserr) { + xdr_truncate_encode(xdr, last_segment); + nfserr = nfs_ok; + } + } return nfserr; } @@ -4679,7 +4800,7 @@ nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p) /* * Encode kmalloc-ed buffer in to XDR stream. */ -static int +static __be32 nfsd4_vbuf_to_stream(struct xdr_stream *xdr, char *buf, u32 buflen) { u32 cplen; @@ -4795,7 +4916,7 @@ nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr, u32 xdrlen, offset; u64 cookie; char *sp; - __be32 status; + __be32 status, tmp; __be32 *p; u32 nuser; @@ -4828,7 +4949,7 @@ nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr, slen = strlen(sp); /* - * Check if this a user. attribute, skip it if not. + * Check if this is a "user." attribute, skip it if not. */ if (strncmp(sp, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) goto contloop; @@ -4859,7 +4980,7 @@ nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr, goto out; } - p = xdr_encode_opaque(p, sp, slen); + xdr_encode_opaque(p, sp, slen); xdrleft -= xdrlen; count++; @@ -4888,8 +5009,8 @@ wreof: cookie = offset + count; write_bytes_to_xdr_buf(xdr->buf, cookie_offset, &cookie, 8); - count = htonl(count); - write_bytes_to_xdr_buf(xdr->buf, count_offset, &count, 4); + tmp = cpu_to_be32(count); + write_bytes_to_xdr_buf(xdr->buf, count_offset, &tmp, 4); out: if (listxattrs->lsxa_len) kvfree(listxattrs->lsxa_buf); @@ -4996,7 +5117,7 @@ static const nfsd4_enc nfsd4_enc_ops[] = { [OP_LAYOUTSTATS] = (nfsd4_enc)nfsd4_encode_noop, [OP_OFFLOAD_CANCEL] = (nfsd4_enc)nfsd4_encode_noop, [OP_OFFLOAD_STATUS] = (nfsd4_enc)nfsd4_encode_offload_status, - [OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_noop, + [OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_read_plus, [OP_SEEK] = (nfsd4_enc)nfsd4_encode_seek, [OP_WRITE_SAME] = (nfsd4_enc)nfsd4_encode_noop, [OP_CLONE] = (nfsd4_enc)nfsd4_encode_noop, @@ -5157,6 +5278,12 @@ void nfsd4_release_compoundargs(struct svc_rqst *rqstp) } int +nfs4svc_decode_voidarg(struct svc_rqst *rqstp, __be32 *p) +{ + return 1; +} + +int nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p) { struct nfsd4_compoundargs *args = rqstp->rq_argp; @@ -5183,15 +5310,14 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p) int nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p) { - /* - * All that remains is to write the tag and operation count... - */ struct nfsd4_compoundres *resp = rqstp->rq_resp; struct xdr_buf *buf = resp->xdr.buf; WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len); + *p = resp->cstate.status; + rqstp->rq_next_page = resp->xdr.page_ptr + 1; p = resp->tagp; diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index 0a0cf1fd77d3..80c90fc231a5 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -172,14 +172,10 @@ int nfsd_reply_cache_init(struct nfsd_net *nn) if (status) goto out_nomem; - nn->drc_hashtbl = kcalloc(hashsize, - sizeof(*nn->drc_hashtbl), GFP_KERNEL); - if (!nn->drc_hashtbl) { - nn->drc_hashtbl = vzalloc(array_size(hashsize, - sizeof(*nn->drc_hashtbl))); - if (!nn->drc_hashtbl) - goto out_shrinker; - } + nn->drc_hashtbl = kvzalloc(array_size(hashsize, + sizeof(*nn->drc_hashtbl)), GFP_KERNEL); + if (!nn->drc_hashtbl) + goto out_shrinker; for (i = 0; i < hashsize; i++) { INIT_LIST_HEAD(&nn->drc_hashtbl[i].lru_head); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 7ae236113040..f6d5d783f4a4 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1534,7 +1534,6 @@ static int __init init_nfsd(void) retval = nfsd4_init_pnfs(); if (retval) goto out_free_slabs; - nfsd_fault_inject_init(); /* nfsd fault injection controls */ nfsd_stat_init(); /* Statistics */ retval = nfsd_drc_slab_create(); if (retval) @@ -1555,7 +1554,6 @@ out_free_lockd: nfsd_drc_slab_free(); out_free_stat: nfsd_stat_shutdown(); - nfsd_fault_inject_cleanup(); nfsd4_exit_pnfs(); out_free_slabs: nfsd4_free_slabs(); @@ -1575,7 +1573,6 @@ static void __exit exit_nfsd(void) nfsd_lockd_shutdown(); nfsd4_free_slabs(); nfsd4_exit_pnfs(); - nfsd_fault_inject_cleanup(); unregister_filesystem(&nfsd_fs_type); unregister_cld_notifier(); unregister_pernet_subsys(&nfsd_net_ops); diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 6e0b066480c5..0d71549f9d42 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -11,30 +11,14 @@ #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) { - return nfs_ok; + return rpc_success; } -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 @@ -44,13 +28,17 @@ nfsd_proc_getattr(struct svc_rqst *rqstp) { struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd_attrstat *resp = rqstp->rq_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); + resp->status = fh_verify(rqstp, &resp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); + if (resp->status != nfs_ok) + goto out; + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; } /* @@ -64,7 +52,6 @@ nfsd_proc_setattr(struct svc_rqst *rqstp) struct nfsd_attrstat *resp = rqstp->rq_resp; struct iattr *iap = &argp->attrs; struct svc_fh *fhp; - __be32 nfserr; dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n", SVCFH_fmt(&argp->fh), @@ -96,9 +83,9 @@ nfsd_proc_setattr(struct svc_rqst *rqstp) */ time64_t delta = iap->ia_atime.tv_sec - ktime_get_real_seconds(); - nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); - if (nfserr) - goto done; + resp->status = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); + if (resp->status != nfs_ok) + goto out; if (delta < 0) delta = -delta; @@ -113,9 +100,20 @@ nfsd_proc_setattr(struct svc_rqst *rqstp) } } - nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time64_t)0); -done: - return nfsd_return_attrs(nfserr, resp); + resp->status = nfsd_setattr(rqstp, fhp, iap, 0, (time64_t)0); + if (resp->status != nfs_ok) + goto out; + + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; +} + +/* Obsolete, replaced by MNTPROC_MNT. */ +static __be32 +nfsd_proc_root(struct svc_rqst *rqstp) +{ + return rpc_success; } /* @@ -129,17 +127,20 @@ nfsd_proc_lookup(struct svc_rqst *rqstp) { struct nfsd_diropargs *argp = rqstp->rq_argp; struct nfsd_diropres *resp = rqstp->rq_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); - + resp->status = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len, + &resp->fh); fh_put(&argp->fh); - return nfsd_return_dirop(nfserr, resp); + if (resp->status != nfs_ok) + goto out; + + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; } /* @@ -150,16 +151,15 @@ nfsd_proc_readlink(struct svc_rqst *rqstp) { struct nfsd_readlinkargs *argp = rqstp->rq_argp; struct nfsd_readlinkres *resp = rqstp->rq_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); + resp->status = nfsd_readlink(rqstp, &argp->fh, argp->buffer, &resp->len); fh_put(&argp->fh); - return nfserr; + return rpc_success; } /* @@ -171,7 +171,6 @@ nfsd_proc_read(struct svc_rqst *rqstp) { struct nfsd_readargs *argp = rqstp->rq_argp; struct nfsd_readres *resp = rqstp->rq_resp; - __be32 nfserr; u32 eof; dprintk("nfsd: READ %s %d bytes at %d\n", @@ -193,14 +192,23 @@ nfsd_proc_read(struct svc_rqst *rqstp) 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, - &eof); - - if (nfserr) return nfserr; - return fh_getattr(&resp->fh, &resp->stat); + resp->status = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), + argp->offset, + rqstp->rq_vec, argp->vlen, + &resp->count, + &eof); + if (resp->status == nfs_ok) + resp->status = fh_getattr(&resp->fh, &resp->stat); + else if (resp->status == nfserr_jukebox) + return rpc_drop_reply; + return rpc_success; +} + +/* Reserved */ +static __be32 +nfsd_proc_writecache(struct svc_rqst *rqstp) +{ + return rpc_success; } /* @@ -212,7 +220,6 @@ nfsd_proc_write(struct svc_rqst *rqstp) { struct nfsd_writeargs *argp = rqstp->rq_argp; struct nfsd_attrstat *resp = rqstp->rq_resp; - __be32 nfserr; unsigned long cnt = argp->len; unsigned int nvecs; @@ -222,12 +229,20 @@ nfsd_proc_write(struct svc_rqst *rqstp) nvecs = svc_fill_write_vector(rqstp, rqstp->rq_arg.pages, &argp->first, cnt); - if (!nvecs) - return nfserr_io; - nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), - argp->offset, rqstp->rq_vec, nvecs, - &cnt, NFS_DATA_SYNC, NULL); - return nfsd_return_attrs(nfserr, resp); + if (!nvecs) { + resp->status = nfserr_io; + goto out; + } + + resp->status = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), + argp->offset, rqstp->rq_vec, nvecs, + &cnt, NFS_DATA_SYNC, NULL); + if (resp->status == nfs_ok) + resp->status = fh_getattr(&resp->fh, &resp->stat); + else if (resp->status == nfserr_jukebox) + return rpc_drop_reply; +out: + return rpc_success; } /* @@ -247,7 +262,6 @@ nfsd_proc_create(struct svc_rqst *rqstp) struct inode *inode; struct dentry *dchild; int type, mode; - __be32 nfserr; int hosterr; dev_t rdev = 0, wanted = new_decode_dev(attr->ia_size); @@ -255,40 +269,40 @@ nfsd_proc_create(struct svc_rqst *rqstp) 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) + resp->status = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC); + if (resp->status != nfs_ok) goto done; /* must fh_put dirfhp even on error */ /* Check for NFSD_MAY_WRITE in nfsd_create if necessary */ - nfserr = nfserr_exist; + resp->status = nfserr_exist; if (isdotent(argp->name, argp->len)) goto done; hosterr = fh_want_write(dirfhp); if (hosterr) { - nfserr = nfserrno(hosterr); + resp->status = 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)); + resp->status = nfserrno(PTR_ERR(dchild)); goto out_unlock; } fh_init(newfhp, NFS_FHSIZE); - nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp); - if (!nfserr && d_really_is_negative(dchild)) - nfserr = nfserr_noent; + resp->status = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp); + if (!resp->status && d_really_is_negative(dchild)) + resp->status = nfserr_noent; dput(dchild); - if (nfserr) { - if (nfserr != nfserr_noent) + if (resp->status) { + if (resp->status != 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; + resp->status = nfserr_acces; if (!newfhp->fh_dentry) { printk(KERN_WARNING "nfsd_proc_create: file handle not verified\n"); @@ -321,11 +335,11 @@ nfsd_proc_create(struct svc_rqst *rqstp) * echo thing > device-special-file-or-pipe * by doing a CREATE with type==0 */ - nfserr = nfsd_permission(rqstp, + resp->status = nfsd_permission(rqstp, newfhp->fh_export, newfhp->fh_dentry, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS); - if (nfserr && nfserr != nfserr_rofs) + if (resp->status && resp->status != nfserr_rofs) goto out_unlock; } } else @@ -361,16 +375,17 @@ nfsd_proc_create(struct svc_rqst *rqstp) attr->ia_valid &= ~ATTR_SIZE; /* Make sure the type and device matches */ - nfserr = nfserr_exist; + resp->status = nfserr_exist; if (inode && type != (inode->i_mode & S_IFMT)) goto out_unlock; } - nfserr = 0; + resp->status = nfs_ok; if (!inode) { /* File doesn't exist. Create it and set attrs */ - nfserr = nfsd_create_locked(rqstp, dirfhp, argp->name, - argp->len, attr, type, rdev, newfhp); + resp->status = nfsd_create_locked(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); @@ -380,7 +395,8 @@ nfsd_proc_create(struct svc_rqst *rqstp) */ attr->ia_valid &= ATTR_SIZE; if (attr->ia_valid) - nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time64_t)0); + resp->status = nfsd_setattr(rqstp, newfhp, attr, 0, + (time64_t)0); } out_unlock: @@ -389,47 +405,52 @@ out_unlock: fh_drop_write(dirfhp); done: fh_put(dirfhp); - return nfsd_return_dirop(nfserr, resp); + if (resp->status != nfs_ok) + goto out; + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; } static __be32 nfsd_proc_remove(struct svc_rqst *rqstp) { struct nfsd_diropargs *argp = rqstp->rq_argp; - __be32 nfserr; + struct nfsd_stat *resp = rqstp->rq_resp; 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); + resp->status = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, + argp->name, argp->len); fh_put(&argp->fh); - return nfserr; + return rpc_success; } static __be32 nfsd_proc_rename(struct svc_rqst *rqstp) { struct nfsd_renameargs *argp = rqstp->rq_argp; - __be32 nfserr; + struct nfsd_stat *resp = rqstp->rq_resp; 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); + resp->status = 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; + return rpc_success; } static __be32 nfsd_proc_link(struct svc_rqst *rqstp) { struct nfsd_linkargs *argp = rqstp->rq_argp; - __be32 nfserr; + struct nfsd_stat *resp = rqstp->rq_resp; dprintk("nfsd: LINK %s ->\n", SVCFH_fmt(&argp->ffh)); @@ -438,41 +459,46 @@ nfsd_proc_link(struct svc_rqst *rqstp) argp->tlen, argp->tname); - nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen, - &argp->ffh); + resp->status = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen, + &argp->ffh); fh_put(&argp->ffh); fh_put(&argp->tfh); - return nfserr; + return rpc_success; } static __be32 nfsd_proc_symlink(struct svc_rqst *rqstp) { struct nfsd_symlinkargs *argp = rqstp->rq_argp; + struct nfsd_stat *resp = rqstp->rq_resp; struct svc_fh newfh; - __be32 nfserr; - if (argp->tlen > NFS_MAXPATHLEN) - return nfserr_nametoolong; + if (argp->tlen > NFS_MAXPATHLEN) { + resp->status = nfserr_nametoolong; + goto out; + } argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first, page_address(rqstp->rq_arg.pages[0]), argp->tlen); - if (IS_ERR(argp->tname)) - return nfserrno(PTR_ERR(argp->tname)); + if (IS_ERR(argp->tname)) { + resp->status = nfserrno(PTR_ERR(argp->tname)); + goto out; + } dprintk("nfsd: SYMLINK %s %.*s -> %.*s\n", SVCFH_fmt(&argp->ffh), argp->flen, argp->fname, argp->tlen, argp->tname); fh_init(&newfh, NFS_FHSIZE); - nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, - argp->tname, &newfh); + resp->status = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, + argp->tname, &newfh); kfree(argp->tname); fh_put(&argp->ffh); fh_put(&newfh); - return nfserr; +out: + return rpc_success; } /* @@ -484,7 +510,6 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp) { struct nfsd_createargs *argp = rqstp->rq_argp; struct nfsd_diropres *resp = rqstp->rq_resp; - __be32 nfserr; dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name); @@ -495,10 +520,15 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp) 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); + resp->status = 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); + if (resp->status != nfs_ok) + goto out; + + resp->status = fh_getattr(&resp->fh, &resp->stat); +out: + return rpc_success; } /* @@ -508,13 +538,14 @@ static __be32 nfsd_proc_rmdir(struct svc_rqst *rqstp) { struct nfsd_diropargs *argp = rqstp->rq_argp; - __be32 nfserr; + struct nfsd_stat *resp = rqstp->rq_resp; 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); + resp->status = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, + argp->name, argp->len); fh_put(&argp->fh); - return nfserr; + return rpc_success; } /* @@ -526,7 +557,6 @@ nfsd_proc_readdir(struct svc_rqst *rqstp) struct nfsd_readdirargs *argp = rqstp->rq_argp; struct nfsd_readdirres *resp = rqstp->rq_resp; int count; - __be32 nfserr; loff_t offset; dprintk("nfsd: READDIR %s %d bytes at %d\n", @@ -547,15 +577,15 @@ nfsd_proc_readdir(struct svc_rqst *rqstp) 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->status = 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; + return rpc_success; } /* @@ -566,14 +596,13 @@ nfsd_proc_statfs(struct svc_rqst *rqstp) { struct nfsd_fhandle *argp = rqstp->rq_argp; struct nfsd_statfsres *resp = rqstp->rq_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); + resp->status = nfsd_statfs(rqstp, &argp->fh, &resp->stats, + NFSD_MAY_BYPASS_GSS_ON_ROOT); fh_put(&argp->fh); - return nfserr; + return rpc_success; } /* @@ -594,13 +623,13 @@ static const struct svc_procedure nfsd_procedures2[18] = { .pc_argsize = sizeof(struct nfsd_void), .pc_ressize = sizeof(struct nfsd_void), .pc_cachetype = RC_NOCACHE, - .pc_xdrressize = ST, + .pc_xdrressize = 0, }, [NFSPROC_GETATTR] = { .pc_func = nfsd_proc_getattr, .pc_decode = nfssvc_decode_fhandle, .pc_encode = nfssvc_encode_attrstat, - .pc_release = nfssvc_release_fhandle, + .pc_release = nfssvc_release_attrstat, .pc_argsize = sizeof(struct nfsd_fhandle), .pc_ressize = sizeof(struct nfsd_attrstat), .pc_cachetype = RC_NOCACHE, @@ -610,25 +639,26 @@ static const struct svc_procedure nfsd_procedures2[18] = { .pc_func = nfsd_proc_setattr, .pc_decode = nfssvc_decode_sattrargs, .pc_encode = nfssvc_encode_attrstat, - .pc_release = nfssvc_release_fhandle, + .pc_release = nfssvc_release_attrstat, .pc_argsize = sizeof(struct nfsd_sattrargs), .pc_ressize = sizeof(struct nfsd_attrstat), .pc_cachetype = RC_REPLBUFF, .pc_xdrressize = ST+AT, }, [NFSPROC_ROOT] = { + .pc_func = nfsd_proc_root, .pc_decode = nfssvc_decode_void, .pc_encode = nfssvc_encode_void, .pc_argsize = sizeof(struct nfsd_void), .pc_ressize = sizeof(struct nfsd_void), .pc_cachetype = RC_NOCACHE, - .pc_xdrressize = ST, + .pc_xdrressize = 0, }, [NFSPROC_LOOKUP] = { .pc_func = nfsd_proc_lookup, .pc_decode = nfssvc_decode_diropargs, .pc_encode = nfssvc_encode_diropres, - .pc_release = nfssvc_release_fhandle, + .pc_release = nfssvc_release_diropres, .pc_argsize = sizeof(struct nfsd_diropargs), .pc_ressize = sizeof(struct nfsd_diropres), .pc_cachetype = RC_NOCACHE, @@ -647,25 +677,26 @@ static const struct svc_procedure nfsd_procedures2[18] = { .pc_func = nfsd_proc_read, .pc_decode = nfssvc_decode_readargs, .pc_encode = nfssvc_encode_readres, - .pc_release = nfssvc_release_fhandle, + .pc_release = nfssvc_release_readres, .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_func = nfsd_proc_writecache, .pc_decode = nfssvc_decode_void, .pc_encode = nfssvc_encode_void, .pc_argsize = sizeof(struct nfsd_void), .pc_ressize = sizeof(struct nfsd_void), .pc_cachetype = RC_NOCACHE, - .pc_xdrressize = ST, + .pc_xdrressize = 0, }, [NFSPROC_WRITE] = { .pc_func = nfsd_proc_write, .pc_decode = nfssvc_decode_writeargs, .pc_encode = nfssvc_encode_attrstat, - .pc_release = nfssvc_release_fhandle, + .pc_release = nfssvc_release_attrstat, .pc_argsize = sizeof(struct nfsd_writeargs), .pc_ressize = sizeof(struct nfsd_attrstat), .pc_cachetype = RC_REPLBUFF, @@ -675,7 +706,7 @@ static const struct svc_procedure nfsd_procedures2[18] = { .pc_func = nfsd_proc_create, .pc_decode = nfssvc_decode_createargs, .pc_encode = nfssvc_encode_diropres, - .pc_release = nfssvc_release_fhandle, + .pc_release = nfssvc_release_diropres, .pc_argsize = sizeof(struct nfsd_createargs), .pc_ressize = sizeof(struct nfsd_diropres), .pc_cachetype = RC_REPLBUFF, @@ -684,36 +715,36 @@ static const struct svc_procedure nfsd_procedures2[18] = { [NFSPROC_REMOVE] = { .pc_func = nfsd_proc_remove, .pc_decode = nfssvc_decode_diropargs, - .pc_encode = nfssvc_encode_void, + .pc_encode = nfssvc_encode_stat, .pc_argsize = sizeof(struct nfsd_diropargs), - .pc_ressize = sizeof(struct nfsd_void), + .pc_ressize = sizeof(struct nfsd_stat), .pc_cachetype = RC_REPLSTAT, .pc_xdrressize = ST, }, [NFSPROC_RENAME] = { .pc_func = nfsd_proc_rename, .pc_decode = nfssvc_decode_renameargs, - .pc_encode = nfssvc_encode_void, + .pc_encode = nfssvc_encode_stat, .pc_argsize = sizeof(struct nfsd_renameargs), - .pc_ressize = sizeof(struct nfsd_void), + .pc_ressize = sizeof(struct nfsd_stat), .pc_cachetype = RC_REPLSTAT, .pc_xdrressize = ST, }, [NFSPROC_LINK] = { .pc_func = nfsd_proc_link, .pc_decode = nfssvc_decode_linkargs, - .pc_encode = nfssvc_encode_void, + .pc_encode = nfssvc_encode_stat, .pc_argsize = sizeof(struct nfsd_linkargs), - .pc_ressize = sizeof(struct nfsd_void), + .pc_ressize = sizeof(struct nfsd_stat), .pc_cachetype = RC_REPLSTAT, .pc_xdrressize = ST, }, [NFSPROC_SYMLINK] = { .pc_func = nfsd_proc_symlink, .pc_decode = nfssvc_decode_symlinkargs, - .pc_encode = nfssvc_encode_void, + .pc_encode = nfssvc_encode_stat, .pc_argsize = sizeof(struct nfsd_symlinkargs), - .pc_ressize = sizeof(struct nfsd_void), + .pc_ressize = sizeof(struct nfsd_stat), .pc_cachetype = RC_REPLSTAT, .pc_xdrressize = ST, }, @@ -721,7 +752,7 @@ static const struct svc_procedure nfsd_procedures2[18] = { .pc_func = nfsd_proc_mkdir, .pc_decode = nfssvc_decode_createargs, .pc_encode = nfssvc_encode_diropres, - .pc_release = nfssvc_release_fhandle, + .pc_release = nfssvc_release_diropres, .pc_argsize = sizeof(struct nfsd_createargs), .pc_ressize = sizeof(struct nfsd_diropres), .pc_cachetype = RC_REPLBUFF, @@ -730,9 +761,9 @@ static const struct svc_procedure nfsd_procedures2[18] = { [NFSPROC_RMDIR] = { .pc_func = nfsd_proc_rmdir, .pc_decode = nfssvc_decode_diropargs, - .pc_encode = nfssvc_encode_void, + .pc_encode = nfssvc_encode_stat, .pc_argsize = sizeof(struct nfsd_diropargs), - .pc_ressize = sizeof(struct nfsd_void), + .pc_ressize = sizeof(struct nfsd_stat), .pc_cachetype = RC_REPLSTAT, .pc_xdrressize = ST, }, diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index f7f6473578af..27b1ad136150 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -960,15 +960,6 @@ out: 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; -} - /* * A write procedure can have a large argument, and a read procedure can * have a large reply, but no NFSv2 or NFSv3 procedure has argument and @@ -1000,80 +991,85 @@ static bool nfs_request_too_big(struct svc_rqst *rqstp, return rqstp->rq_arg.len > PAGE_SIZE; } -int -nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) +/** + * nfsd_dispatch - Process an NFS or NFSACL Request + * @rqstp: incoming request + * @statp: pointer to location of accept_stat field in RPC Reply buffer + * + * This RPC dispatcher integrates the NFS server's duplicate reply cache. + * + * Return values: + * %0: Processing complete; do not send a Reply + * %1: Processing complete; send Reply in rqstp->rq_res + */ +int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) { - const struct svc_procedure *proc; - __be32 nfserr; - __be32 *nfserrp; + const struct svc_procedure *proc = rqstp->rq_procinfo; + struct kvec *argv = &rqstp->rq_arg.head[0]; + struct kvec *resv = &rqstp->rq_res.head[0]; + __be32 *p; dprintk("nfsd_dispatch: vers %d proc %d\n", rqstp->rq_vers, rqstp->rq_proc); - proc = rqstp->rq_procinfo; - if (nfs_request_too_big(rqstp, proc)) { - dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers); - *statp = rpc_garbage_args; - return 1; - } - rqstp->rq_lease_breaker = NULL; + if (nfs_request_too_big(rqstp, proc)) + goto out_too_large; + /* * 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 */ - if (proc->pc_decode && - !proc->pc_decode(rqstp, (__be32*)rqstp->rq_arg.head[0].iov_base)) { - dprintk("nfsd: failed to decode arguments!\n"); - *statp = rpc_garbage_args; - return 1; - } + if (!proc->pc_decode(rqstp, argv->iov_base)) + goto out_decode_err; - /* Check whether we have this call in the cache. */ switch (nfsd_cache_lookup(rqstp)) { - case RC_DROPIT: - return 0; + case RC_DOIT: + break; case RC_REPLY: - return 1; - case RC_DOIT:; - /* do it */ + goto out_cached_reply; + case RC_DROPIT: + goto out_dropit; } - /* need to grab the location to store the status, as - * nfsv4 does some encoding while processing + /* + * 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); - nfserr = map_new_errors(rqstp->rq_vers, nfserr); - if (nfserr == nfserr_dropit || test_bit(RQ_DROPME, &rqstp->rq_flags)) { - dprintk("nfsd: Dropping request; may be revisited later\n"); - nfsd_cache_update(rqstp, RC_NOCACHE, NULL); - return 0; - } + p = resv->iov_base + resv->iov_len; + resv->iov_len += sizeof(__be32); - if (rqstp->rq_proc != 0) - *nfserrp++ = nfserr; + *statp = proc->pc_func(rqstp); + if (*statp == rpc_drop_reply || test_bit(RQ_DROPME, &rqstp->rq_flags)) + goto out_update_drop; - /* Encode result. - * For NFSv2, additional info is never returned in case of an error. - */ - if (!(nfserr && rqstp->rq_vers == 2)) { - if (proc->pc_encode && !proc->pc_encode(rqstp, nfserrp)) { - /* 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; - } - } + if (!proc->pc_encode(rqstp, p)) + goto out_encode_err; - /* Store reply in cache. */ nfsd_cache_update(rqstp, rqstp->rq_cachetype, statp + 1); +out_cached_reply: + return 1; + +out_too_large: + dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers); + *statp = rpc_garbage_args; + return 1; + +out_decode_err: + dprintk("nfsd: failed to decode arguments!\n"); + *statp = rpc_garbage_args; + return 1; + +out_update_drop: + dprintk("nfsd: Dropping request; may be revisited later\n"); + nfsd_cache_update(rqstp, RC_NOCACHE, NULL); +out_dropit: + return 0; + +out_encode_err: + dprintk("nfsd: failed to encode result!\n"); + nfsd_cache_update(rqstp, RC_NOCACHE, NULL); + *statp = rpc_system_err; return 1; } diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index b51fe515f06f..8a288c8fcd57 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -430,11 +430,24 @@ nfssvc_encode_void(struct svc_rqst *rqstp, __be32 *p) } int +nfssvc_encode_stat(struct svc_rqst *rqstp, __be32 *p) +{ + struct nfsd_stat *resp = rqstp->rq_resp; + + *p++ = resp->status; + return xdr_ressize_check(rqstp, p); +} + +int nfssvc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p) { struct nfsd_attrstat *resp = rqstp->rq_resp; + *p++ = resp->status; + if (resp->status != nfs_ok) + goto out; p = encode_fattr(rqstp, p, &resp->fh, &resp->stat); +out: return xdr_ressize_check(rqstp, p); } @@ -443,8 +456,12 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd_diropres *resp = rqstp->rq_resp; + *p++ = resp->status; + if (resp->status != nfs_ok) + goto out; p = encode_fh(p, &resp->fh); p = encode_fattr(rqstp, p, &resp->fh, &resp->stat); +out: return xdr_ressize_check(rqstp, p); } @@ -453,6 +470,10 @@ nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd_readlinkres *resp = rqstp->rq_resp; + *p++ = resp->status; + if (resp->status != nfs_ok) + return xdr_ressize_check(rqstp, p); + *p++ = htonl(resp->len); xdr_ressize_check(rqstp, p); rqstp->rq_res.page_len = resp->len; @@ -470,6 +491,10 @@ nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd_readres *resp = rqstp->rq_resp; + *p++ = resp->status; + if (resp->status != nfs_ok) + return xdr_ressize_check(rqstp, p); + p = encode_fattr(rqstp, p, &resp->fh, &resp->stat); *p++ = htonl(resp->count); xdr_ressize_check(rqstp, p); @@ -490,6 +515,10 @@ nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p) { struct nfsd_readdirres *resp = rqstp->rq_resp; + *p++ = resp->status; + if (resp->status != nfs_ok) + return xdr_ressize_check(rqstp, p); + xdr_ressize_check(rqstp, p); p = resp->buffer; *p++ = 0; /* no more entries */ @@ -505,6 +534,10 @@ nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p) struct nfsd_statfsres *resp = rqstp->rq_resp; struct kstatfs *stat = &resp->stats; + *p++ = resp->status; + if (resp->status != nfs_ok) + return xdr_ressize_check(rqstp, p); + *p++ = htonl(NFSSVC_MAXBLKSIZE_V2); /* max transfer size */ *p++ = htonl(stat->f_bsize); *p++ = htonl(stat->f_blocks); @@ -561,10 +594,23 @@ nfssvc_encode_entry(void *ccdv, const char *name, /* * XDR release functions */ -void -nfssvc_release_fhandle(struct svc_rqst *rqstp) +void nfssvc_release_attrstat(struct svc_rqst *rqstp) { - struct nfsd_fhandle *resp = rqstp->rq_resp; + struct nfsd_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); +} + +void nfssvc_release_diropres(struct svc_rqst *rqstp) +{ + struct nfsd_diropres *resp = rqstp->rq_resp; + + fh_put(&resp->fh); +} + +void nfssvc_release_readres(struct svc_rqst *rqstp) +{ + struct nfsd_readres *resp = rqstp->rq_resp; fh_put(&resp->fh); } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 3b408532a5dc..9eae11a9d21c 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -693,31 +693,4 @@ 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 -void 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 void nfsd_fault_inject_init(void) {} -static inline void nfsd_fault_inject_cleanup(void) {} -#endif /* CONFIG_NFSD_FAULT_INJECTION */ - #endif /* NFSD4_STATE_H */ diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 1861db1bdc67..99bf07800cd0 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -289,8 +289,8 @@ DEFINE_STATEID_EVENT(layout_recall_done); DEFINE_STATEID_EVENT(layout_recall_fail); DEFINE_STATEID_EVENT(layout_recall_release); -DEFINE_STATEID_EVENT(deleg_open); -DEFINE_STATEID_EVENT(deleg_none); +DEFINE_STATEID_EVENT(open); +DEFINE_STATEID_EVENT(deleg_read); DEFINE_STATEID_EVENT(deleg_break); DEFINE_STATEID_EVENT(deleg_recall); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index aba5af9df328..1ecaceebee13 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -2259,7 +2259,8 @@ out: __be32 nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name) { - int err, ret; + __be32 err; + int ret; err = fh_verify(rqstp, fhp, 0, NFSD_MAY_WRITE); if (err) @@ -2283,7 +2284,8 @@ __be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name, void *buf, u32 len, u32 flags) { - int err, ret; + __be32 err; + int ret; err = fh_verify(rqstp, fhp, 0, NFSD_MAY_WRITE); if (err) diff --git a/fs/nfsd/xdr.h b/fs/nfsd/xdr.h index ea7cca3a64b7..0ff336b0b25f 100644 --- a/fs/nfsd/xdr.h +++ b/fs/nfsd/xdr.h @@ -82,27 +82,37 @@ struct nfsd_readdirargs { __be32 * buffer; }; +struct nfsd_stat { + __be32 status; +}; + struct nfsd_attrstat { + __be32 status; struct svc_fh fh; struct kstat stat; }; struct nfsd_diropres { + __be32 status; struct svc_fh fh; struct kstat stat; }; struct nfsd_readlinkres { + __be32 status; int len; }; struct nfsd_readres { + __be32 status; struct svc_fh fh; unsigned long count; struct kstat stat; }; struct nfsd_readdirres { + __be32 status; + int count; struct readdir_cd common; @@ -112,6 +122,7 @@ struct nfsd_readdirres { }; struct nfsd_statfsres { + __be32 status; struct kstatfs stats; }; @@ -146,6 +157,7 @@ int nfssvc_decode_linkargs(struct svc_rqst *, __be32 *); int nfssvc_decode_symlinkargs(struct svc_rqst *, __be32 *); int nfssvc_decode_readdirargs(struct svc_rqst *, __be32 *); int nfssvc_encode_void(struct svc_rqst *, __be32 *); +int nfssvc_encode_stat(struct svc_rqst *, __be32 *); int nfssvc_encode_attrstat(struct svc_rqst *, __be32 *); int nfssvc_encode_diropres(struct svc_rqst *, __be32 *); int nfssvc_encode_readlinkres(struct svc_rqst *, __be32 *); @@ -156,7 +168,9 @@ int nfssvc_encode_readdirres(struct svc_rqst *, __be32 *); int nfssvc_encode_entry(void *, const char *name, int namlen, loff_t offset, u64 ino, unsigned int); -void nfssvc_release_fhandle(struct svc_rqst *); +void nfssvc_release_attrstat(struct svc_rqst *rqstp); +void nfssvc_release_diropres(struct svc_rqst *rqstp); +void nfssvc_release_readres(struct svc_rqst *rqstp); /* Helper functions for NFSv2 ACL code */ __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat); diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h index 4155fd71714c..ae6fa6c9cb46 100644 --- a/fs/nfsd/xdr3.h +++ b/fs/nfsd/xdr3.h @@ -273,6 +273,7 @@ union nfsd3_xdrstore { #define NFS3_SVC_XDRSIZE sizeof(union nfsd3_xdrstore) +int nfs3svc_decode_voidarg(struct svc_rqst *, __be32 *); int nfs3svc_decode_fhandle(struct svc_rqst *, __be32 *); int nfs3svc_decode_sattrargs(struct svc_rqst *, __be32 *); int nfs3svc_decode_diropargs(struct svc_rqst *, __be32 *); diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 66499fb6b567..679d40af1bbb 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -781,6 +781,7 @@ set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp) bool nfsd4_mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp); +int nfs4svc_decode_voidarg(struct svc_rqst *, __be32 *); int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *); int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *); int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *); diff --git a/include/linux/nfs_ssc.h b/include/linux/nfs_ssc.h new file mode 100644 index 000000000000..f5ba0fbff72f --- /dev/null +++ b/include/linux/nfs_ssc.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * include/linux/nfs_ssc.h + * + * Author: Dai Ngo <dai.ngo@oracle.com> + * + * Copyright (c) 2020, Oracle and/or its affiliates. + */ + +#include <linux/nfs_fs.h> + +extern struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl; + +/* + * NFS_V4 + */ +struct nfs4_ssc_client_ops { + struct file *(*sco_open)(struct vfsmount *ss_mnt, + struct nfs_fh *src_fh, nfs4_stateid *stateid); + void (*sco_close)(struct file *filep); +}; + +/* + * NFS_FS + */ +struct nfs_ssc_client_ops { + void (*sco_sb_deactive)(struct super_block *sb); +}; + +struct nfs_ssc_client_ops_tbl { + const struct nfs4_ssc_client_ops *ssc_nfs4_ops; + const struct nfs_ssc_client_ops *ssc_nfs_ops; +}; + +extern void nfs42_ssc_register_ops(void); +extern void nfs42_ssc_unregister_ops(void); + +extern void nfs42_ssc_register(const struct nfs4_ssc_client_ops *ops); +extern void nfs42_ssc_unregister(const struct nfs4_ssc_client_ops *ops); + +#ifdef CONFIG_NFSD_V4_2_INTER_SSC +static inline struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, + struct nfs_fh *src_fh, nfs4_stateid *stateid) +{ + if (nfs_ssc_client_tbl.ssc_nfs4_ops) + return (*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_open)(ss_mnt, src_fh, stateid); + return ERR_PTR(-EIO); +} + +static inline void nfs42_ssc_close(struct file *filep) +{ + if (nfs_ssc_client_tbl.ssc_nfs4_ops) + (*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep); +} +#endif + +/* + * NFS_FS + */ +extern void nfs_ssc_register(const struct nfs_ssc_client_ops *ops); +extern void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops); + +static inline void nfs_do_sb_deactive(struct super_block *sb) +{ + if (nfs_ssc_client_tbl.ssc_nfs_ops) + (*nfs_ssc_client_tbl.ssc_nfs_ops->sco_sb_deactive)(sb); +} diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index fe7ff7f5b584..9548d075e06d 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -234,6 +234,8 @@ typedef int (*kxdrdproc_t)(struct rpc_rqst *rqstp, struct xdr_stream *xdr, extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, struct rpc_rqst *rqst); extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); +extern int xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec, + size_t nbytes); extern void xdr_commit_encode(struct xdr_stream *xdr); extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len); extern int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen); diff --git a/include/uapi/linux/nfsacl.h b/include/uapi/linux/nfsacl.h index ca9a8501ff30..2c2ad204d3b0 100644 --- a/include/uapi/linux/nfsacl.h +++ b/include/uapi/linux/nfsacl.h @@ -9,11 +9,13 @@ #define NFS_ACL_PROGRAM 100227 +#define ACLPROC2_NULL 0 #define ACLPROC2_GETACL 1 #define ACLPROC2_SETACL 2 #define ACLPROC2_GETATTR 3 #define ACLPROC2_ACCESS 4 +#define ACLPROC3_NULL 0 #define ACLPROC3_GETACL 1 #define ACLPROC3_SETACL 2 diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 258b04372f85..bd4678db9d76 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1147,9 +1147,9 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, struct gssp_in_token *in_token) { struct kvec *argv = &rqstp->rq_arg.head[0]; - unsigned int page_base, length; - int pages, i, res; - size_t inlen; + unsigned int length, pgto_offs, pgfrom_offs; + int pages, i, res, pgto, pgfrom; + size_t inlen, to_offs, from_offs; res = gss_read_common_verf(gc, argv, authp, in_handle); if (res) @@ -1177,17 +1177,24 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, memcpy(page_address(in_token->pages[0]), argv->iov_base, length); inlen -= length; - i = 1; - page_base = rqstp->rq_arg.page_base; + to_offs = length; + from_offs = rqstp->rq_arg.page_base; while (inlen) { - length = min_t(unsigned int, inlen, PAGE_SIZE); - memcpy(page_address(in_token->pages[i]), - page_address(rqstp->rq_arg.pages[i]) + page_base, + pgto = to_offs >> PAGE_SHIFT; + pgfrom = from_offs >> PAGE_SHIFT; + pgto_offs = to_offs & ~PAGE_MASK; + pgfrom_offs = from_offs & ~PAGE_MASK; + + length = min_t(unsigned int, inlen, + min_t(unsigned int, PAGE_SIZE - pgto_offs, + PAGE_SIZE - pgfrom_offs)); + memcpy(page_address(in_token->pages[pgto]) + pgto_offs, + page_address(rqstp->rq_arg.pages[pgfrom]) + pgfrom_offs, length); + to_offs += length; + from_offs += length; inlen -= length; - page_base = 0; - i++; } return 0; } diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 3fecad369592..22a2c235abf1 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -111,7 +111,7 @@ out_free: * by the backchannel. This function can be called multiple times * when creating new sessions that use the same rpc_xprt. The * preallocated buffers are added to the pool of resources used by - * the rpc_xprt. Anyone of these resources may be used used by an + * the rpc_xprt. Any one of these resources may be used by an * incoming callback request. It's up to the higher levels in the * stack to enforce that the maximum number of session slots is not * being exceeded. diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index baef5ee43dbb..20c93b68505e 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -498,16 +498,17 @@ static int cache_clean(void) */ static void do_cache_clean(struct work_struct *work) { - int delay = 5; - if (cache_clean() == -1) - delay = round_jiffies_relative(30*HZ); + int delay; if (list_empty(&cache_list)) - delay = 0; + return; + + if (cache_clean() == -1) + delay = round_jiffies_relative(30*HZ); + else + delay = 5; - if (delay) - queue_delayed_work(system_power_efficient_wq, - &cache_cleaner, delay); + queue_delayed_work(system_power_efficient_wq, &cache_cleaner, delay); } @@ -908,7 +909,7 @@ static ssize_t cache_do_downcall(char *kaddr, const char __user *buf, static ssize_t cache_slow_downcall(const char __user *buf, size_t count, struct cache_detail *cd) { - static char write_buf[8192]; /* protected by queue_io_mutex */ + static char write_buf[32768]; /* protected by queue_io_mutex */ ssize_t ret = -EINVAL; if (count >= sizeof(write_buf)) @@ -1436,10 +1437,10 @@ static int c_show(struct seq_file *m, void *p) cache_get(cp); if (cache_check(cd, cp, NULL)) /* cache_check does a cache_put on failure */ - seq_printf(m, "# "); + seq_puts(m, "# "); else { if (cache_is_expired(cd, cp)) - seq_printf(m, "# "); + seq_puts(m, "# "); cache_put(cp, cd); } diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c index 6c86e2a7d942..a18b36b5422d 100644 --- a/net/sunrpc/sysctl.c +++ b/net/sunrpc/sysctl.c @@ -70,7 +70,13 @@ static int proc_do_xprt(struct ctl_table *table, int write, return 0; } len = svc_print_xprts(tmpbuf, sizeof(tmpbuf)); - return memory_read_from_buffer(buffer, *lenp, ppos, tmpbuf, len); + *lenp = memory_read_from_buffer(buffer, *lenp, ppos, tmpbuf, len); + + if (*lenp < 0) { + *lenp = 0; + return -EINVAL; + } + return 0; } static int diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 3feff529a764..71e03b930b70 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -792,6 +792,51 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) } EXPORT_SYMBOL_GPL(xdr_reserve_space); + +/** + * xdr_reserve_space_vec - Reserves a large amount of buffer space for sending + * @xdr: pointer to xdr_stream + * @vec: pointer to a kvec array + * @nbytes: number of bytes to reserve + * + * Reserves enough buffer space to encode 'nbytes' of data and stores the + * pointers in 'vec'. The size argument passed to xdr_reserve_space() is + * determined based on the number of bytes remaining in the current page to + * avoid invalidating iov_base pointers when xdr_commit_encode() is called. + */ +int xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec, size_t nbytes) +{ + int thislen; + int v = 0; + __be32 *p; + + /* + * svcrdma requires every READ payload to start somewhere + * in xdr->pages. + */ + if (xdr->iov == xdr->buf->head) { + xdr->iov = NULL; + xdr->end = xdr->p; + } + + while (nbytes) { + thislen = xdr->buf->page_len % PAGE_SIZE; + thislen = min_t(size_t, nbytes, PAGE_SIZE - thislen); + + p = xdr_reserve_space(xdr, thislen); + if (!p) + return -EIO; + + vec[v].iov_base = p; + vec[v].iov_len = thislen; + v++; + nbytes -= thislen; + } + + return v; +} +EXPORT_SYMBOL_GPL(xdr_reserve_space_vec); + /** * xdr_truncate_encode - truncate an encode buffer * @xdr: pointer to xdr_stream @@ -802,7 +847,7 @@ EXPORT_SYMBOL_GPL(xdr_reserve_space); * head, tail, and page lengths are adjusted to correspond. * * If this means moving xdr->p to a different buffer, we assume that - * that the end pointer should be set to the end of the current page, + * the end pointer should be set to the end of the current page, * except in the case of the head buffer when we assume the head * buffer's current length represents the end of the available buffer. * diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index fe54cbe97a46..80a0c0e87590 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -137,7 +137,7 @@ static int svc_rdma_rw_ctx_init(struct svcxprt_rdma *rdma, } /* A chunk context tracks all I/O for moving one Read or Write - * chunk. This is a a set of rdma_rw's that handle data movement + * chunk. This is a set of rdma_rw's that handle data movement * for all segments of one chunk. * * These are small, acquired with a single allocator call, and diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 7b94d971feb3..c3d588b149aa 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -638,10 +638,11 @@ static int svc_rdma_pull_up_reply_msg(struct svcxprt_rdma *rdma, while (remaining) { len = min_t(u32, PAGE_SIZE - pageoff, remaining); - memcpy(dst, page_address(*ppages), len); + memcpy(dst, page_address(*ppages) + pageoff, len); remaining -= len; dst += len; pageoff = 0; + ppages++; } } diff --git a/tools/nfsd/inject_fault.sh b/tools/nfsd/inject_fault.sh deleted file mode 100755 index 10ceee64a09a..000000000000 --- a/tools/nfsd/inject_fault.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0 -# -# Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com> -# -# Script for easier NFSD fault injection - -# Check that debugfs has been mounted -DEBUGFS=`cat /proc/mounts | grep debugfs` -if [ "$DEBUGFS" == "" ]; then - echo "debugfs does not appear to be mounted!" - echo "Please mount debugfs and try again" - exit 1 -fi - -# Check that the fault injection directory exists -DEBUGDIR=`echo $DEBUGFS | awk '{print $2}'`/nfsd -if [ ! -d "$DEBUGDIR" ]; then - echo "$DEBUGDIR does not exist" - echo "Check that your .config selects CONFIG_NFSD_FAULT_INJECTION" - exit 1 -fi - -function help() -{ - echo "Usage $0 injection_type [count]" - echo "" - echo "Injection types are:" - ls $DEBUGDIR - exit 1 -} - -if [ $# == 0 ]; then - help -elif [ ! -f $DEBUGDIR/$1 ]; then - help -elif [ $# != 2 ]; then - COUNT=0 -else - COUNT=$2 -fi - -BEFORE=`mktemp` -AFTER=`mktemp` -dmesg > $BEFORE -echo $COUNT > $DEBUGDIR/$1 -dmesg > $AFTER -# Capture lines that only exist in the $AFTER file -diff $BEFORE $AFTER | grep ">" -rm -f $BEFORE $AFTER |