Merge 5.10.220 into android12-5.10-lts
Changes in 5.10.220 SUNRPC: Rename svc_encode_read_payload() NFSD: Invoke svc_encode_result_payload() in "read" NFSD encoders NFSD: A semicolon is not needed after a switch statement. nfsd/nfs3: remove unused macro nfsd3_fhandleres NFSD: Clean up the show_nf_may macro NFSD: Remove extra "0x" in tracepoint format specifier NFSD: Add SPDX header for fs/nfsd/trace.c nfsd: Fix error return code in nfsd_file_cache_init() SUNRPC: Add xdr_set_scratch_page() and xdr_reset_scratch_buffer() SUNRPC: Prepare for xdr_stream-style decoding on the server-side NFSD: Add common helpers to decode void args and encode void results NFSD: Add tracepoints in nfsd_dispatch() NFSD: Add tracepoints in nfsd4_decode/encode_compound() NFSD: Replace the internals of the READ_BUF() macro NFSD: Replace READ* macros in nfsd4_decode_access() NFSD: Replace READ* macros in nfsd4_decode_close() NFSD: Replace READ* macros in nfsd4_decode_commit() NFSD: Change the way the expected length of a fattr4 is checked NFSD: Replace READ* macros that decode the fattr4 size attribute NFSD: Replace READ* macros that decode the fattr4 acl attribute NFSD: Replace READ* macros that decode the fattr4 mode attribute NFSD: Replace READ* macros that decode the fattr4 owner attribute NFSD: Replace READ* macros that decode the fattr4 owner_group attribute NFSD: Replace READ* macros that decode the fattr4 time_set attributes NFSD: Replace READ* macros that decode the fattr4 security label attribute NFSD: Replace READ* macros that decode the fattr4 umask attribute NFSD: Replace READ* macros in nfsd4_decode_fattr() NFSD: Replace READ* macros in nfsd4_decode_create() NFSD: Replace READ* macros in nfsd4_decode_delegreturn() NFSD: Replace READ* macros in nfsd4_decode_getattr() NFSD: Replace READ* macros in nfsd4_decode_link() NFSD: Relocate nfsd4_decode_opaque() NFSD: Add helpers to decode a clientid4 and an NFSv4 state owner NFSD: Add helper for decoding locker4 NFSD: Replace READ* macros in nfsd4_decode_lock() NFSD: Replace READ* macros in nfsd4_decode_lockt() NFSD: Replace READ* macros in nfsd4_decode_locku() NFSD: Replace READ* macros in nfsd4_decode_lookup() NFSD: Add helper to decode NFSv4 verifiers NFSD: Add helper to decode OPEN's createhow4 argument NFSD: Add helper to decode OPEN's openflag4 argument NFSD: Replace READ* macros in nfsd4_decode_share_access() NFSD: Replace READ* macros in nfsd4_decode_share_deny() NFSD: Add helper to decode OPEN's open_claim4 argument NFSD: Replace READ* macros in nfsd4_decode_open() NFSD: Replace READ* macros in nfsd4_decode_open_confirm() NFSD: Replace READ* macros in nfsd4_decode_open_downgrade() NFSD: Replace READ* macros in nfsd4_decode_putfh() NFSD: Replace READ* macros in nfsd4_decode_read() NFSD: Replace READ* macros in nfsd4_decode_readdir() NFSD: Replace READ* macros in nfsd4_decode_remove() NFSD: Replace READ* macros in nfsd4_decode_rename() NFSD: Replace READ* macros in nfsd4_decode_renew() NFSD: Replace READ* macros in nfsd4_decode_secinfo() NFSD: Replace READ* macros in nfsd4_decode_setattr() NFSD: Replace READ* macros in nfsd4_decode_setclientid() NFSD: Replace READ* macros in nfsd4_decode_setclientid_confirm() NFSD: Replace READ* macros in nfsd4_decode_verify() NFSD: Replace READ* macros in nfsd4_decode_write() NFSD: Replace READ* macros in nfsd4_decode_release_lockowner() NFSD: Replace READ* macros in nfsd4_decode_cb_sec() NFSD: Replace READ* macros in nfsd4_decode_backchannel_ctl() NFSD: Replace READ* macros in nfsd4_decode_bind_conn_to_session() NFSD: Add a separate decoder to handle state_protect_ops NFSD: Add a separate decoder for ssv_sp_parms NFSD: Add a helper to decode state_protect4_a NFSD: Add a helper to decode nfs_impl_id4 NFSD: Add a helper to decode channel_attrs4 NFSD: Replace READ* macros in nfsd4_decode_create_session() NFSD: Replace READ* macros in nfsd4_decode_destroy_session() NFSD: Replace READ* macros in nfsd4_decode_free_stateid() NFSD: Replace READ* macros in nfsd4_decode_getdeviceinfo() NFSD: Replace READ* macros in nfsd4_decode_layoutcommit() NFSD: Replace READ* macros in nfsd4_decode_layoutget() NFSD: Replace READ* macros in nfsd4_decode_layoutreturn() NFSD: Replace READ* macros in nfsd4_decode_secinfo_no_name() NFSD: Replace READ* macros in nfsd4_decode_sequence() NFSD: Replace READ* macros in nfsd4_decode_test_stateid() NFSD: Replace READ* macros in nfsd4_decode_destroy_clientid() NFSD: Replace READ* macros in nfsd4_decode_reclaim_complete() NFSD: Replace READ* macros in nfsd4_decode_fallocate() NFSD: Replace READ* macros in nfsd4_decode_nl4_server() NFSD: Replace READ* macros in nfsd4_decode_copy() NFSD: Replace READ* macros in nfsd4_decode_copy_notify() NFSD: Replace READ* macros in nfsd4_decode_offload_status() NFSD: Replace READ* macros in nfsd4_decode_seek() NFSD: Replace READ* macros in nfsd4_decode_clone() NFSD: Replace READ* macros in nfsd4_decode_xattr_name() NFSD: Replace READ* macros in nfsd4_decode_setxattr() NFSD: Replace READ* macros in nfsd4_decode_listxattrs() NFSD: Make nfsd4_ops::opnum a u32 NFSD: Replace READ* macros in nfsd4_decode_compound() NFSD: Remove macros that are no longer used nfsd: only call inode_query_iversion in the I_VERSION case nfsd: simplify nfsd4_change_info nfsd: minor nfsd4_change_attribute cleanup nfsd4: don't query change attribute in v2/v3 case Revert "nfsd4: support change_attr_type attribute" nfsd: add a new EXPORT_OP_NOWCC flag to struct export_operations nfsd: allow filesystems to opt out of subtree checking nfsd: close cached files prior to a REMOVE or RENAME that would replace target exportfs: Add a function to return the raw output from fh_to_dentry() nfsd: Fix up nfsd to ensure that timeout errors don't result in ESTALE nfsd: Set PF_LOCAL_THROTTLE on local filesystems only nfsd: Record NFSv4 pre/post-op attributes as non-atomic exec: Don't open code get_close_on_exec exec: Move unshare_files to fix posix file locking during exec exec: Simplify unshare_files exec: Remove reset_files_struct kcmp: In kcmp_epoll_target use fget_task bpf: In bpf_task_fd_query use fget_task proc/fd: In proc_fd_link use fget_task Revert "fget: clarify and improve __fget_files() implementation" file: Rename __fcheck_files to files_lookup_fd_raw file: Factor files_lookup_fd_locked out of fcheck_files file: Replace fcheck_files with files_lookup_fd_rcu file: Rename fcheck lookup_fd_rcu file: Implement task_lookup_fd_rcu proc/fd: In tid_fd_mode use task_lookup_fd_rcu kcmp: In get_file_raw_ptr use task_lookup_fd_rcu file: Implement task_lookup_next_fd_rcu proc/fd: In proc_readfd_common use task_lookup_next_fd_rcu proc/fd: In fdinfo seq_show don't use get_files_struct file: Merge __fd_install into fd_install file: In f_dupfd read RLIMIT_NOFILE once. file: Merge __alloc_fd into alloc_fd file: Rename __close_fd to close_fd and remove the files parameter file: Replace ksys_close with close_fd inotify: Increase default inotify.max_user_watches limit to 1048576 fs/lockd: convert comma to semicolon NFSD: Fix sparse warning in nfssvc.c NFSD: Restore NFSv4 decoding's SAVEMEM functionality SUNRPC: Make trace_svc_process() display the RPC procedure symbolically SUNRPC: Display RPC procedure names instead of proc numbers SUNRPC: Move definition of XDR_UNIT NFSD: Update GETATTR3args decoder to use struct xdr_stream NFSD: Update ACCESS3arg decoder to use struct xdr_stream NFSD: Update READ3arg decoder to use struct xdr_stream NFSD: Update WRITE3arg decoder to use struct xdr_stream NFSD: Update READLINK3arg decoder to use struct xdr_stream NFSD: Fix returned READDIR offset cookie NFSD: Add helper to set up the pages where the dirlist is encoded NFSD: Update READDIR3args decoders to use struct xdr_stream NFSD: Update COMMIT3arg decoder to use struct xdr_stream NFSD: Update the NFSv3 DIROPargs decoder to use struct xdr_stream NFSD: Update the RENAME3args decoder to use struct xdr_stream NFSD: Update the LINK3args decoder to use struct xdr_stream NFSD: Update the SETATTR3args decoder to use struct xdr_stream NFSD: Update the CREATE3args decoder to use struct xdr_stream NFSD: Update the MKDIR3args decoder to use struct xdr_stream NFSD: Update the SYMLINK3args decoder to use struct xdr_stream NFSD: Update the MKNOD3args decoder to use struct xdr_stream NFSD: Update the NFSv2 GETATTR argument decoder to use struct xdr_stream NFSD: Update the NFSv2 READ argument decoder to use struct xdr_stream NFSD: Update the NFSv2 WRITE argument decoder to use struct xdr_stream NFSD: Update the NFSv2 READLINK argument decoder to use struct xdr_stream NFSD: Add helper to set up the pages where the dirlist is encoded NFSD: Update the NFSv2 READDIR argument decoder to use struct xdr_stream NFSD: Update NFSv2 diropargs decoding to use struct xdr_stream NFSD: Update the NFSv2 RENAME argument decoder to use struct xdr_stream NFSD: Update the NFSv2 LINK argument decoder to use struct xdr_stream NFSD: Update the NFSv2 SETATTR argument decoder to use struct xdr_stream NFSD: Update the NFSv2 CREATE argument decoder to use struct xdr_stream NFSD: Update the NFSv2 SYMLINK argument decoder to use struct xdr_stream NFSD: Remove argument length checking in nfsd_dispatch() NFSD: Update the NFSv2 GETACL argument decoder to use struct xdr_stream NFSD: Add an xdr_stream-based decoder for NFSv2/3 ACLs NFSD: Update the NFSv2 SETACL argument decoder to use struct xdr_stream NFSD: Update the NFSv2 ACL GETATTR argument decoder to use struct xdr_stream NFSD: Update the NFSv2 ACL ACCESS argument decoder to use struct xdr_stream NFSD: Clean up after updating NFSv2 ACL decoders NFSD: Update the NFSv3 GETACL argument decoder to use struct xdr_stream NFSD: Update the NFSv2 SETACL argument decoder to use struct xdr_stream NFSD: Clean up after updating NFSv3 ACL decoders nfsd: remove unused stats counters nfsd: protect concurrent access to nfsd stats counters nfsd: report per-export stats nfsd4: simplify process_lookup1 nfsd: simplify process_lock nfsd: simplify nfsd_renew nfsd: rename lookup_clientid->set_client nfsd: refactor set_client nfsd: find_cpntf_state cleanup nfsd: remove unused set_client argument nfsd: simplify nfsd4_check_open_reclaim nfsd: cstate->session->se_client -> cstate->clp NFSv4_2: SSC helper should use its own config. nfs: use change attribute for NFS re-exports nfsd: skip some unnecessary stats in the v4 case inotify, memcg: account inotify instances to kmemcg module: unexport find_module and module_mutex module: use RCU to synchronize find_module kallsyms: refactor {,module_}kallsyms_on_each_symbol kallsyms: only build {,module_}kallsyms_on_each_symbol when required fs: add file and path permissions helpers namei: introduce struct renamedata NFSD: Extract the svcxdr_init_encode() helper NFSD: Update the GETATTR3res encoder to use struct xdr_stream NFSD: Update the NFSv3 ACCESS3res encoder to use struct xdr_stream NFSD: Update the NFSv3 LOOKUP3res encoder to use struct xdr_stream NFSD: Update the NFSv3 wccstat result encoder to use struct xdr_stream NFSD: Update the NFSv3 READLINK3res encoder to use struct xdr_stream NFSD: Update the NFSv3 READ3res encode to use struct xdr_stream NFSD: Update the NFSv3 WRITE3res encoder to use struct xdr_stream NFSD: Update the NFSv3 CREATE family of encoders to use struct xdr_stream NFSD: Update the NFSv3 RENAMEv3res encoder to use struct xdr_stream NFSD: Update the NFSv3 LINK3res encoder to use struct xdr_stream NFSD: Update the NFSv3 FSSTAT3res encoder to use struct xdr_stream NFSD: Update the NFSv3 FSINFO3res encoder to use struct xdr_stream NFSD: Update the NFSv3 PATHCONF3res encoder to use struct xdr_stream NFSD: Update the NFSv3 COMMIT3res encoder to use struct xdr_stream NFSD: Add a helper that encodes NFSv3 directory offset cookies NFSD: Count bytes instead of pages in the NFSv3 READDIR encoder NFSD: Update the NFSv3 READDIR3res encoder to use struct xdr_stream NFSD: Update NFSv3 READDIR entry encoders to use struct xdr_stream NFSD: Remove unused NFSv3 directory entry encoders NFSD: Reduce svc_rqst::rq_pages churn during READDIR operations NFSD: Update the NFSv2 stat encoder to use struct xdr_stream NFSD: Update the NFSv2 attrstat encoder to use struct xdr_stream NFSD: Update the NFSv2 diropres encoder to use struct xdr_stream NFSD: Update the NFSv2 READLINK result encoder to use struct xdr_stream NFSD: Update the NFSv2 READ result encoder to use struct xdr_stream NFSD: Update the NFSv2 STATFS result encoder to use struct xdr_stream NFSD: Add a helper that encodes NFSv3 directory offset cookies NFSD: Count bytes instead of pages in the NFSv2 READDIR encoder NFSD: Update the NFSv2 READDIR result encoder to use struct xdr_stream NFSD: Update the NFSv2 READDIR entry encoder to use struct xdr_stream NFSD: Remove unused NFSv2 directory entry encoders NFSD: Add an xdr_stream-based encoder for NFSv2/3 ACLs NFSD: Update the NFSv2 GETACL result encoder to use struct xdr_stream NFSD: Update the NFSv2 SETACL result encoder to use struct xdr_stream NFSD: Update the NFSv2 ACL GETATTR result encoder to use struct xdr_stream NFSD: Update the NFSv2 ACL ACCESS result encoder to use struct xdr_stream NFSD: Clean up after updating NFSv2 ACL encoders NFSD: Update the NFSv3 GETACL result encoder to use struct xdr_stream NFSD: Update the NFSv3 SETACL result encoder to use struct xdr_stream NFSD: Clean up after updating NFSv3 ACL encoders NFSD: Add a tracepoint to record directory entry encoding NFSD: Clean up NFSDDBG_FACILITY macro nfsd: helper for laundromat expiry calculations nfsd: Log client tracking type log message as info instead of warning nfsd: Fix typo "accesible" nfsd: COPY with length 0 should copy to end of file nfsd: don't ignore high bits of copy count nfsd: report client confirmation status in "info" file SUNRPC: Export svc_xprt_received() UAPI: nfsfh.h: Replace one-element array with flexible-array member NFSD: Use DEFINE_SPINLOCK() for spinlock fsnotify: allow fsnotify_{peek,remove}_first_event with empty queue Revert "fanotify: limit number of event merge attempts" fanotify: reduce event objectid to 29-bit hash fanotify: mix event info and pid into merge key hash fsnotify: use hash table for faster events merge fanotify: limit number of event merge attempts fanotify: configurable limits via sysfs fanotify: support limited functionality for unprivileged users fanotify_user: use upper_32_bits() to verify mask nfsd: remove unused function nfsd: removed unused argument in nfsd_startup_generic() nfsd: hash nfs4_files by inode number nfsd: track filehandle aliasing in nfs4_files nfsd: reshuffle some code nfsd: grant read delegations to clients holding writes nfsd: Fix fall-through warnings for Clang NFSv4.2: Remove ifdef CONFIG_NFSD from NFSv4.2 client SSC code. NFS: fix nfs_fetch_iversion() fanotify: fix permission model of unprivileged group NFSD: Add an RPC authflavor tracepoint display helper NFSD: Add nfsd_clid_cred_mismatch tracepoint NFSD: Add nfsd_clid_verf_mismatch tracepoint NFSD: Remove trace_nfsd_clid_inuse_err NFSD: Add nfsd_clid_confirmed tracepoint NFSD: Add nfsd_clid_reclaim_complete tracepoint NFSD: Add nfsd_clid_destroyed tracepoint NFSD: Add a couple more nfsd_clid_expired call sites NFSD: Add tracepoints for SETCLIENTID edge cases NFSD: Add tracepoints for EXCHANGEID edge cases NFSD: Constify @fh argument of knfsd_fh_hash() NFSD: Capture every CB state transition NFSD: Drop TRACE_DEFINE_ENUM for NFSD4_CB_<state> macros NFSD: Add cb_lost tracepoint NFSD: Adjust cb_shutdown tracepoint NFSD: Enhance the nfsd_cb_setup tracepoint NFSD: Add an nfsd_cb_lm_notify tracepoint NFSD: Add an nfsd_cb_offload tracepoint NFSD: Replace the nfsd_deleg_break tracepoint NFSD: Add an nfsd_cb_probe tracepoint NFSD: Remove the nfsd_cb_work and nfsd_cb_done tracepoints NFSD: Update nfsd_cb_args tracepoint nfsd: Prevent truncation of an unlinked inode from blocking access to its directory nfsd: move some commit_metadata()s outside the inode lock NFSD add vfs_fsync after async copy is done NFSD: delay unmount source's export after inter-server copy completed. nfsd: move fsnotify on client creation outside spinlock nfsd4: Expose the callback address and state of each NFS4 client nfsd: fix kernel test robot warning in SSC code NFSD: Fix error return code in nfsd4_interssc_connect() nfsd: rpc_peeraddr2str needs rcu lock lockd: Remove stale comments lockd: Create a simplified .vs_dispatch method for NLM requests lockd: Common NLM XDR helpers lockd: Update the NLMv1 void argument decoder to use struct xdr_stream lockd: Update the NLMv1 TEST arguments decoder to use struct xdr_stream lockd: Update the NLMv1 LOCK arguments decoder to use struct xdr_stream lockd: Update the NLMv1 CANCEL arguments decoder to use struct xdr_stream lockd: Update the NLMv1 UNLOCK arguments decoder to use struct xdr_stream lockd: Update the NLMv1 nlm_res arguments decoder to use struct xdr_stream lockd: Update the NLMv1 SM_NOTIFY arguments decoder to use struct xdr_stream lockd: Update the NLMv1 SHARE arguments decoder to use struct xdr_stream lockd: Update the NLMv1 FREE_ALL arguments decoder to use struct xdr_stream lockd: Update the NLMv1 void results encoder to use struct xdr_stream lockd: Update the NLMv1 TEST results encoder to use struct xdr_stream lockd: Update the NLMv1 nlm_res results encoder to use struct xdr_stream lockd: Update the NLMv1 SHARE results encoder to use struct xdr_stream lockd: Update the NLMv4 void arguments decoder to use struct xdr_stream lockd: Update the NLMv4 TEST arguments decoder to use struct xdr_stream lockd: Update the NLMv4 LOCK arguments decoder to use struct xdr_stream lockd: Update the NLMv4 CANCEL arguments decoder to use struct xdr_stream lockd: Update the NLMv4 UNLOCK arguments decoder to use struct xdr_stream lockd: Update the NLMv4 nlm_res arguments decoder to use struct xdr_stream lockd: Update the NLMv4 SM_NOTIFY arguments decoder to use struct xdr_stream lockd: Update the NLMv4 SHARE arguments decoder to use struct xdr_stream lockd: Update the NLMv4 FREE_ALL arguments decoder to use struct xdr_stream lockd: Update the NLMv4 void results encoder to use struct xdr_stream lockd: Update the NLMv4 TEST results encoder to use struct xdr_stream lockd: Update the NLMv4 nlm_res results encoder to use struct xdr_stream lockd: Update the NLMv4 SHARE results encoder to use struct xdr_stream nfsd: remove redundant assignment to pointer 'this' NFSD: Prevent a possible oops in the nfs_dirent() tracepoint nfsd: fix NULL dereference in nfs3svc_encode_getaclres kernel/pid.c: remove static qualifier from pidfd_create() kernel/pid.c: implement additional checks upon pidfd_create() parameters fanotify: minor cosmetic adjustments to fid labels fanotify: introduce a generic info record copying helper fanotify: add pidfd support to the fanotify API fsnotify: replace igrab() with ihold() on attach connector fsnotify: count s_fsnotify_inode_refs for attached connectors fsnotify: count all objects with attached connectors fsnotify: optimize the case of no marks of any type NFSD: Clean up splice actor SUNRPC: Add svc_rqst_replace_page() API NFSD: Batch release pages during splice read NFSD: remove vanity comments sysctl: introduce new proc handler proc_dobool lockd: change the proc_handler for nsm_use_hostnames nlm: minor nlm_lookup_file argument change nlm: minor refactoring lockd: update nlm_lookup_file reexport comment Keep read and write fds with each nlm_file nfs: don't atempt blocking locks on nfs reexports lockd: don't attempt blocking locks on nfs reexports nfs: don't allow reexport reclaims SUNRPC: Add svc_rqst::rq_auth_stat SUNRPC: Set rq_auth_stat in the pg_authenticate() callout SUNRPC: Eliminate the RQ_AUTHERR flag NFS: Add a private local dispatcher for NFSv4 callback operations NFS: Remove unused callback void decoder fsnotify: fix sb_connectors leak NLM: Fix svcxdr_encode_owner() nfsd: Fix a warning for nfsd_file_close_inode fsnotify: pass data_type to fsnotify_name() fsnotify: pass dentry instead of inode data fsnotify: clarify contract for create event hooks fsnotify: Don't insert unmergeable events in hashtable fanotify: Fold event size calculation to its own function fanotify: Split fsid check from other fid mode checks inotify: Don't force FS_IN_IGNORED fsnotify: Add helper to detect overflow_event fsnotify: Add wrapper around fsnotify_add_event fsnotify: Retrieve super block from the data field fsnotify: Protect fsnotify_handle_inode_event from no-inode events fsnotify: Pass group argument to free_event fanotify: Support null inode event in fanotify_dfid_inode fanotify: Allow file handle encoding for unhashed events fanotify: Encode empty file handle when no inode is provided fanotify: Require fid_mode for any non-fd event fsnotify: Support FS_ERROR event type fanotify: Reserve UAPI bits for FAN_FS_ERROR fanotify: Pre-allocate pool of error events fanotify: Support enqueueing of error events fanotify: Support merging of error events fanotify: Wrap object_fh inline space in a creator macro fanotify: Add helpers to decide whether to report FID/DFID fanotify: WARN_ON against too large file handles fanotify: Report fid info for file related file system errors fanotify: Emit generic error info for error event fanotify: Allow users to request FAN_FS_ERROR events SUNRPC: Trace calls to .rpc_call_done NFSD: Optimize DRC bucket pruning NFSD: move filehandle format declarations out of "uapi". NFSD: drop support for ancient filehandles NFSD: simplify struct nfsfh NFSD: Initialize pointer ni with NULL and not plain integer 0 NFSD: Have legacy NFSD WRITE decoders use xdr_stream_subsegment() SUNRPC: Replace the "__be32 *p" parameter to .pc_decode SUNRPC: Change return value type of .pc_decode NFSD: Save location of NFSv4 COMPOUND status SUNRPC: Replace the "__be32 *p" parameter to .pc_encode SUNRPC: Change return value type of .pc_encode nfsd: update create verifier comment NFSD:fix boolreturn.cocci warning nfsd4: remove obselete comment NFSD: Fix exposure in nfsd4_decode_bitmap() NFSD: Fix READDIR buffer overflow fsnotify: clarify object type argument fsnotify: separate mark iterator type from object type enum fanotify: introduce group flag FAN_REPORT_TARGET_FID fsnotify: generate FS_RENAME event with rich information fanotify: use macros to get the offset to fanotify_info buffer fanotify: use helpers to parcel fanotify_info buffer fanotify: support secondary dir fh and name in fanotify_info fanotify: record old and new parent and name in FAN_RENAME event fanotify: record either old name new name or both for FAN_RENAME fanotify: report old and/or new parent+name in FAN_RENAME event fanotify: wire up FAN_RENAME event exit: Implement kthread_exit exit: Rename module_put_and_exit to module_put_and_kthread_exit NFSD: Fix sparse warning NFSD: handle errors better in write_ports_addfd() SUNRPC: change svc_get() to return the svc. SUNRPC/NFSD: clean up get/put functions. SUNRPC: stop using ->sv_nrthreads as a refcount nfsd: make nfsd_stats.th_cnt atomic_t SUNRPC: use sv_lock to protect updates to sv_nrthreads. NFSD: narrow nfsd_mutex protection in nfsd thread NFSD: Make it possible to use svc_set_num_threads_sync SUNRPC: discard svo_setup and rename svc_set_num_threads_sync() NFSD: simplify locking for network notifier. lockd: introduce nlmsvc_serv lockd: simplify management of network status notifiers lockd: move lockd_start_svc() call into lockd_create_svc() lockd: move svc_exit_thread() into the thread lockd: introduce lockd_put() lockd: rename lockd_create_svc() to lockd_get() SUNRPC: move the pool_map definitions (back) into svc.c SUNRPC: always treat sv_nrpools==1 as "not pooled" lockd: use svc_set_num_threads() for thread start and stop NFS: switch the callback service back to non-pooled. NFSD: Remove be32_to_cpu() from DRC hash function NFSD: Fix inconsistent indenting NFSD: simplify per-net file cache management NFSD: Combine XDR error tracepoints nfsd: improve stateid access bitmask documentation NFSD: De-duplicate nfsd4_decode_bitmap4() nfs: block notification on fs with its own ->lock nfsd4: add refcount for nfsd4_blocked_lock NFSD: Fix zero-length NFSv3 WRITEs nfsd: map EBADF nfsd: Add errno mapping for EREMOTEIO nfsd: Retry once in nfsd_open on an -EOPENSTALE return NFSD: Clean up nfsd_vfs_write() NFSD: De-duplicate net_generic(SVC_NET(rqstp), nfsd_net_id) NFSD: De-duplicate net_generic(nf->nf_net, nfsd_net_id) nfsd: Add a tracepoint for errors in nfsd4_clone_file_range() NFSD: Write verifier might go backwards NFSD: Clean up the nfsd_net::nfssvc_boot field NFSD: Rename boot verifier functions NFSD: Trace boot verifier resets Revert "nfsd: skip some unnecessary stats in the v4 case" NFSD: Move fill_pre_wcc() and fill_post_wcc() nfsd: fix crash on COPY_NOTIFY with special stateid fanotify: remove variable set but not used lockd: fix server crash on reboot of client holding lock lockd: fix failure to cleanup client locks NFSD: Fix the behavior of READ near OFFSET_MAX NFSD: Fix ia_size underflow NFSD: Fix NFSv3 SETATTR/CREATE's handling of large file sizes NFSD: COMMIT operations must not return NFS?ERR_INVAL NFSD: Deprecate NFS_OFFSET_MAX nfsd: Add support for the birth time attribute NFSD: De-duplicate hash bucket indexing NFSD: Skip extra computation for RC_NOCACHE case NFSD: Streamline the rare "found" case SUNRPC: Remove the .svo_enqueue_xprt method SUNRPC: Merge svc_do_enqueue_xprt() into svc_enqueue_xprt() SUNRPC: Remove svo_shutdown method SUNRPC: Rename svc_create_xprt() SUNRPC: Rename svc_close_xprt() SUNRPC: Remove svc_shutdown_net() NFSD: Remove svc_serv_ops::svo_module NFSD: Move svc_serv_ops::svo_function into struct svc_serv NFSD: Remove CONFIG_NFSD_V3 NFSD: Clean up _lm_ operation names nfsd: fix using the correct variable for sizeof() fsnotify: fix merge with parent's ignored mask fsnotify: optimize FS_MODIFY events with no ignored masks fsnotify: remove redundant parameter judgment SUNRPC: Return true/false (not 1/0) from bool functions nfsd: Fix a write performance regression nfsd: Clean up nfsd_file_put() fanotify: do not allow setting dirent events in mask of non-dir fs/lock: documentation cleanup. Replace inode->i_lock with flc_lock. inotify: move control flags from mask to mark flags fsnotify: pass flags argument to fsnotify_alloc_group() fsnotify: make allow_dups a property of the group fsnotify: create helpers for group mark_mutex lock inotify: use fsnotify group lock helpers nfsd: use fsnotify group lock helpers dnotify: use fsnotify group lock helpers fsnotify: allow adding an inode mark without pinning inode fanotify: create helper fanotify_mark_user_flags() fanotify: factor out helper fanotify_mark_update_flags() fanotify: implement "evictable" inode marks fanotify: use fsnotify group lock helpers fanotify: enable "evictable" inode marks fsnotify: introduce mark type iterator fsnotify: consistent behavior for parent not watching children fanotify: fix incorrect fmode_t casts NFSD: Clean up nfsd_splice_actor() NFSD: add courteous server support for thread with only delegation NFSD: add support for share reservation conflict to courteous server NFSD: move create/destroy of laundry_wq to init_nfsd and exit_nfsd fs/lock: add helper locks_owner_has_blockers to check for blockers fs/lock: add 2 callbacks to lock_manager_operations to resolve conflict NFSD: add support for lock conflict to courteous server NFSD: Show state of courtesy client in client info NFSD: Clean up nfsd3_proc_create() NFSD: Avoid calling fh_drop_write() twice in do_nfsd_create() NFSD: Refactor nfsd_create_setattr() NFSD: Refactor NFSv3 CREATE NFSD: Refactor NFSv4 OPEN(CREATE) NFSD: Remove do_nfsd_create() NFSD: Clean up nfsd_open_verified() NFSD: Instantiate a struct file when creating a regular NFSv4 file NFSD: Remove dprintk call sites from tail of nfsd4_open() NFSD: Fix whitespace NFSD: Move documenting comment for nfsd4_process_open2() NFSD: Trace filecache opens NFSD: Clean up the show_nf_flags() macro SUNRPC: Use RMW bitops in single-threaded hot paths nfsd: Unregister the cld notifier when laundry_wq create failed nfsd: Fix null-ptr-deref in nfsd_fill_super() nfsd: destroy percpu stats counters after reply cache shutdown NFSD: Modernize nfsd4_release_lockowner() NFSD: Add documenting comment for nfsd4_release_lockowner() NFSD: nfsd_file_put() can sleep NFSD: Fix potential use-after-free in nfsd_file_put() SUNRPC: Optimize xdr_reserve_space() fanotify: refine the validation checks on non-dir inode mask NFS: restore module put when manager exits. NFSD: Decode NFSv4 birth time attribute lockd: set fl_owner when unlocking files lockd: fix nlm_close_files fs: inotify: Fix typo in inotify comment fanotify: prepare for setting event flags in ignore mask fanotify: cleanups for fanotify_mark() input validations fanotify: introduce FAN_MARK_IGNORE fsnotify: Fix comment typo nfsd: eliminate the NFSD_FILE_BREAK_* flags SUNRPC: Fix xdr_encode_bool() NLM: Defend against file_lock changes after vfs_test_lock() NFSD: Fix space and spelling mistake nfsd: remove redundant assignment to variable len NFSD: Demote a WARN to a pr_warn() NFSD: Report filecache LRU size NFSD: Report count of calls to nfsd_file_acquire() NFSD: Report count of freed filecache items NFSD: Report average age of filecache items NFSD: Add nfsd_file_lru_dispose_list() helper NFSD: Refactor nfsd_file_gc() NFSD: Refactor nfsd_file_lru_scan() NFSD: Report the number of items evicted by the LRU walk NFSD: Record number of flush calls NFSD: Zero counters when the filecache is re-initialized NFSD: Hook up the filecache stat file NFSD: WARN when freeing an item still linked via nf_lru NFSD: Trace filecache LRU activity NFSD: Leave open files out of the filecache LRU NFSD: Fix the filecache LRU shrinker NFSD: Never call nfsd_file_gc() in foreground paths NFSD: No longer record nf_hashval in the trace log NFSD: Remove lockdep assertion from unhash_and_release_locked() NFSD: nfsd_file_unhash can compute hashval from nf->nf_inode NFSD: Refactor __nfsd_file_close_inode() NFSD: nfsd_file_hash_remove can compute hashval NFSD: Remove nfsd_file::nf_hashval NFSD: Replace the "init once" mechanism NFSD: Set up an rhashtable for the filecache NFSD: Convert the filecache to use rhashtable NFSD: Clean up unused code after rhashtable conversion NFSD: Separate tracepoints for acquire and create NFSD: Move nfsd_file_trace_alloc() tracepoint NFSD: NFSv4 CLOSE should release an nfsd_file immediately NFSD: Ensure nf_inode is never dereferenced NFSD: refactoring v4 specific code to a helper in nfs4state.c NFSD: keep track of the number of v4 clients in the system NFSD: limit the number of v4 clients to 1024 per 1GB of system memory nfsd: silence extraneous printk on nfsd.ko insertion NFSD: Optimize nfsd4_encode_operation() NFSD: Optimize nfsd4_encode_fattr() NFSD: Clean up SPLICE_OK in nfsd4_encode_read() NFSD: Add an nfsd4_read::rd_eof field NFSD: Optimize nfsd4_encode_readv() NFSD: Simplify starting_len NFSD: Use xdr_pad_size() NFSD: Clean up nfsd4_encode_readlink() NFSD: Fix strncpy() fortify warning NFSD: nfserrno(-ENOMEM) is nfserr_jukebox NFSD: Shrink size of struct nfsd4_copy_notify NFSD: Shrink size of struct nfsd4_copy NFSD: Reorder the fields in struct nfsd4_op NFSD: Make nfs4_put_copy() static NFSD: Replace boolean fields in struct nfsd4_copy NFSD: Refactor nfsd4_cleanup_inter_ssc() (1/2) NFSD: Refactor nfsd4_cleanup_inter_ssc() (2/2) NFSD: Refactor nfsd4_do_copy() NFSD: Remove kmalloc from nfsd4_do_async_copy() NFSD: Add nfsd4_send_cb_offload() NFSD: Move copy offload callback arguments into a separate structure NFSD: drop fh argument from alloc_init_deleg NFSD: verify the opened dentry after setting a delegation NFSD: introduce struct nfsd_attrs NFSD: set attributes when creating symlinks NFSD: add security label to struct nfsd_attrs NFSD: add posix ACLs to struct nfsd_attrs NFSD: change nfsd_create()/nfsd_symlink() to unlock directory before returning. NFSD: always drop directory lock in nfsd_unlink() NFSD: only call fh_unlock() once in nfsd_link() NFSD: reduce locking in nfsd_lookup() NFSD: use explicit lock/unlock for directory ops NFSD: use (un)lock_inode instead of fh_(un)lock for file operations NFSD: discard fh_locked flag and fh_lock/fh_unlock lockd: detect and reject lock arguments that overflow NFSD: fix regression with setting ACLs. nfsd_splice_actor(): handle compound pages NFSD: move from strlcpy with unused retval to strscpy lockd: move from strlcpy with unused retval to strscpy NFSD enforce filehandle check for source file in COPY NFSD: remove redundant variable status nfsd: Avoid some useless tests nfsd: Propagate some error code returned by memdup_user() NFSD: Increase NFSD_MAX_OPS_PER_COMPOUND NFSD: Protect against send buffer overflow in NFSv2 READDIR NFSD: Protect against send buffer overflow in NFSv3 READDIR NFSD: Protect against send buffer overflow in NFSv2 READ NFSD: Protect against send buffer overflow in NFSv3 READ NFSD: drop fname and flen args from nfsd_create_locked() NFSD: Fix handling of oversized NFSv4 COMPOUND requests nfsd: clean up mounted_on_fileid handling nfsd: remove nfsd4_prepare_cb_recall() declaration NFSD: Add tracepoints to report NFSv4 callback completions NFSD: Add a mechanism to wait for a DELEGRETURN NFSD: Refactor nfsd_setattr() NFSD: Make nfsd4_setattr() wait before returning NFS4ERR_DELAY NFSD: Make nfsd4_rename() wait before returning NFS4ERR_DELAY NFSD: Make nfsd4_remove() wait before returning NFS4ERR_DELAY NFSD: keep track of the number of courtesy clients in the system NFSD: add shrinker to reap courtesy clients on low memory condition SUNRPC: Parametrize how much of argsize should be zeroed NFSD: Reduce amount of struct nfsd4_compoundargs that needs clearing NFSD: Refactor common code out of dirlist helpers NFSD: Use xdr_inline_decode() to decode NFSv3 symlinks NFSD: Clean up WRITE arg decoders NFSD: Clean up nfs4svc_encode_compoundres() NFSD: Remove "inline" directives on op_rsize_bop helpers NFSD: Remove unused nfsd4_compoundargs::cachetype field NFSD: Pack struct nfsd4_compoundres nfsd: use DEFINE_PROC_SHOW_ATTRIBUTE to define nfsd_proc_ops nfsd: use DEFINE_SHOW_ATTRIBUTE to define export_features_fops and supported_enctypes_fops nfsd: use DEFINE_SHOW_ATTRIBUTE to define client_info_fops nfsd: use DEFINE_SHOW_ATTRIBUTE to define nfsd_reply_cache_stats_fops nfsd: use DEFINE_SHOW_ATTRIBUTE to define nfsd_file_cache_stats_fops NFSD: Rename the fields in copy_stateid_t NFSD: Cap rsize_bop result based on send buffer size nfsd: only fill out return pointer on success in nfsd4_lookup_stateid nfsd: fix comments about spinlock handling with delegations nfsd: make nfsd4_run_cb a bool return function nfsd: extra checks when freeing delegation stateids fs/notify: constify path fsnotify: remove unused declaration fanotify: Remove obsoleted fanotify_event_has_path() nfsd: fix nfsd_file_unhash_and_dispose nfsd: rework hashtable handling in nfsd_do_file_acquire NFSD: unregister shrinker when nfsd_init_net() fails nfsd: fix net-namespace logic in __nfsd_file_cache_purge nfsd: fix use-after-free in nfsd_file_do_acquire tracepoint nfsd: put the export reference in nfsd4_verify_deleg_dentry NFSD: Fix reads with a non-zero offset that don't end on a page boundary filelock: add a new locks_inode_context accessor function lockd: use locks_inode_context helper nfsd: use locks_inode_context helper NFSD: Simplify READ_PLUS NFSD: Remove redundant assignment to variable host_err NFSD: Finish converting the NFSv2 GETACL result encoder NFSD: Finish converting the NFSv3 GETACL result encoder nfsd: ignore requests to disable unsupported versions nfsd: move nfserrno() to vfs.c nfsd: allow disabling NFSv2 at compile time exportfs: use pr_debug for unreachable debug statements NFSD: Pass the target nfsd_file to nfsd_commit() NFSD: Revert "NFSD: NFSv4 CLOSE should release an nfsd_file immediately" NFSD: Add an NFSD_FILE_GC flag to enable nfsd_file garbage collection NFSD: Flesh out a documenting comment for filecache.c NFSD: Clean up nfs4_preprocess_stateid_op() call sites NFSD: Trace stateids returned via DELEGRETURN NFSD: Trace delegation revocations NFSD: Use const pointers as parameters to fh_ helpers NFSD: Update file_hashtbl() helpers NFSD: Clean up nfsd4_init_file() NFSD: Add a nfsd4_file_hash_remove() helper NFSD: Clean up find_or_add_file() NFSD: Refactor find_file() NFSD: Use rhashtable for managing nfs4_file objects NFSD: Fix licensing header in filecache.c nfsd: remove the pages_flushed statistic from filecache nfsd: reorganize filecache.c nfsd: fix up the filecache laundrette scheduling NFSD: Add an nfsd_file_fsync tracepoint lockd: set other missing fields when unlocking files nfsd: return error if nfs4_setacl fails NFSD: Use struct_size() helper in alloc_session() lockd: set missing fl_flags field when retrieving args lockd: ensure we use the correct file descriptor when unlocking lockd: fix file selection in nlmsvc_cancel_blocked NFSD: pass range end to vfs_fsync_range() instead of count NFSD: refactoring courtesy_client_reaper to a generic low memory shrinker NFSD: add support for sending CB_RECALL_ANY NFSD: add delegation reaper to react to low memory condition NFSD: Use only RQ_DROPME to signal the need to drop a reply NFSD: Avoid clashing function prototypes nfsd: rework refcounting in filecache nfsd: fix handling of cached open files in nfsd4_open codepath Revert "SUNRPC: Use RMW bitops in single-threaded hot paths" NFSD: Use set_bit(RQ_DROPME) NFSD: fix use-after-free in nfsd4_ssc_setup_dul() NFSD: register/unregister of nfsd-client shrinker at nfsd startup/shutdown time NFSD: replace delayed_work with work_struct for nfsd_client_shrinker nfsd: don't free files unconditionally in __nfsd_file_cache_purge nfsd: don't destroy global nfs4_file table in per-net shutdown NFSD: enhance inter-server copy cleanup nfsd: allow nfsd_file_get to sanely handle a NULL pointer nfsd: clean up potential nfsd_file refcount leaks in COPY codepath NFSD: fix leaked reference count of nfsd4_ssc_umount_item nfsd: don't hand out delegation on setuid files being opened for write NFSD: fix problems with cleanup on errors in nfsd4_copy nfsd: fix courtesy client with deny mode handling in nfs4_upgrade_open nfsd: don't fsync nfsd_files on last close NFSD: copy the whole verifier in nfsd_copy_write_verifier NFSD: Protect against filesystem freezing lockd: set file_lock start and end when decoding nlm4 testargs nfsd: don't replace page in rq_pages if it's a continuation of last page NFSD: Avoid calling OPDESC() with ops->opnum == OP_ILLEGAL nfsd: call op_release, even when op_func returns an error nfsd: don't open-code clear_and_wake_up_bit nfsd: NFSD_FILE_KEY_INODE only needs to find GC'ed entries nfsd: simplify test_bit return in NFSD_FILE_KEY_FULL comparator nfsd: don't kill nfsd_files because of lease break error nfsd: add some comments to nfsd_file_do_acquire nfsd: don't take/put an extra reference when putting a file nfsd: update comment over __nfsd_file_cache_purge nfsd: allow reaping files still under writeback NFSD: Convert filecache to rhltable nfsd: simplify the delayed disposal list code NFSD: Fix problem of COMMIT and NFS4ERR_DELAY in infinite loop nfsd: make a copy of struct iattr before calling notify_change nfsd: fix double fget() bug in __write_ports_addfd() lockd: drop inappropriate svc_get() from locked_get() NFSD: Add an nfsd4_encode_nfstime4() helper nfsd: Fix creation time serialization order nfsd: don't allow nfsd threads to be signalled. nfsd: Simplify code around svc_exit_thread() call in nfsd() nfsd: separate nfsd_last_thread() from nfsd_put() Documentation: Add missing documentation for EXPORT_OP flags NFSD: fix possible oops when nfsd/pool_stats is closed. nfsd: call nfsd_last_thread() before final nfsd_put() nfsd: drop the nfsd_put helper nfsd: fix RELEASE_LOCKOWNER nfsd: don't take fi_lock in nfsd_break_deleg_cb() nfsd: don't call locks_release_private() twice concurrently nfsd: Fix a regression in nfsd_setattr() Linux 5.10.220 Change-Id: I589ec5e63d1f985ab69f9755b9a87330627d44c5 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
commit
87a7f35a24
@ -62,7 +62,7 @@ the fdtable structure -
|
||||
be held.
|
||||
|
||||
4. To look up the file structure given an fd, a reader
|
||||
must use either fcheck() or fcheck_files() APIs. These
|
||||
must use either lookup_fd_rcu() or files_lookup_fd_rcu() APIs. These
|
||||
take care of barrier requirements due to lock-free lookup.
|
||||
|
||||
An example::
|
||||
@ -70,7 +70,7 @@ the fdtable structure -
|
||||
struct file *file;
|
||||
|
||||
rcu_read_lock();
|
||||
file = fcheck(fd);
|
||||
file = lookup_fd_rcu(fd);
|
||||
if (file) {
|
||||
...
|
||||
}
|
||||
@ -84,7 +84,7 @@ the fdtable structure -
|
||||
on ->f_count::
|
||||
|
||||
rcu_read_lock();
|
||||
file = fcheck_files(files, fd);
|
||||
file = files_lookup_fd_rcu(files, fd);
|
||||
if (file) {
|
||||
if (atomic_long_inc_not_zero(&file->f_count))
|
||||
*fput_needed = 1;
|
||||
@ -104,7 +104,7 @@ the fdtable structure -
|
||||
lock-free, they must be installed using rcu_assign_pointer()
|
||||
API. If they are looked up lock-free, rcu_dereference()
|
||||
must be used. However it is advisable to use files_fdtable()
|
||||
and fcheck()/fcheck_files() which take care of these issues.
|
||||
and lookup_fd_rcu()/files_lookup_fd_rcu() which take care of these issues.
|
||||
|
||||
7. While updating, the fdtable pointer must be looked up while
|
||||
holding files->file_lock. If ->file_lock is dropped, then
|
||||
|
@ -433,17 +433,21 @@ prototypes::
|
||||
void (*lm_break)(struct file_lock *); /* break_lease callback */
|
||||
int (*lm_change)(struct file_lock **, int);
|
||||
bool (*lm_breaker_owns_lease)(struct file_lock *);
|
||||
bool (*lm_lock_expirable)(struct file_lock *);
|
||||
void (*lm_expire_lock)(void);
|
||||
|
||||
locking rules:
|
||||
|
||||
====================== ============= ================= =========
|
||||
ops inode->i_lock blocked_lock_lock may block
|
||||
ops flc_lock blocked_lock_lock may block
|
||||
====================== ============= ================= =========
|
||||
lm_notify: yes yes no
|
||||
lm_notify: no yes no
|
||||
lm_grant: no no no
|
||||
lm_break: yes no no
|
||||
lm_change yes no no
|
||||
lm_breaker_owns_lease: no no no
|
||||
lm_breaker_owns_lease: yes no no
|
||||
lm_lock_expirable yes no no
|
||||
lm_expire_lock no no yes
|
||||
====================== ============= ================= =========
|
||||
|
||||
buffer_head
|
||||
|
@ -154,6 +154,11 @@ struct which has the following members:
|
||||
to find potential names, and matches inode numbers to find the correct
|
||||
match.
|
||||
|
||||
flags
|
||||
Some filesystems may need to be handled differently than others. The
|
||||
export_operations struct also includes a flags field that allows the
|
||||
filesystem to communicate such information to nfsd. See the Export
|
||||
Operations Flags section below for more explanation.
|
||||
|
||||
A filehandle fragment consists of an array of 1 or more 4byte words,
|
||||
together with a one byte "type".
|
||||
@ -163,3 +168,76 @@ generated by encode_fh, in which case it will have been padded with
|
||||
nuls. Rather, the encode_fh routine should choose a "type" which
|
||||
indicates the decode_fh how much of the filehandle is valid, and how
|
||||
it should be interpreted.
|
||||
|
||||
Export Operations Flags
|
||||
-----------------------
|
||||
In addition to the operation vector pointers, struct export_operations also
|
||||
contains a "flags" field that allows the filesystem to communicate to nfsd
|
||||
that it may want to do things differently when dealing with it. The
|
||||
following flags are defined:
|
||||
|
||||
EXPORT_OP_NOWCC - disable NFSv3 WCC attributes on this filesystem
|
||||
RFC 1813 recommends that servers always send weak cache consistency
|
||||
(WCC) data to the client after each operation. The server should
|
||||
atomically collect attributes about the inode, do an operation on it,
|
||||
and then collect the attributes afterward. This allows the client to
|
||||
skip issuing GETATTRs in some situations but means that the server
|
||||
is calling vfs_getattr for almost all RPCs. On some filesystems
|
||||
(particularly those that are clustered or networked) this is expensive
|
||||
and atomicity is difficult to guarantee. This flag indicates to nfsd
|
||||
that it should skip providing WCC attributes to the client in NFSv3
|
||||
replies when doing operations on this filesystem. Consider enabling
|
||||
this on filesystems that have an expensive ->getattr inode operation,
|
||||
or when atomicity between pre and post operation attribute collection
|
||||
is impossible to guarantee.
|
||||
|
||||
EXPORT_OP_NOSUBTREECHK - disallow subtree checking on this fs
|
||||
Many NFS operations deal with filehandles, which the server must then
|
||||
vet to ensure that they live inside of an exported tree. When the
|
||||
export consists of an entire filesystem, this is trivial. nfsd can just
|
||||
ensure that the filehandle live on the filesystem. When only part of a
|
||||
filesystem is exported however, then nfsd must walk the ancestors of the
|
||||
inode to ensure that it's within an exported subtree. This is an
|
||||
expensive operation and not all filesystems can support it properly.
|
||||
This flag exempts the filesystem from subtree checking and causes
|
||||
exportfs to get back an error if it tries to enable subtree checking
|
||||
on it.
|
||||
|
||||
EXPORT_OP_CLOSE_BEFORE_UNLINK - always close cached files before unlinking
|
||||
On some exportable filesystems (such as NFS) unlinking a file that
|
||||
is still open can cause a fair bit of extra work. For instance,
|
||||
the NFS client will do a "sillyrename" to ensure that the file
|
||||
sticks around while it's still open. When reexporting, that open
|
||||
file is held by nfsd so we usually end up doing a sillyrename, and
|
||||
then immediately deleting the sillyrenamed file just afterward when
|
||||
the link count actually goes to zero. Sometimes this delete can race
|
||||
with other operations (for instance an rmdir of the parent directory).
|
||||
This flag causes nfsd to close any open files for this inode _before_
|
||||
calling into the vfs to do an unlink or a rename that would replace
|
||||
an existing file.
|
||||
|
||||
EXPORT_OP_REMOTE_FS - Backing storage for this filesystem is remote
|
||||
PF_LOCAL_THROTTLE exists for loopback NFSD, where a thread needs to
|
||||
write to one bdi (the final bdi) in order to free up writes queued
|
||||
to another bdi (the client bdi). Such threads get a private balance
|
||||
of dirty pages so that dirty pages for the client bdi do not imact
|
||||
the daemon writing to the final bdi. For filesystems whose durable
|
||||
storage is not local (such as exported NFS filesystems), this
|
||||
constraint has negative consequences. EXPORT_OP_REMOTE_FS enables
|
||||
an export to disable writeback throttling.
|
||||
|
||||
EXPORT_OP_NOATOMIC_ATTR - Filesystem does not update attributes atomically
|
||||
EXPORT_OP_NOATOMIC_ATTR indicates that the exported filesystem
|
||||
cannot provide the semantics required by the "atomic" boolean in
|
||||
NFSv4's change_info4. This boolean indicates to a client whether the
|
||||
returned before and after change attributes were obtained atomically
|
||||
with the respect to the requested metadata operation (UNLINK,
|
||||
OPEN/CREATE, MKDIR, etc).
|
||||
|
||||
EXPORT_OP_FLUSH_ON_CLOSE - Filesystem flushes file data on close(2)
|
||||
On most filesystems, inodes can remain under writeback after the
|
||||
file is closed. NFSD relies on client activity or local flusher
|
||||
threads to handle writeback. Certain filesystems, such as NFS, flush
|
||||
all of an inode's dirty data on last close. Exports that behave this
|
||||
way should set EXPORT_OP_FLUSH_ON_CLOSE so that NFSD knows to skip
|
||||
waiting for writeback when closing such files.
|
||||
|
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
VERSION = 5
|
||||
PATCHLEVEL = 10
|
||||
SUBLEVEL = 219
|
||||
SUBLEVEL = 220
|
||||
EXTRAVERSION =
|
||||
NAME = Dare mighty things
|
||||
|
||||
|
@ -74,7 +74,7 @@ static struct spu_context *coredump_next_context(int *fd)
|
||||
*fd = n - 1;
|
||||
|
||||
rcu_read_lock();
|
||||
file = fcheck(*fd);
|
||||
file = lookup_fd_rcu(*fd);
|
||||
ctx = SPUFS_I(file_inode(file))->i_ctx;
|
||||
get_spu_context(ctx);
|
||||
rcu_read_unlock();
|
||||
|
@ -74,7 +74,7 @@ static int cryptomgr_probe(void *data)
|
||||
complete_all(¶m->larval->completion);
|
||||
crypto_alg_put(¶m->larval->alg);
|
||||
kfree(param);
|
||||
module_put_and_exit(0);
|
||||
module_put_and_kthread_exit(0);
|
||||
}
|
||||
|
||||
static int cryptomgr_schedule_probe(struct crypto_larval *larval)
|
||||
@ -209,7 +209,7 @@ static int cryptomgr_test(void *data)
|
||||
crypto_alg_tested(param->driver, err);
|
||||
|
||||
kfree(param);
|
||||
module_put_and_exit(0);
|
||||
module_put_and_kthread_exit(0);
|
||||
}
|
||||
|
||||
static int cryptomgr_schedule_test(struct crypto_alg *alg)
|
||||
|
@ -321,7 +321,7 @@ config LOCKD
|
||||
|
||||
config LOCKD_V4
|
||||
bool
|
||||
depends on NFSD_V3 || NFS_V3
|
||||
depends on NFSD || NFS_V3
|
||||
depends on FILE_LOCKING
|
||||
default y
|
||||
|
||||
@ -334,6 +334,10 @@ config NFS_COMMON
|
||||
depends on NFSD || NFS_FS || LOCKD
|
||||
default y
|
||||
|
||||
config NFS_V4_2_SSC_HELPER
|
||||
bool
|
||||
default y if NFS_V4_2
|
||||
|
||||
source "net/sunrpc/Kconfig"
|
||||
source "fs/ceph/Kconfig"
|
||||
source "fs/cifs/Kconfig"
|
||||
|
@ -4,9 +4,10 @@
|
||||
* Copyright 2008 Ian Kent <raven@themaw.net>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/nospec.h>
|
||||
|
||||
@ -289,7 +290,7 @@ static int autofs_dev_ioctl_closemount(struct file *fp,
|
||||
struct autofs_sb_info *sbi,
|
||||
struct autofs_dev_ioctl *param)
|
||||
{
|
||||
return ksys_close(param->ioctlfd);
|
||||
return close_fd(param->ioctlfd);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -412,9 +412,14 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
|
||||
if (ret < 0) {
|
||||
cachefiles_io_error(cache, "Rename security error %d", ret);
|
||||
} else {
|
||||
struct renamedata rd = {
|
||||
.old_dir = d_inode(dir),
|
||||
.old_dentry = rep,
|
||||
.new_dir = d_inode(cache->graveyard),
|
||||
.new_dentry = grave,
|
||||
};
|
||||
trace_cachefiles_rename(object, rep, grave, why);
|
||||
ret = vfs_rename(d_inode(dir), rep,
|
||||
d_inode(cache->graveyard), grave, NULL, 0);
|
||||
ret = vfs_rename(&rd);
|
||||
if (ret != 0 && ret != -ENOMEM)
|
||||
cachefiles_io_error(cache,
|
||||
"Rename failed with error %d", ret);
|
||||
|
@ -1242,7 +1242,7 @@ cifs_demultiplex_thread(void *p)
|
||||
}
|
||||
|
||||
memalloc_noreclaim_restore(noreclaim_flag);
|
||||
module_put_and_exit(0);
|
||||
module_put_and_kthread_exit(0);
|
||||
}
|
||||
|
||||
/* extract the host portion of the UNC string */
|
||||
|
@ -587,7 +587,6 @@ void do_coredump(const kernel_siginfo_t *siginfo)
|
||||
int ispipe;
|
||||
size_t *argv = NULL;
|
||||
int argc = 0;
|
||||
struct files_struct *displaced;
|
||||
/* require nonrelative corefile path and be extra careful */
|
||||
bool need_suid_safe = false;
|
||||
bool core_dumped = false;
|
||||
@ -793,11 +792,9 @@ void do_coredump(const kernel_siginfo_t *siginfo)
|
||||
}
|
||||
|
||||
/* get us an unshared descriptor table; almost always a no-op */
|
||||
retval = unshare_files(&displaced);
|
||||
retval = unshare_files();
|
||||
if (retval)
|
||||
goto close_fail;
|
||||
if (displaced)
|
||||
put_files_struct(displaced);
|
||||
if (!dump_interrupted()) {
|
||||
/*
|
||||
* umh disabled with CONFIG_STATIC_USERMODEHELPER_PATH="" would
|
||||
|
@ -598,6 +598,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct dentry *lower_new_dir_dentry;
|
||||
struct dentry *trap;
|
||||
struct inode *target_inode;
|
||||
struct renamedata rd = {};
|
||||
|
||||
if (flags)
|
||||
return -EINVAL;
|
||||
@ -627,9 +628,12 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
rc = -ENOTEMPTY;
|
||||
goto out_lock;
|
||||
}
|
||||
rc = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
|
||||
d_inode(lower_new_dir_dentry), lower_new_dentry,
|
||||
NULL, 0);
|
||||
|
||||
rd.old_dir = d_inode(lower_old_dir_dentry);
|
||||
rd.old_dentry = lower_old_dentry;
|
||||
rd.new_dir = d_inode(lower_new_dir_dentry);
|
||||
rd.new_dentry = lower_new_dentry;
|
||||
rc = vfs_rename(&rd);
|
||||
if (rc)
|
||||
goto out_lock;
|
||||
if (target_inode)
|
||||
|
29
fs/exec.c
29
fs/exec.c
@ -1266,6 +1266,11 @@ int begin_new_exec(struct linux_binprm * bprm)
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
/* Ensure the files table is not shared. */
|
||||
retval = unshare_files();
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Must be called _before_ exec_mmap() as bprm->mm is
|
||||
* not visibile until then. This also enables the update
|
||||
@ -1791,7 +1796,6 @@ static int bprm_execve(struct linux_binprm *bprm,
|
||||
int fd, struct filename *filename, int flags)
|
||||
{
|
||||
struct file *file;
|
||||
struct files_struct *displaced;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
@ -1799,13 +1803,9 @@ static int bprm_execve(struct linux_binprm *bprm,
|
||||
*/
|
||||
io_uring_task_cancel();
|
||||
|
||||
retval = unshare_files(&displaced);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = prepare_bprm_creds(bprm);
|
||||
if (retval)
|
||||
goto out_files;
|
||||
return retval;
|
||||
|
||||
check_unsafe_exec(bprm);
|
||||
current->in_execve = 1;
|
||||
@ -1820,11 +1820,14 @@ static int bprm_execve(struct linux_binprm *bprm,
|
||||
bprm->file = file;
|
||||
/*
|
||||
* Record that a name derived from an O_CLOEXEC fd will be
|
||||
* inaccessible after exec. Relies on having exclusive access to
|
||||
* current->files (due to unshare_files above).
|
||||
* inaccessible after exec. This allows the code in exec to
|
||||
* choose to fail when the executable is not mmaped into the
|
||||
* interpreter and an open file descriptor is not passed to
|
||||
* the interpreter. This makes for a better user experience
|
||||
* than having the interpreter start and then immediately fail
|
||||
* when it finds the executable is inaccessible.
|
||||
*/
|
||||
if (bprm->fdpath &&
|
||||
close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
|
||||
if (bprm->fdpath && get_close_on_exec(fd))
|
||||
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
|
||||
|
||||
/* Set the unchanging part of bprm->cred */
|
||||
@ -1842,8 +1845,6 @@ static int bprm_execve(struct linux_binprm *bprm,
|
||||
rseq_execve(current);
|
||||
acct_update_integrals(current);
|
||||
task_numa_free(current, false);
|
||||
if (displaced)
|
||||
put_files_struct(displaced);
|
||||
return retval;
|
||||
|
||||
out:
|
||||
@ -1860,10 +1861,6 @@ static int bprm_execve(struct linux_binprm *bprm,
|
||||
current->fs->in_exec = 0;
|
||||
current->in_execve = 0;
|
||||
|
||||
out_files:
|
||||
if (displaced)
|
||||
reset_files_struct(displaced);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
|
||||
#define dprintk(fmt, args...) do{}while(0)
|
||||
#define dprintk(fmt, args...) pr_debug(fmt, ##args)
|
||||
|
||||
|
||||
static int get_name(const struct path *path, char *name, struct dentry *child);
|
||||
@ -132,8 +132,8 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
|
||||
inode_unlock(dentry->d_inode);
|
||||
|
||||
if (IS_ERR(parent)) {
|
||||
dprintk("%s: get_parent of %ld failed, err %d\n",
|
||||
__func__, dentry->d_inode->i_ino, PTR_ERR(parent));
|
||||
dprintk("get_parent of %lu failed, err %ld\n",
|
||||
dentry->d_inode->i_ino, PTR_ERR(parent));
|
||||
return parent;
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
|
||||
dprintk("%s: found name: %s\n", __func__, nbuf);
|
||||
tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf));
|
||||
if (IS_ERR(tmp)) {
|
||||
dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp));
|
||||
dprintk("lookup failed: %ld\n", PTR_ERR(tmp));
|
||||
err = PTR_ERR(tmp);
|
||||
goto out_err;
|
||||
}
|
||||
@ -417,9 +417,11 @@ int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exportfs_encode_fh);
|
||||
|
||||
struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
|
||||
int fh_len, int fileid_type,
|
||||
int (*acceptable)(void *, struct dentry *), void *context)
|
||||
struct dentry *
|
||||
exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
|
||||
int fileid_type,
|
||||
int (*acceptable)(void *, struct dentry *),
|
||||
void *context)
|
||||
{
|
||||
const struct export_operations *nop = mnt->mnt_sb->s_export_op;
|
||||
struct dentry *result, *alias;
|
||||
@ -432,10 +434,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
|
||||
if (!nop || !nop->fh_to_dentry)
|
||||
return ERR_PTR(-ESTALE);
|
||||
result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
|
||||
if (PTR_ERR(result) == -ENOMEM)
|
||||
return ERR_CAST(result);
|
||||
if (IS_ERR_OR_NULL(result))
|
||||
return ERR_PTR(-ESTALE);
|
||||
return result;
|
||||
|
||||
/*
|
||||
* If no acceptance criteria was specified by caller, a disconnected
|
||||
@ -561,10 +561,26 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
|
||||
|
||||
err_result:
|
||||
dput(result);
|
||||
if (err != -ENOMEM)
|
||||
err = -ESTALE;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exportfs_decode_fh_raw);
|
||||
|
||||
struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
|
||||
int fh_len, int fileid_type,
|
||||
int (*acceptable)(void *, struct dentry *),
|
||||
void *context)
|
||||
{
|
||||
struct dentry *ret;
|
||||
|
||||
ret = exportfs_decode_fh_raw(mnt, fid, fh_len, fileid_type,
|
||||
acceptable, context);
|
||||
if (IS_ERR_OR_NULL(ret)) {
|
||||
if (ret == ERR_PTR(-ENOMEM))
|
||||
return ret;
|
||||
return ERR_PTR(-ESTALE);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exportfs_decode_fh);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
177
fs/file.c
177
fs/file.c
@ -175,7 +175,7 @@ static int expand_fdtable(struct files_struct *files, unsigned int nr)
|
||||
spin_unlock(&files->file_lock);
|
||||
new_fdt = alloc_fdtable(nr);
|
||||
|
||||
/* make sure all __fd_install() have seen resize_in_progress
|
||||
/* make sure all fd_install() have seen resize_in_progress
|
||||
* or have finished their rcu_read_lock_sched() section.
|
||||
*/
|
||||
if (atomic_read(&files->count) > 1)
|
||||
@ -198,7 +198,7 @@ static int expand_fdtable(struct files_struct *files, unsigned int nr)
|
||||
rcu_assign_pointer(files->fdt, new_fdt);
|
||||
if (cur_fdt != &files->fdtab)
|
||||
call_rcu(&cur_fdt->rcu, free_fdtable_rcu);
|
||||
/* coupled with smp_rmb() in __fd_install() */
|
||||
/* coupled with smp_rmb() in fd_install() */
|
||||
smp_wmb();
|
||||
return 1;
|
||||
}
|
||||
@ -466,18 +466,6 @@ void put_files_struct(struct files_struct *files)
|
||||
}
|
||||
}
|
||||
|
||||
void reset_files_struct(struct files_struct *files)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct files_struct *old;
|
||||
|
||||
old = tsk->files;
|
||||
task_lock(tsk);
|
||||
tsk->files = files;
|
||||
task_unlock(tsk);
|
||||
put_files_struct(old);
|
||||
}
|
||||
|
||||
void exit_files(struct task_struct *tsk)
|
||||
{
|
||||
struct files_struct * files = tsk->files;
|
||||
@ -521,9 +509,9 @@ static unsigned int find_next_fd(struct fdtable *fdt, unsigned int start)
|
||||
/*
|
||||
* allocate a file descriptor, mark it busy.
|
||||
*/
|
||||
int __alloc_fd(struct files_struct *files,
|
||||
unsigned start, unsigned end, unsigned flags)
|
||||
static int alloc_fd(unsigned start, unsigned end, unsigned flags)
|
||||
{
|
||||
struct files_struct *files = current->files;
|
||||
unsigned int fd;
|
||||
int error;
|
||||
struct fdtable *fdt;
|
||||
@ -579,14 +567,9 @@ int __alloc_fd(struct files_struct *files,
|
||||
return error;
|
||||
}
|
||||
|
||||
static int alloc_fd(unsigned start, unsigned flags)
|
||||
{
|
||||
return __alloc_fd(current->files, start, rlimit(RLIMIT_NOFILE), flags);
|
||||
}
|
||||
|
||||
int __get_unused_fd_flags(unsigned flags, unsigned long nofile)
|
||||
{
|
||||
return __alloc_fd(current->files, 0, nofile, flags);
|
||||
return alloc_fd(0, nofile, flags);
|
||||
}
|
||||
|
||||
int get_unused_fd_flags(unsigned flags)
|
||||
@ -625,17 +608,13 @@ EXPORT_SYMBOL(put_unused_fd);
|
||||
* It should never happen - if we allow dup2() do it, _really_ bad things
|
||||
* will follow.
|
||||
*
|
||||
* NOTE: __fd_install() variant is really, really low-level; don't
|
||||
* use it unless you are forced to by truly lousy API shoved down
|
||||
* your throat. 'files' *MUST* be either current->files or obtained
|
||||
* by get_files_struct(current) done by whoever had given it to you,
|
||||
* or really bad things will happen. Normally you want to use
|
||||
* fd_install() instead.
|
||||
* This consumes the "file" refcount, so callers should treat it
|
||||
* as if they had called fput(file).
|
||||
*/
|
||||
|
||||
void __fd_install(struct files_struct *files, unsigned int fd,
|
||||
struct file *file)
|
||||
void fd_install(unsigned int fd, struct file *file)
|
||||
{
|
||||
struct files_struct *files = current->files;
|
||||
struct fdtable *fdt;
|
||||
|
||||
rcu_read_lock_sched();
|
||||
@ -657,15 +636,6 @@ void __fd_install(struct files_struct *files, unsigned int fd,
|
||||
rcu_read_unlock_sched();
|
||||
}
|
||||
|
||||
/*
|
||||
* This consumes the "file" refcount, so callers should treat it
|
||||
* as if they had called fput(file).
|
||||
*/
|
||||
void fd_install(unsigned int fd, struct file *file)
|
||||
{
|
||||
__fd_install(current->files, fd, file);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(fd_install);
|
||||
|
||||
static struct file *pick_file(struct files_struct *files, unsigned fd)
|
||||
@ -689,11 +659,9 @@ static struct file *pick_file(struct files_struct *files, unsigned fd)
|
||||
return file;
|
||||
}
|
||||
|
||||
/*
|
||||
* The same warnings as for __alloc_fd()/__fd_install() apply here...
|
||||
*/
|
||||
int __close_fd(struct files_struct *files, unsigned fd)
|
||||
int close_fd(unsigned fd)
|
||||
{
|
||||
struct files_struct *files = current->files;
|
||||
struct file *file;
|
||||
|
||||
file = pick_file(files, fd);
|
||||
@ -702,7 +670,7 @@ int __close_fd(struct files_struct *files, unsigned fd)
|
||||
|
||||
return filp_close(file, files);
|
||||
}
|
||||
EXPORT_SYMBOL(__close_fd); /* for ksys_close() */
|
||||
EXPORT_SYMBOL(close_fd); /* for ksys_close() */
|
||||
|
||||
/**
|
||||
* __close_range() - Close all file descriptors in a given range.
|
||||
@ -861,68 +829,28 @@ void do_close_on_exec(struct files_struct *files)
|
||||
spin_unlock(&files->file_lock);
|
||||
}
|
||||
|
||||
static inline struct file *__fget_files_rcu(struct files_struct *files,
|
||||
unsigned int fd, fmode_t mask, unsigned int refs)
|
||||
{
|
||||
for (;;) {
|
||||
struct file *file;
|
||||
struct fdtable *fdt = rcu_dereference_raw(files->fdt);
|
||||
struct file __rcu **fdentry;
|
||||
|
||||
if (unlikely(fd >= fdt->max_fds))
|
||||
return NULL;
|
||||
|
||||
fdentry = fdt->fd + array_index_nospec(fd, fdt->max_fds);
|
||||
file = rcu_dereference_raw(*fdentry);
|
||||
if (unlikely(!file))
|
||||
return NULL;
|
||||
|
||||
if (unlikely(file->f_mode & mask))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Ok, we have a file pointer. However, because we do
|
||||
* this all locklessly under RCU, we may be racing with
|
||||
* that file being closed.
|
||||
*
|
||||
* Such a race can take two forms:
|
||||
*
|
||||
* (a) the file ref already went down to zero,
|
||||
* and get_file_rcu_many() fails. Just try
|
||||
* again:
|
||||
*/
|
||||
if (unlikely(!get_file_rcu_many(file, refs)))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* (b) the file table entry has changed under us.
|
||||
* Note that we don't need to re-check the 'fdt->fd'
|
||||
* pointer having changed, because it always goes
|
||||
* hand-in-hand with 'fdt'.
|
||||
*
|
||||
* If so, we need to put our refs and try again.
|
||||
*/
|
||||
if (unlikely(rcu_dereference_raw(files->fdt) != fdt) ||
|
||||
unlikely(rcu_dereference_raw(*fdentry) != file)) {
|
||||
fput_many(file, refs);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, we have a ref to the file, and checked that it
|
||||
* still exists.
|
||||
*/
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
static struct file *__fget_files(struct files_struct *files, unsigned int fd,
|
||||
fmode_t mask, unsigned int refs)
|
||||
{
|
||||
struct file *file;
|
||||
|
||||
rcu_read_lock();
|
||||
file = __fget_files_rcu(files, fd, mask, refs);
|
||||
loop:
|
||||
file = files_lookup_fd_rcu(files, fd);
|
||||
if (file) {
|
||||
/* File object ref couldn't be taken.
|
||||
* dup2() atomicity guarantee is the reason
|
||||
* we loop to catch the new file (or NULL pointer)
|
||||
*/
|
||||
if (file->f_mode & mask)
|
||||
file = NULL;
|
||||
else if (!get_file_rcu_many(file, refs))
|
||||
goto loop;
|
||||
else if (files_lookup_fd_raw(files, fd) != file) {
|
||||
fput_many(file, refs);
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return file;
|
||||
@ -963,6 +891,42 @@ struct file *fget_task(struct task_struct *task, unsigned int fd)
|
||||
return file;
|
||||
}
|
||||
|
||||
struct file *task_lookup_fd_rcu(struct task_struct *task, unsigned int fd)
|
||||
{
|
||||
/* Must be called with rcu_read_lock held */
|
||||
struct files_struct *files;
|
||||
struct file *file = NULL;
|
||||
|
||||
task_lock(task);
|
||||
files = task->files;
|
||||
if (files)
|
||||
file = files_lookup_fd_rcu(files, fd);
|
||||
task_unlock(task);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
struct file *task_lookup_next_fd_rcu(struct task_struct *task, unsigned int *ret_fd)
|
||||
{
|
||||
/* Must be called with rcu_read_lock held */
|
||||
struct files_struct *files;
|
||||
unsigned int fd = *ret_fd;
|
||||
struct file *file = NULL;
|
||||
|
||||
task_lock(task);
|
||||
files = task->files;
|
||||
if (files) {
|
||||
for (; fd < files_fdtable(files)->max_fds; fd++) {
|
||||
file = files_lookup_fd_rcu(files, fd);
|
||||
if (file)
|
||||
break;
|
||||
}
|
||||
}
|
||||
task_unlock(task);
|
||||
*ret_fd = fd;
|
||||
return file;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lightweight file lookup - no refcnt increment if fd table isn't shared.
|
||||
*
|
||||
@ -985,7 +949,7 @@ static unsigned long __fget_light(unsigned int fd, fmode_t mask)
|
||||
struct file *file;
|
||||
|
||||
if (atomic_read(&files->count) == 1) {
|
||||
file = __fcheck_files(files, fd);
|
||||
file = files_lookup_fd_raw(files, fd);
|
||||
if (!file || unlikely(file->f_mode & mask))
|
||||
return 0;
|
||||
return (unsigned long)file;
|
||||
@ -1121,7 +1085,7 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags)
|
||||
struct files_struct *files = current->files;
|
||||
|
||||
if (!file)
|
||||
return __close_fd(files, fd);
|
||||
return close_fd(fd);
|
||||
|
||||
if (fd >= rlimit(RLIMIT_NOFILE))
|
||||
return -EBADF;
|
||||
@ -1210,7 +1174,7 @@ static int ksys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
err = expand_files(files, newfd);
|
||||
file = fcheck(oldfd);
|
||||
file = files_lookup_fd_locked(files, oldfd);
|
||||
if (unlikely(!file))
|
||||
goto Ebadf;
|
||||
if (unlikely(err < 0)) {
|
||||
@ -1239,7 +1203,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
|
||||
int retval = oldfd;
|
||||
|
||||
rcu_read_lock();
|
||||
if (!fcheck_files(files, oldfd))
|
||||
if (!files_lookup_fd_rcu(files, oldfd))
|
||||
retval = -EBADF;
|
||||
rcu_read_unlock();
|
||||
return retval;
|
||||
@ -1264,10 +1228,11 @@ SYSCALL_DEFINE1(dup, unsigned int, fildes)
|
||||
|
||||
int f_dupfd(unsigned int from, struct file *file, unsigned flags)
|
||||
{
|
||||
unsigned long nofile = rlimit(RLIMIT_NOFILE);
|
||||
int err;
|
||||
if (from >= rlimit(RLIMIT_NOFILE))
|
||||
if (from >= nofile)
|
||||
return -EINVAL;
|
||||
err = alloc_fd(from, flags);
|
||||
err = alloc_fd(from, nofile, flags);
|
||||
if (err >= 0) {
|
||||
get_file(file);
|
||||
fd_install(err, file);
|
||||
|
@ -49,7 +49,7 @@ int __init init_chdir(const char *filename)
|
||||
error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
|
||||
if (error)
|
||||
return error;
|
||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||
error = path_permission(&path, MAY_EXEC | MAY_CHDIR);
|
||||
if (!error)
|
||||
set_fs_pwd(current->fs, &path);
|
||||
path_put(&path);
|
||||
@ -64,7 +64,7 @@ int __init init_chroot(const char *filename)
|
||||
error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
|
||||
if (error)
|
||||
return error;
|
||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||
error = path_permission(&path, MAY_EXEC | MAY_CHDIR);
|
||||
if (error)
|
||||
goto dput_and_out;
|
||||
error = -EPERM;
|
||||
@ -118,7 +118,7 @@ int __init init_eaccess(const char *filename)
|
||||
error = kern_path(filename, LOOKUP_FOLLOW, &path);
|
||||
if (error)
|
||||
return error;
|
||||
error = inode_permission(d_inode(path.dentry), MAY_ACCESS);
|
||||
error = path_permission(&path, MAY_ACCESS);
|
||||
path_put(&path);
|
||||
return error;
|
||||
}
|
||||
|
@ -261,7 +261,6 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
|
||||
u32 exclusive;
|
||||
int error;
|
||||
__be32 *p;
|
||||
s32 end;
|
||||
|
||||
memset(lock, 0, sizeof(*lock));
|
||||
locks_init_lock(fl);
|
||||
@ -285,13 +284,7 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
|
||||
fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
|
||||
p = xdr_decode_hyper(p, &l_offset);
|
||||
xdr_decode_hyper(p, &l_len);
|
||||
end = l_offset + l_len - 1;
|
||||
|
||||
fl->fl_start = (loff_t)l_offset;
|
||||
if (l_len == 0 || end < 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = (loff_t)end;
|
||||
nlm4svc_set_file_lock_range(fl, l_offset, l_len);
|
||||
error = 0;
|
||||
out:
|
||||
return error;
|
||||
|
@ -794,9 +794,6 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
|
||||
goto retry_cancel;
|
||||
}
|
||||
|
||||
dprintk("lockd: cancel status %u (task %u)\n",
|
||||
status, task->tk_pid);
|
||||
|
||||
switch (status) {
|
||||
case NLM_LCK_GRANTED:
|
||||
case NLM_LCK_DENIED_GRACE_PERIOD:
|
||||
|
@ -163,8 +163,8 @@ static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
|
||||
host->h_nsmhandle = nsm;
|
||||
host->h_addrbuf = nsm->sm_addrbuf;
|
||||
host->net = ni->net;
|
||||
host->h_cred = get_cred(ni->cred),
|
||||
strlcpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
|
||||
host->h_cred = get_cred(ni->cred);
|
||||
strscpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
|
||||
|
||||
out:
|
||||
return host;
|
||||
|
260
fs/lockd/svc.c
260
fs/lockd/svc.c
@ -54,13 +54,9 @@ EXPORT_SYMBOL_GPL(nlmsvc_ops);
|
||||
|
||||
static DEFINE_MUTEX(nlmsvc_mutex);
|
||||
static unsigned int nlmsvc_users;
|
||||
static struct task_struct *nlmsvc_task;
|
||||
static struct svc_rqst *nlmsvc_rqst;
|
||||
static struct svc_serv *nlmsvc_serv;
|
||||
unsigned long nlmsvc_timeout;
|
||||
|
||||
static atomic_t nlm_ntf_refcnt = ATOMIC_INIT(0);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(nlm_ntf_wq);
|
||||
|
||||
unsigned int lockd_net_id;
|
||||
|
||||
/*
|
||||
@ -184,6 +180,10 @@ lockd(void *vrqstp)
|
||||
nlm_shutdown_hosts();
|
||||
cancel_delayed_work_sync(&ln->grace_period_end);
|
||||
locks_end_grace(&ln->lockd_manager);
|
||||
|
||||
dprintk("lockd_down: service stopped\n");
|
||||
|
||||
svc_exit_thread(rqstp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name,
|
||||
|
||||
xprt = svc_find_xprt(serv, name, net, family, 0);
|
||||
if (xprt == NULL)
|
||||
return svc_create_xprt(serv, name, net, family, port,
|
||||
return svc_xprt_create(serv, name, net, family, port,
|
||||
SVC_SOCK_DEFAULTS, cred);
|
||||
svc_xprt_put(xprt);
|
||||
return 0;
|
||||
@ -247,7 +247,8 @@ static int make_socks(struct svc_serv *serv, struct net *net,
|
||||
if (warned++ == 0)
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: makesock failed, error=%d\n", err);
|
||||
svc_shutdown_net(serv, net);
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -285,13 +286,12 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net)
|
||||
nlm_shutdown_hosts_net(net);
|
||||
cancel_delayed_work_sync(&ln->grace_period_end);
|
||||
locks_end_grace(&ln->lockd_manager);
|
||||
svc_shutdown_net(serv, net);
|
||||
dprintk("%s: per-net data destroyed; net=%x\n",
|
||||
__func__, net->ns.inum);
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
}
|
||||
} else {
|
||||
pr_err("%s: no users! task=%p, net=%x\n",
|
||||
__func__, nlmsvc_task, net->ns.inum);
|
||||
pr_err("%s: no users! net=%x\n",
|
||||
__func__, net->ns.inum);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
@ -302,20 +302,16 @@ static int lockd_inetaddr_event(struct notifier_block *this,
|
||||
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
if ((event != NETDEV_DOWN) ||
|
||||
!atomic_inc_not_zero(&nlm_ntf_refcnt))
|
||||
if (event != NETDEV_DOWN)
|
||||
goto out;
|
||||
|
||||
if (nlmsvc_rqst) {
|
||||
if (nlmsvc_serv) {
|
||||
dprintk("lockd_inetaddr_event: removed %pI4\n",
|
||||
&ifa->ifa_local);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = ifa->ifa_local;
|
||||
svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
|
||||
(struct sockaddr *)&sin);
|
||||
svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin);
|
||||
}
|
||||
atomic_dec(&nlm_ntf_refcnt);
|
||||
wake_up(&nlm_ntf_wq);
|
||||
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
@ -332,21 +328,17 @@ static int lockd_inet6addr_event(struct notifier_block *this,
|
||||
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
|
||||
struct sockaddr_in6 sin6;
|
||||
|
||||
if ((event != NETDEV_DOWN) ||
|
||||
!atomic_inc_not_zero(&nlm_ntf_refcnt))
|
||||
if (event != NETDEV_DOWN)
|
||||
goto out;
|
||||
|
||||
if (nlmsvc_rqst) {
|
||||
if (nlmsvc_serv) {
|
||||
dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr);
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_addr = ifa->addr;
|
||||
if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
|
||||
sin6.sin6_scope_id = ifa->idev->dev->ifindex;
|
||||
svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
|
||||
(struct sockaddr *)&sin6);
|
||||
svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin6);
|
||||
}
|
||||
atomic_dec(&nlm_ntf_refcnt);
|
||||
wake_up(&nlm_ntf_wq);
|
||||
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
@ -357,86 +349,14 @@ static struct notifier_block lockd_inet6addr_notifier = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static void lockd_unregister_notifiers(void)
|
||||
{
|
||||
unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
|
||||
#endif
|
||||
wait_event(nlm_ntf_wq, atomic_read(&nlm_ntf_refcnt) == 0);
|
||||
}
|
||||
|
||||
static void lockd_svc_exit_thread(void)
|
||||
{
|
||||
atomic_dec(&nlm_ntf_refcnt);
|
||||
lockd_unregister_notifiers();
|
||||
svc_exit_thread(nlmsvc_rqst);
|
||||
}
|
||||
|
||||
static int lockd_start_svc(struct svc_serv *serv)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (nlmsvc_rqst)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Create the kernel thread and wait for it to start.
|
||||
*/
|
||||
nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
|
||||
if (IS_ERR(nlmsvc_rqst)) {
|
||||
error = PTR_ERR(nlmsvc_rqst);
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: svc_rqst allocation failed, error=%d\n",
|
||||
error);
|
||||
lockd_unregister_notifiers();
|
||||
goto out_rqst;
|
||||
}
|
||||
|
||||
atomic_inc(&nlm_ntf_refcnt);
|
||||
svc_sock_update_bufs(serv);
|
||||
serv->sv_maxconn = nlm_max_connections;
|
||||
|
||||
nlmsvc_task = kthread_create(lockd, nlmsvc_rqst, "%s", serv->sv_name);
|
||||
if (IS_ERR(nlmsvc_task)) {
|
||||
error = PTR_ERR(nlmsvc_task);
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: kthread_run failed, error=%d\n", error);
|
||||
goto out_task;
|
||||
}
|
||||
nlmsvc_rqst->rq_task = nlmsvc_task;
|
||||
wake_up_process(nlmsvc_task);
|
||||
|
||||
dprintk("lockd_up: service started\n");
|
||||
return 0;
|
||||
|
||||
out_task:
|
||||
lockd_svc_exit_thread();
|
||||
nlmsvc_task = NULL;
|
||||
out_rqst:
|
||||
nlmsvc_rqst = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
static const struct svc_serv_ops lockd_sv_ops = {
|
||||
.svo_shutdown = svc_rpcb_cleanup,
|
||||
.svo_enqueue_xprt = svc_xprt_do_enqueue,
|
||||
};
|
||||
|
||||
static struct svc_serv *lockd_create_svc(void)
|
||||
static int lockd_get(void)
|
||||
{
|
||||
struct svc_serv *serv;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Check whether we're already up and running.
|
||||
*/
|
||||
if (nlmsvc_rqst) {
|
||||
/*
|
||||
* Note: increase service usage, because later in case of error
|
||||
* svc_destroy() will be called.
|
||||
*/
|
||||
svc_get(nlmsvc_rqst->rq_server);
|
||||
return nlmsvc_rqst->rq_server;
|
||||
if (nlmsvc_serv) {
|
||||
nlmsvc_users++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -451,17 +371,44 @@ static struct svc_serv *lockd_create_svc(void)
|
||||
nlm_timeout = LOCKD_DFLT_TIMEO;
|
||||
nlmsvc_timeout = nlm_timeout * HZ;
|
||||
|
||||
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, &lockd_sv_ops);
|
||||
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, lockd);
|
||||
if (!serv) {
|
||||
printk(KERN_WARNING "lockd_up: create service failed\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
serv->sv_maxconn = nlm_max_connections;
|
||||
error = svc_set_num_threads(serv, NULL, 1);
|
||||
/* The thread now holds the only reference */
|
||||
svc_put(serv);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
nlmsvc_serv = serv;
|
||||
register_inetaddr_notifier(&lockd_inetaddr_notifier);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
register_inet6addr_notifier(&lockd_inet6addr_notifier);
|
||||
#endif
|
||||
dprintk("lockd_up: service created\n");
|
||||
return serv;
|
||||
nlmsvc_users++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lockd_put(void)
|
||||
{
|
||||
if (WARN(nlmsvc_users <= 0, "lockd_down: no users!\n"))
|
||||
return;
|
||||
if (--nlmsvc_users)
|
||||
return;
|
||||
|
||||
unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
|
||||
#endif
|
||||
|
||||
svc_set_num_threads(nlmsvc_serv, NULL, 0);
|
||||
nlmsvc_serv = NULL;
|
||||
dprintk("lockd_down: service destroyed\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -469,36 +416,21 @@ static struct svc_serv *lockd_create_svc(void)
|
||||
*/
|
||||
int lockd_up(struct net *net, const struct cred *cred)
|
||||
{
|
||||
struct svc_serv *serv;
|
||||
int error;
|
||||
|
||||
mutex_lock(&nlmsvc_mutex);
|
||||
|
||||
serv = lockd_create_svc();
|
||||
if (IS_ERR(serv)) {
|
||||
error = PTR_ERR(serv);
|
||||
goto err_create;
|
||||
error = lockd_get();
|
||||
if (error)
|
||||
goto err;
|
||||
|
||||
error = lockd_up_net(nlmsvc_serv, net, cred);
|
||||
if (error < 0) {
|
||||
lockd_put();
|
||||
goto err;
|
||||
}
|
||||
|
||||
error = lockd_up_net(serv, net, cred);
|
||||
if (error < 0) {
|
||||
lockd_unregister_notifiers();
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
error = lockd_start_svc(serv);
|
||||
if (error < 0) {
|
||||
lockd_down_net(serv, net);
|
||||
goto err_put;
|
||||
}
|
||||
nlmsvc_users++;
|
||||
/*
|
||||
* Note: svc_serv structures have an initial use count of 1,
|
||||
* so we exit through here on both success and failure.
|
||||
*/
|
||||
err_put:
|
||||
svc_destroy(serv);
|
||||
err_create:
|
||||
err:
|
||||
mutex_unlock(&nlmsvc_mutex);
|
||||
return error;
|
||||
}
|
||||
@ -511,27 +443,8 @@ void
|
||||
lockd_down(struct net *net)
|
||||
{
|
||||
mutex_lock(&nlmsvc_mutex);
|
||||
lockd_down_net(nlmsvc_rqst->rq_server, net);
|
||||
if (nlmsvc_users) {
|
||||
if (--nlmsvc_users)
|
||||
goto out;
|
||||
} else {
|
||||
printk(KERN_ERR "lockd_down: no users! task=%p\n",
|
||||
nlmsvc_task);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (!nlmsvc_task) {
|
||||
printk(KERN_ERR "lockd_down: no lockd running.\n");
|
||||
BUG();
|
||||
}
|
||||
kthread_stop(nlmsvc_task);
|
||||
dprintk("lockd_down: service stopped\n");
|
||||
lockd_svc_exit_thread();
|
||||
dprintk("lockd_down: service destroyed\n");
|
||||
nlmsvc_task = NULL;
|
||||
nlmsvc_rqst = NULL;
|
||||
out:
|
||||
lockd_down_net(nlmsvc_serv, net);
|
||||
lockd_put();
|
||||
mutex_unlock(&nlmsvc_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lockd_down);
|
||||
@ -584,7 +497,7 @@ static struct ctl_table nlm_sysctls[] = {
|
||||
.data = &nsm_use_hostnames,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_dobool,
|
||||
},
|
||||
{
|
||||
.procname = "nsm_local_state",
|
||||
@ -649,6 +562,7 @@ static int lockd_authenticate(struct svc_rqst *rqstp)
|
||||
switch (rqstp->rq_authop->flavour) {
|
||||
case RPC_AUTH_NULL:
|
||||
case RPC_AUTH_UNIX:
|
||||
rqstp->rq_auth_stat = rpc_auth_ok;
|
||||
if (rqstp->rq_proc == 0)
|
||||
return SVC_OK;
|
||||
if (is_callback(rqstp->rq_proc)) {
|
||||
@ -659,6 +573,7 @@ static int lockd_authenticate(struct svc_rqst *rqstp)
|
||||
}
|
||||
return svc_set_client(rqstp);
|
||||
}
|
||||
rqstp->rq_auth_stat = rpc_autherr_badcred;
|
||||
return SVC_DENIED;
|
||||
}
|
||||
|
||||
@ -766,6 +681,44 @@ static void __exit exit_nlm(void)
|
||||
module_init(init_nlm);
|
||||
module_exit(exit_nlm);
|
||||
|
||||
/**
|
||||
* nlmsvc_dispatch - Process an NLM Request
|
||||
* @rqstp: incoming request
|
||||
* @statp: pointer to location of accept_stat field in RPC Reply buffer
|
||||
*
|
||||
* Return values:
|
||||
* %0: Processing complete; do not send a Reply
|
||||
* %1: Processing complete; send Reply in rqstp->rq_res
|
||||
*/
|
||||
static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
||||
{
|
||||
const struct svc_procedure *procp = rqstp->rq_procinfo;
|
||||
|
||||
svcxdr_init_decode(rqstp);
|
||||
if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream))
|
||||
goto out_decode_err;
|
||||
|
||||
*statp = procp->pc_func(rqstp);
|
||||
if (*statp == rpc_drop_reply)
|
||||
return 0;
|
||||
if (*statp != rpc_success)
|
||||
return 1;
|
||||
|
||||
svcxdr_init_encode(rqstp);
|
||||
if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream))
|
||||
goto out_encode_err;
|
||||
|
||||
return 1;
|
||||
|
||||
out_decode_err:
|
||||
*statp = rpc_garbage_args;
|
||||
return 1;
|
||||
|
||||
out_encode_err:
|
||||
*statp = rpc_system_err;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Define NLM program and procedures
|
||||
*/
|
||||
@ -775,6 +728,7 @@ static const struct svc_version nlmsvc_version1 = {
|
||||
.vs_nproc = 17,
|
||||
.vs_proc = nlmsvc_procedures,
|
||||
.vs_count = nlmsvc_version1_count,
|
||||
.vs_dispatch = nlmsvc_dispatch,
|
||||
.vs_xdrsize = NLMSVC_XDRSIZE,
|
||||
};
|
||||
static unsigned int nlmsvc_version3_count[24];
|
||||
@ -783,6 +737,7 @@ static const struct svc_version nlmsvc_version3 = {
|
||||
.vs_nproc = 24,
|
||||
.vs_proc = nlmsvc_procedures,
|
||||
.vs_count = nlmsvc_version3_count,
|
||||
.vs_dispatch = nlmsvc_dispatch,
|
||||
.vs_xdrsize = NLMSVC_XDRSIZE,
|
||||
};
|
||||
#ifdef CONFIG_LOCKD_V4
|
||||
@ -792,6 +747,7 @@ static const struct svc_version nlmsvc_version4 = {
|
||||
.vs_nproc = 24,
|
||||
.vs_proc = nlmsvc_procedures4,
|
||||
.vs_count = nlmsvc_version4_count,
|
||||
.vs_dispatch = nlmsvc_dispatch,
|
||||
.vs_xdrsize = NLMSVC_XDRSIZE,
|
||||
};
|
||||
#endif
|
||||
|
@ -32,6 +32,10 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
if (!nlmsvc_ops)
|
||||
return nlm_lck_denied_nolocks;
|
||||
|
||||
if (lock->lock_start > OFFSET_MAX ||
|
||||
(lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start))))
|
||||
return nlm4_fbig;
|
||||
|
||||
/* Obtain host handle */
|
||||
if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len))
|
||||
|| (argp->monitor && nsm_monitor(host) < 0))
|
||||
@ -40,13 +44,21 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
|
||||
/* Obtain file pointer. Not used by FREE_ALL call. */
|
||||
if (filp != NULL) {
|
||||
if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
|
||||
int mode = lock_to_openmode(&lock->fl);
|
||||
|
||||
error = nlm_lookup_file(rqstp, &file, lock);
|
||||
if (error)
|
||||
goto no_locks;
|
||||
*filp = file;
|
||||
|
||||
/* Set up the missing parts of the file_lock structure */
|
||||
lock->fl.fl_file = file->f_file;
|
||||
lock->fl.fl_flags = FL_POSIX;
|
||||
lock->fl.fl_file = file->f_file[mode];
|
||||
lock->fl.fl_pid = current->tgid;
|
||||
lock->fl.fl_start = (loff_t)lock->lock_start;
|
||||
lock->fl.fl_end = lock->lock_len ?
|
||||
(loff_t)(lock->lock_start + lock->lock_len - 1) :
|
||||
OFFSET_MAX;
|
||||
lock->fl.fl_lmops = &nlmsvc_lock_operations;
|
||||
nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
|
||||
if (!lock->fl.fl_owner) {
|
||||
@ -84,6 +96,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
struct nlm_lockowner *test_owner;
|
||||
__be32 rc = rpc_success;
|
||||
|
||||
dprintk("lockd: TEST4 called\n");
|
||||
@ -93,6 +106,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
test_owner = argp->lock.fl.fl_owner;
|
||||
/* Now check for conflicting locks */
|
||||
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie);
|
||||
if (resp->status == nlm_drop_reply)
|
||||
@ -100,7 +114,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
else
|
||||
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
|
||||
|
||||
nlmsvc_release_lockowner(&argp->lock);
|
||||
nlmsvc_put_lockowner(test_owner);
|
||||
nlmsvc_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rc;
|
||||
@ -266,8 +280,6 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp)
|
||||
*/
|
||||
static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
|
||||
{
|
||||
dprintk("lockd: %5u callback returned %d\n", task->tk_pid,
|
||||
-task->tk_status);
|
||||
}
|
||||
|
||||
static void nlm4svc_callback_release(void *data)
|
||||
@ -510,191 +522,239 @@ const struct svc_procedure nlmsvc_procedures4[24] = {
|
||||
.pc_decode = nlm4svc_decode_void,
|
||||
.pc_encode = nlm4svc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[NLMPROC_TEST] = {
|
||||
.pc_func = nlm4svc_proc_test,
|
||||
.pc_decode = nlm4svc_decode_testargs,
|
||||
.pc_encode = nlm4svc_encode_testres,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St+2+No+Rg,
|
||||
.pc_name = "TEST",
|
||||
},
|
||||
[NLMPROC_LOCK] = {
|
||||
.pc_func = nlm4svc_proc_lock,
|
||||
.pc_decode = nlm4svc_decode_lockargs,
|
||||
.pc_encode = nlm4svc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "LOCK",
|
||||
},
|
||||
[NLMPROC_CANCEL] = {
|
||||
.pc_func = nlm4svc_proc_cancel,
|
||||
.pc_decode = nlm4svc_decode_cancargs,
|
||||
.pc_encode = nlm4svc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "CANCEL",
|
||||
},
|
||||
[NLMPROC_UNLOCK] = {
|
||||
.pc_func = nlm4svc_proc_unlock,
|
||||
.pc_decode = nlm4svc_decode_unlockargs,
|
||||
.pc_encode = nlm4svc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "UNLOCK",
|
||||
},
|
||||
[NLMPROC_GRANTED] = {
|
||||
.pc_func = nlm4svc_proc_granted,
|
||||
.pc_decode = nlm4svc_decode_testargs,
|
||||
.pc_encode = nlm4svc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "GRANTED",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "TEST_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "LOCK_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "CANCEL_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "UNLOCK_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "GRANTED_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "TEST_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "LOCK_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "CANCEL_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "UNLOCK_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "GRANTED_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_reboot),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "SM_NOTIFY",
|
||||
},
|
||||
[17] = {
|
||||
.pc_func = nlm4svc_proc_unused,
|
||||
.pc_decode = nlm4svc_decode_void,
|
||||
.pc_encode = nlm4svc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "UNUSED",
|
||||
},
|
||||
[18] = {
|
||||
.pc_func = nlm4svc_proc_unused,
|
||||
.pc_decode = nlm4svc_decode_void,
|
||||
.pc_encode = nlm4svc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "UNUSED",
|
||||
},
|
||||
[19] = {
|
||||
.pc_func = nlm4svc_proc_unused,
|
||||
.pc_decode = nlm4svc_decode_void,
|
||||
.pc_encode = nlm4svc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "UNUSED",
|
||||
},
|
||||
[NLMPROC_SHARE] = {
|
||||
.pc_func = nlm4svc_proc_share,
|
||||
.pc_decode = nlm4svc_decode_shareargs,
|
||||
.pc_encode = nlm4svc_encode_shareres,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St+1,
|
||||
.pc_name = "SHARE",
|
||||
},
|
||||
[NLMPROC_UNSHARE] = {
|
||||
.pc_func = nlm4svc_proc_unshare,
|
||||
.pc_decode = nlm4svc_decode_shareargs,
|
||||
.pc_encode = nlm4svc_encode_shareres,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St+1,
|
||||
.pc_name = "UNSHARE",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "NM_LOCK",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "FREE_ALL",
|
||||
},
|
||||
};
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/lockd/nlm.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/exportfs.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_SVCLOCK
|
||||
|
||||
@ -339,7 +340,7 @@ nlmsvc_get_lockowner(struct nlm_lockowner *lockowner)
|
||||
return lockowner;
|
||||
}
|
||||
|
||||
static void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner)
|
||||
void nlmsvc_put_lockowner(struct nlm_lockowner *lockowner)
|
||||
{
|
||||
if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
|
||||
return;
|
||||
@ -469,18 +470,27 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
struct nlm_host *host, struct nlm_lock *lock, int wait,
|
||||
struct nlm_cookie *cookie, int reclaim)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
|
||||
struct inode *inode = nlmsvc_file_inode(file);
|
||||
#endif
|
||||
struct nlm_block *block = NULL;
|
||||
int error;
|
||||
int mode;
|
||||
int async_block = 0;
|
||||
__be32 ret;
|
||||
|
||||
dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
|
||||
locks_inode(file->f_file)->i_sb->s_id,
|
||||
locks_inode(file->f_file)->i_ino,
|
||||
inode->i_sb->s_id, inode->i_ino,
|
||||
lock->fl.fl_type, lock->fl.fl_pid,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end,
|
||||
wait);
|
||||
|
||||
if (nlmsvc_file_file(file)->f_op->lock) {
|
||||
async_block = wait;
|
||||
wait = 0;
|
||||
}
|
||||
|
||||
/* Lock file against concurrent access */
|
||||
mutex_lock(&file->f_mutex);
|
||||
/* Get existing block (in case client is busy-waiting)
|
||||
@ -524,7 +534,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
|
||||
if (!wait)
|
||||
lock->fl.fl_flags &= ~FL_SLEEP;
|
||||
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
|
||||
mode = lock_to_openmode(&lock->fl);
|
||||
error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
|
||||
lock->fl.fl_flags &= ~FL_SLEEP;
|
||||
|
||||
dprintk("lockd: vfs_lock_file returned %d\n", error);
|
||||
@ -540,7 +551,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
*/
|
||||
if (wait)
|
||||
break;
|
||||
ret = nlm_lck_denied;
|
||||
ret = async_block ? nlm_lck_blocked : nlm_lck_denied;
|
||||
goto out;
|
||||
case FILE_LOCK_DEFERRED:
|
||||
if (wait)
|
||||
@ -577,12 +588,12 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
struct nlm_lock *conflock, struct nlm_cookie *cookie)
|
||||
{
|
||||
int error;
|
||||
int mode;
|
||||
__be32 ret;
|
||||
struct nlm_lockowner *test_owner;
|
||||
|
||||
dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
|
||||
locks_inode(file->f_file)->i_sb->s_id,
|
||||
locks_inode(file->f_file)->i_ino,
|
||||
nlmsvc_file_inode(file)->i_sb->s_id,
|
||||
nlmsvc_file_inode(file)->i_ino,
|
||||
lock->fl.fl_type,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end);
|
||||
@ -592,10 +603,8 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If there's a conflicting lock, remember to clean up the test lock */
|
||||
test_owner = (struct nlm_lockowner *)lock->fl.fl_owner;
|
||||
|
||||
error = vfs_test_lock(file->f_file, &lock->fl);
|
||||
mode = lock_to_openmode(&lock->fl);
|
||||
error = vfs_test_lock(file->f_file[mode], &lock->fl);
|
||||
if (error) {
|
||||
/* We can't currently deal with deferred test requests */
|
||||
if (error == FILE_LOCK_DEFERRED)
|
||||
@ -622,10 +631,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
conflock->fl.fl_end = lock->fl.fl_end;
|
||||
locks_release_private(&lock->fl);
|
||||
|
||||
/* Clean up the test lock */
|
||||
lock->fl.fl_owner = NULL;
|
||||
nlmsvc_put_lockowner(test_owner);
|
||||
|
||||
ret = nlm_lck_denied;
|
||||
out:
|
||||
return ret;
|
||||
@ -641,11 +646,11 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
__be32
|
||||
nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
|
||||
{
|
||||
int error;
|
||||
int error = 0;
|
||||
|
||||
dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
|
||||
locks_inode(file->f_file)->i_sb->s_id,
|
||||
locks_inode(file->f_file)->i_ino,
|
||||
nlmsvc_file_inode(file)->i_sb->s_id,
|
||||
nlmsvc_file_inode(file)->i_ino,
|
||||
lock->fl.fl_pid,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end);
|
||||
@ -654,7 +659,14 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
|
||||
nlmsvc_cancel_blocked(net, file, lock);
|
||||
|
||||
lock->fl.fl_type = F_UNLCK;
|
||||
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
|
||||
lock->fl.fl_file = file->f_file[O_RDONLY];
|
||||
if (lock->fl.fl_file)
|
||||
error = vfs_lock_file(lock->fl.fl_file, F_SETLK,
|
||||
&lock->fl, NULL);
|
||||
lock->fl.fl_file = file->f_file[O_WRONLY];
|
||||
if (lock->fl.fl_file)
|
||||
error |= vfs_lock_file(lock->fl.fl_file, F_SETLK,
|
||||
&lock->fl, NULL);
|
||||
|
||||
return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
|
||||
}
|
||||
@ -671,10 +683,11 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
|
||||
{
|
||||
struct nlm_block *block;
|
||||
int status = 0;
|
||||
int mode;
|
||||
|
||||
dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
|
||||
locks_inode(file->f_file)->i_sb->s_id,
|
||||
locks_inode(file->f_file)->i_ino,
|
||||
nlmsvc_file_inode(file)->i_sb->s_id,
|
||||
nlmsvc_file_inode(file)->i_ino,
|
||||
lock->fl.fl_pid,
|
||||
(long long)lock->fl.fl_start,
|
||||
(long long)lock->fl.fl_end);
|
||||
@ -686,8 +699,10 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
|
||||
block = nlmsvc_lookup_block(file, lock);
|
||||
mutex_unlock(&file->f_mutex);
|
||||
if (block != NULL) {
|
||||
vfs_cancel_lock(block->b_file->f_file,
|
||||
&block->b_call->a_args.lock.fl);
|
||||
struct file_lock *fl = &block->b_call->a_args.lock.fl;
|
||||
|
||||
mode = lock_to_openmode(fl);
|
||||
vfs_cancel_lock(block->b_file->f_file[mode], fl);
|
||||
status = nlmsvc_unlink_block(block);
|
||||
nlmsvc_release_block(block);
|
||||
}
|
||||
@ -803,6 +818,7 @@ nlmsvc_grant_blocked(struct nlm_block *block)
|
||||
{
|
||||
struct nlm_file *file = block->b_file;
|
||||
struct nlm_lock *lock = &block->b_call->a_args.lock;
|
||||
int mode;
|
||||
int error;
|
||||
loff_t fl_start, fl_end;
|
||||
|
||||
@ -828,7 +844,8 @@ nlmsvc_grant_blocked(struct nlm_block *block)
|
||||
lock->fl.fl_flags |= FL_SLEEP;
|
||||
fl_start = lock->fl.fl_start;
|
||||
fl_end = lock->fl.fl_end;
|
||||
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
|
||||
mode = lock_to_openmode(&lock->fl);
|
||||
error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
|
||||
lock->fl.fl_flags &= ~FL_SLEEP;
|
||||
lock->fl.fl_start = fl_start;
|
||||
lock->fl.fl_end = fl_end;
|
||||
|
@ -55,6 +55,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
struct nlm_host *host = NULL;
|
||||
struct nlm_file *file = NULL;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
int mode;
|
||||
__be32 error = 0;
|
||||
|
||||
/* nfsd callbacks must have been installed for this procedure */
|
||||
@ -69,13 +70,15 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
|
||||
/* Obtain file pointer. Not used by FREE_ALL call. */
|
||||
if (filp != NULL) {
|
||||
error = cast_status(nlm_lookup_file(rqstp, &file, &lock->fh));
|
||||
error = cast_status(nlm_lookup_file(rqstp, &file, lock));
|
||||
if (error != 0)
|
||||
goto no_locks;
|
||||
*filp = file;
|
||||
|
||||
/* Set up the missing parts of the file_lock structure */
|
||||
lock->fl.fl_file = file->f_file;
|
||||
mode = lock_to_openmode(&lock->fl);
|
||||
lock->fl.fl_flags = FL_POSIX;
|
||||
lock->fl.fl_file = file->f_file[mode];
|
||||
lock->fl.fl_pid = current->tgid;
|
||||
lock->fl.fl_lmops = &nlmsvc_lock_operations;
|
||||
nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
|
||||
@ -114,6 +117,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
struct nlm_host *host;
|
||||
struct nlm_file *file;
|
||||
struct nlm_lockowner *test_owner;
|
||||
__be32 rc = rpc_success;
|
||||
|
||||
dprintk("lockd: TEST called\n");
|
||||
@ -123,6 +127,8 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
test_owner = argp->lock.fl.fl_owner;
|
||||
|
||||
/* Now check for conflicting locks */
|
||||
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie));
|
||||
if (resp->status == nlm_drop_reply)
|
||||
@ -131,7 +137,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
dprintk("lockd: TEST status %d vers %d\n",
|
||||
ntohl(resp->status), rqstp->rq_vers);
|
||||
|
||||
nlmsvc_release_lockowner(&argp->lock);
|
||||
nlmsvc_put_lockowner(test_owner);
|
||||
nlmsvc_release_host(host);
|
||||
nlm_release_file(file);
|
||||
return rc;
|
||||
@ -299,8 +305,6 @@ nlmsvc_proc_granted(struct svc_rqst *rqstp)
|
||||
*/
|
||||
static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
|
||||
{
|
||||
dprintk("lockd: %5u callback returned %d\n", task->tk_pid,
|
||||
-task->tk_status);
|
||||
}
|
||||
|
||||
void nlmsvc_release_call(struct nlm_rqst *call)
|
||||
@ -552,191 +556,239 @@ const struct svc_procedure nlmsvc_procedures[24] = {
|
||||
.pc_decode = nlmsvc_decode_void,
|
||||
.pc_encode = nlmsvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[NLMPROC_TEST] = {
|
||||
.pc_func = nlmsvc_proc_test,
|
||||
.pc_decode = nlmsvc_decode_testargs,
|
||||
.pc_encode = nlmsvc_encode_testres,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St+2+No+Rg,
|
||||
.pc_name = "TEST",
|
||||
},
|
||||
[NLMPROC_LOCK] = {
|
||||
.pc_func = nlmsvc_proc_lock,
|
||||
.pc_decode = nlmsvc_decode_lockargs,
|
||||
.pc_encode = nlmsvc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "LOCK",
|
||||
},
|
||||
[NLMPROC_CANCEL] = {
|
||||
.pc_func = nlmsvc_proc_cancel,
|
||||
.pc_decode = nlmsvc_decode_cancargs,
|
||||
.pc_encode = nlmsvc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "CANCEL",
|
||||
},
|
||||
[NLMPROC_UNLOCK] = {
|
||||
.pc_func = nlmsvc_proc_unlock,
|
||||
.pc_decode = nlmsvc_decode_unlockargs,
|
||||
.pc_encode = nlmsvc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "UNLOCK",
|
||||
},
|
||||
[NLMPROC_GRANTED] = {
|
||||
.pc_func = nlmsvc_proc_granted,
|
||||
.pc_decode = nlmsvc_decode_testargs,
|
||||
.pc_encode = nlmsvc_encode_res,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "GRANTED",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "TEST_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "LOCK_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "CANCEL_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "UNLOCK_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "GRANTED_MSG",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "TEST_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "LOCK_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "CANCEL_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "UNLOCK_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_res),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "GRANTED_RES",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_reboot),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "SM_NOTIFY",
|
||||
},
|
||||
[17] = {
|
||||
.pc_func = nlmsvc_proc_unused,
|
||||
.pc_decode = nlmsvc_decode_void,
|
||||
.pc_encode = nlmsvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "UNUSED",
|
||||
},
|
||||
[18] = {
|
||||
.pc_func = nlmsvc_proc_unused,
|
||||
.pc_decode = nlmsvc_decode_void,
|
||||
.pc_encode = nlmsvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "UNUSED",
|
||||
},
|
||||
[19] = {
|
||||
.pc_func = nlmsvc_proc_unused,
|
||||
.pc_decode = nlmsvc_decode_void,
|
||||
.pc_encode = nlmsvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nlm_void),
|
||||
.pc_argzero = sizeof(struct nlm_void),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = St,
|
||||
.pc_name = "UNUSED",
|
||||
},
|
||||
[NLMPROC_SHARE] = {
|
||||
.pc_func = nlmsvc_proc_share,
|
||||
.pc_decode = nlmsvc_decode_shareargs,
|
||||
.pc_encode = nlmsvc_encode_shareres,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St+1,
|
||||
.pc_name = "SHARE",
|
||||
},
|
||||
[NLMPROC_UNSHARE] = {
|
||||
.pc_func = nlmsvc_proc_unshare,
|
||||
.pc_decode = nlmsvc_decode_shareargs,
|
||||
.pc_encode = nlmsvc_encode_shareres,
|
||||
.pc_argsize = sizeof(struct nlm_args),
|
||||
.pc_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St+1,
|
||||
.pc_name = "UNSHARE",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_res),
|
||||
.pc_xdrressize = Ck+St,
|
||||
.pc_name = "NM_LOCK",
|
||||
},
|
||||
[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_argzero = sizeof(struct nlm_args),
|
||||
.pc_ressize = sizeof(struct nlm_void),
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "FREE_ALL",
|
||||
},
|
||||
};
|
||||
|
@ -45,7 +45,7 @@ static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
|
||||
|
||||
static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
|
||||
{
|
||||
struct inode *inode = locks_inode(file->f_file);
|
||||
struct inode *inode = nlmsvc_file_inode(file);
|
||||
|
||||
dprintk("lockd: %s %s/%ld\n",
|
||||
msg, inode->i_sb->s_id, inode->i_ino);
|
||||
@ -71,56 +71,75 @@ static inline unsigned int file_hash(struct nfs_fh *f)
|
||||
return tmp & (FILE_NRHASH - 1);
|
||||
}
|
||||
|
||||
int lock_to_openmode(struct file_lock *lock)
|
||||
{
|
||||
return (lock->fl_type == F_WRLCK) ? O_WRONLY : O_RDONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the file. Note that if we're reexporting, for example,
|
||||
* this could block the lockd thread for a while.
|
||||
*
|
||||
* We have to make sure we have the right credential to open
|
||||
* the file.
|
||||
*/
|
||||
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
|
||||
struct nlm_file *file, int mode)
|
||||
{
|
||||
struct file **fp = &file->f_file[mode];
|
||||
__be32 nfserr;
|
||||
|
||||
if (*fp)
|
||||
return 0;
|
||||
nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
|
||||
if (nfserr)
|
||||
dprintk("lockd: open failed (error %d)\n", nfserr);
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup file info. If it doesn't exist, create a file info struct
|
||||
* and open a (VFS) file for the given inode.
|
||||
*
|
||||
* FIXME:
|
||||
* Note that we open the file O_RDONLY even when creating write locks.
|
||||
* This is not quite right, but for now, we assume the client performs
|
||||
* the proper R/W checking.
|
||||
*/
|
||||
__be32
|
||||
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
|
||||
struct nfs_fh *f)
|
||||
struct nlm_lock *lock)
|
||||
{
|
||||
struct nlm_file *file;
|
||||
unsigned int hash;
|
||||
__be32 nfserr;
|
||||
int mode;
|
||||
|
||||
nlm_debug_print_fh("nlm_lookup_file", f);
|
||||
nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
|
||||
|
||||
hash = file_hash(f);
|
||||
hash = file_hash(&lock->fh);
|
||||
mode = lock_to_openmode(&lock->fl);
|
||||
|
||||
/* Lock file table */
|
||||
mutex_lock(&nlm_file_mutex);
|
||||
|
||||
hlist_for_each_entry(file, &nlm_files[hash], f_list)
|
||||
if (!nfs_compare_fh(&file->f_handle, f))
|
||||
if (!nfs_compare_fh(&file->f_handle, &lock->fh)) {
|
||||
mutex_lock(&file->f_mutex);
|
||||
nfserr = nlm_do_fopen(rqstp, file, mode);
|
||||
mutex_unlock(&file->f_mutex);
|
||||
goto found;
|
||||
|
||||
nlm_debug_print_fh("creating file for", f);
|
||||
}
|
||||
nlm_debug_print_fh("creating file for", &lock->fh);
|
||||
|
||||
nfserr = nlm_lck_denied_nolocks;
|
||||
file = kzalloc(sizeof(*file), GFP_KERNEL);
|
||||
if (!file)
|
||||
goto out_unlock;
|
||||
goto out_free;
|
||||
|
||||
memcpy(&file->f_handle, f, sizeof(struct nfs_fh));
|
||||
memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh));
|
||||
mutex_init(&file->f_mutex);
|
||||
INIT_HLIST_NODE(&file->f_list);
|
||||
INIT_LIST_HEAD(&file->f_blocks);
|
||||
|
||||
/* Open the file. Note that this must not sleep for too long, else
|
||||
* we would lock up lockd:-) So no NFS re-exports, folks.
|
||||
*
|
||||
* We have to make sure we have the right credential to open
|
||||
* the file.
|
||||
*/
|
||||
if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) {
|
||||
dprintk("lockd: open failed (error %d)\n", nfserr);
|
||||
goto out_free;
|
||||
}
|
||||
nfserr = nlm_do_fopen(rqstp, file, mode);
|
||||
if (nfserr)
|
||||
goto out_unlock;
|
||||
|
||||
hlist_add_head(&file->f_list, &nlm_files[hash]);
|
||||
|
||||
@ -128,7 +147,6 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
|
||||
dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
|
||||
*result = file;
|
||||
file->f_count++;
|
||||
nfserr = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&nlm_file_mutex);
|
||||
@ -148,13 +166,40 @@ nlm_delete_file(struct nlm_file *file)
|
||||
nlm_debug_print_file("closing file", file);
|
||||
if (!hlist_unhashed(&file->f_list)) {
|
||||
hlist_del(&file->f_list);
|
||||
nlmsvc_ops->fclose(file->f_file);
|
||||
if (file->f_file[O_RDONLY])
|
||||
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
|
||||
if (file->f_file[O_WRONLY])
|
||||
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
|
||||
kfree(file);
|
||||
} else {
|
||||
printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int nlm_unlock_files(struct nlm_file *file, const struct file_lock *fl)
|
||||
{
|
||||
struct file_lock lock;
|
||||
|
||||
locks_init_lock(&lock);
|
||||
lock.fl_type = F_UNLCK;
|
||||
lock.fl_start = 0;
|
||||
lock.fl_end = OFFSET_MAX;
|
||||
lock.fl_owner = fl->fl_owner;
|
||||
lock.fl_pid = fl->fl_pid;
|
||||
lock.fl_flags = FL_POSIX;
|
||||
|
||||
lock.fl_file = file->f_file[O_RDONLY];
|
||||
if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
|
||||
goto out_err;
|
||||
lock.fl_file = file->f_file[O_WRONLY];
|
||||
if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
|
||||
goto out_err;
|
||||
return 0;
|
||||
out_err:
|
||||
pr_warn("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop over all locks on the given file and perform the specified
|
||||
* action.
|
||||
@ -165,7 +210,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
|
||||
{
|
||||
struct inode *inode = nlmsvc_file_inode(file);
|
||||
struct file_lock *fl;
|
||||
struct file_lock_context *flctx = inode->i_flctx;
|
||||
struct file_lock_context *flctx = locks_inode_context(inode);
|
||||
struct nlm_host *lockhost;
|
||||
|
||||
if (!flctx || list_empty_careful(&flctx->flc_posix))
|
||||
@ -182,17 +227,10 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
|
||||
|
||||
lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
|
||||
if (match(lockhost, host)) {
|
||||
struct file_lock lock = *fl;
|
||||
|
||||
spin_unlock(&flctx->flc_lock);
|
||||
lock.fl_type = F_UNLCK;
|
||||
lock.fl_start = 0;
|
||||
lock.fl_end = OFFSET_MAX;
|
||||
if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) {
|
||||
printk("lockd: unlock failure in %s:%d\n",
|
||||
__FILE__, __LINE__);
|
||||
if (nlm_unlock_files(file, fl))
|
||||
return 1;
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
@ -227,7 +265,7 @@ nlm_file_inuse(struct nlm_file *file)
|
||||
{
|
||||
struct inode *inode = nlmsvc_file_inode(file);
|
||||
struct file_lock *fl;
|
||||
struct file_lock_context *flctx = inode->i_flctx;
|
||||
struct file_lock_context *flctx = locks_inode_context(inode);
|
||||
|
||||
if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
|
||||
return 1;
|
||||
@ -246,6 +284,14 @@ nlm_file_inuse(struct nlm_file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nlm_close_files(struct nlm_file *file)
|
||||
{
|
||||
if (file->f_file[O_RDONLY])
|
||||
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
|
||||
if (file->f_file[O_WRONLY])
|
||||
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop over all files in the file table.
|
||||
*/
|
||||
@ -276,7 +322,7 @@ nlm_traverse_files(void *data, nlm_host_match_fn_t match,
|
||||
if (list_empty(&file->f_blocks) && !file->f_locks
|
||||
&& !file->f_shares && !file->f_count) {
|
||||
hlist_del(&file->f_list);
|
||||
nlmsvc_ops->fclose(file->f_file);
|
||||
nlm_close_files(file);
|
||||
kfree(file);
|
||||
}
|
||||
}
|
||||
@ -410,12 +456,13 @@ nlmsvc_invalidate_all(void)
|
||||
nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nlmsvc_match_sb(void *datap, struct nlm_file *file)
|
||||
{
|
||||
struct super_block *sb = datap;
|
||||
|
||||
return sb == locks_inode(file->f_file)->i_sb;
|
||||
return sb == nlmsvc_file_inode(file)->i_sb;
|
||||
}
|
||||
|
||||
/**
|
||||
|
142
fs/lockd/svcxdr.h
Normal file
142
fs/lockd/svcxdr.h
Normal file
@ -0,0 +1,142 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Encode/decode NLM basic data types
|
||||
*
|
||||
* Basic NLMv3 XDR data types are not defined in an IETF standards
|
||||
* document. X/Open has a description of these data types that
|
||||
* is useful. See Chapter 10 of "Protocols for Interworking:
|
||||
* XNFS, Version 3W".
|
||||
*
|
||||
* Basic NLMv4 XDR data types are defined in Appendix II.1.4 of
|
||||
* RFC 1813: "NFS Version 3 Protocol Specification".
|
||||
*
|
||||
* Author: Chuck Lever <chuck.lever@oracle.com>
|
||||
*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates.
|
||||
*/
|
||||
|
||||
#ifndef _LOCKD_SVCXDR_H_
|
||||
#define _LOCKD_SVCXDR_H_
|
||||
|
||||
static inline bool
|
||||
svcxdr_decode_stats(struct xdr_stream *xdr, __be32 *status)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, XDR_UNIT);
|
||||
if (!p)
|
||||
return false;
|
||||
*status = *p;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svcxdr_encode_stats(struct xdr_stream *xdr, __be32 status)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, XDR_UNIT);
|
||||
if (!p)
|
||||
return false;
|
||||
*p = status;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svcxdr_decode_string(struct xdr_stream *xdr, char **data, unsigned int *data_len)
|
||||
{
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
if (len > NLM_MAXSTRLEN)
|
||||
return false;
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (!p)
|
||||
return false;
|
||||
*data_len = len;
|
||||
*data = (char *)p;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* NLM cookies are defined by specification to be a variable-length
|
||||
* XDR opaque no longer than 1024 bytes. However, this implementation
|
||||
* limits their length to 32 bytes, and treats zero-length cookies
|
||||
* specially.
|
||||
*/
|
||||
static inline bool
|
||||
svcxdr_decode_cookie(struct xdr_stream *xdr, struct nlm_cookie *cookie)
|
||||
{
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
if (len > NLM_MAXCOOKIELEN)
|
||||
return false;
|
||||
if (!len)
|
||||
goto out_hpux;
|
||||
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (!p)
|
||||
return false;
|
||||
cookie->len = len;
|
||||
memcpy(cookie->data, p, len);
|
||||
|
||||
return true;
|
||||
|
||||
/* apparently HPUX can return empty cookies */
|
||||
out_hpux:
|
||||
cookie->len = 4;
|
||||
memset(cookie->data, 0, 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svcxdr_encode_cookie(struct xdr_stream *xdr, const struct nlm_cookie *cookie)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
if (xdr_stream_encode_u32(xdr, cookie->len) < 0)
|
||||
return false;
|
||||
p = xdr_reserve_space(xdr, cookie->len);
|
||||
if (!p)
|
||||
return false;
|
||||
memcpy(p, cookie->data, cookie->len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svcxdr_decode_owner(struct xdr_stream *xdr, struct xdr_netobj *obj)
|
||||
{
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
if (len > XDR_MAX_NETOBJ)
|
||||
return false;
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (!p)
|
||||
return false;
|
||||
obj->len = len;
|
||||
obj->data = (u8 *)p;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
svcxdr_encode_owner(struct xdr_stream *xdr, const struct xdr_netobj *obj)
|
||||
{
|
||||
if (obj->len > XDR_MAX_NETOBJ)
|
||||
return false;
|
||||
return xdr_stream_encode_opaque(xdr, obj->data, obj->len) > 0;
|
||||
}
|
||||
|
||||
#endif /* _LOCKD_SVCXDR_H_ */
|
424
fs/lockd/xdr.c
424
fs/lockd/xdr.c
@ -19,7 +19,7 @@
|
||||
|
||||
#include <uapi/linux/nfs2.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_XDR
|
||||
#include "svcxdr.h"
|
||||
|
||||
|
||||
static inline loff_t
|
||||
@ -42,224 +42,233 @@ loff_t_to_s32(loff_t offset)
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR functions for basic NLM types
|
||||
* NLM file handles are defined by specification to be a variable-length
|
||||
* XDR opaque no longer than 1024 bytes. However, this implementation
|
||||
* constrains their length to exactly the length of an NFSv2 file
|
||||
* handle.
|
||||
*/
|
||||
static __be32 *nlm_decode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
static bool
|
||||
svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
|
||||
{
|
||||
unsigned int len;
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
len = ntohl(*p++);
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
if (len != NFS2_FHSIZE)
|
||||
return false;
|
||||
|
||||
if(len==0)
|
||||
{
|
||||
c->len=4;
|
||||
memset(c->data, 0, 4); /* hockeypux brain damage */
|
||||
}
|
||||
else if(len<=NLM_MAXCOOKIELEN)
|
||||
{
|
||||
c->len=len;
|
||||
memcpy(c->data, p, len);
|
||||
p+=XDR_QUADLEN(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintk("lockd: bad cookie size %d (only cookies under "
|
||||
"%d bytes are supported.)\n",
|
||||
len, NLM_MAXCOOKIELEN);
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (!p)
|
||||
return false;
|
||||
fh->size = NFS2_FHSIZE;
|
||||
memcpy(fh->data, p, len);
|
||||
memset(fh->data + NFS2_FHSIZE, 0, sizeof(fh->data) - NFS2_FHSIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline __be32 *
|
||||
nlm_encode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
{
|
||||
*p++ = htonl(c->len);
|
||||
memcpy(p, c->data, c->len);
|
||||
p+=XDR_QUADLEN(c->len);
|
||||
return p;
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm_decode_fh(__be32 *p, struct nfs_fh *f)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
if ((len = ntohl(*p++)) != NFS2_FHSIZE) {
|
||||
dprintk("lockd: bad fhandle size %d (should be %d)\n",
|
||||
len, NFS2_FHSIZE);
|
||||
return NULL;
|
||||
}
|
||||
f->size = NFS2_FHSIZE;
|
||||
memset(f->data, 0, sizeof(f->data));
|
||||
memcpy(f->data, p, NFS2_FHSIZE);
|
||||
return p + XDR_QUADLEN(NFS2_FHSIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode and decode owner handle
|
||||
*/
|
||||
static inline __be32 *
|
||||
nlm_decode_oh(__be32 *p, struct xdr_netobj *oh)
|
||||
{
|
||||
return xdr_decode_netobj(p, oh);
|
||||
}
|
||||
|
||||
static inline __be32 *
|
||||
nlm_encode_oh(__be32 *p, struct xdr_netobj *oh)
|
||||
{
|
||||
return xdr_encode_netobj(p, oh);
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
|
||||
static bool
|
||||
svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock)
|
||||
{
|
||||
struct file_lock *fl = &lock->fl;
|
||||
s32 start, len, end;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len,
|
||||
NLM_MAXSTRLEN))
|
||||
|| !(p = nlm_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm_decode_oh(p, &lock->oh)))
|
||||
return NULL;
|
||||
lock->svid = ntohl(*p++);
|
||||
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
|
||||
return false;
|
||||
if (!svcxdr_decode_fhandle(xdr, &lock->fh))
|
||||
return false;
|
||||
if (!svcxdr_decode_owner(xdr, &lock->oh))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &lock->svid) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &start) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
|
||||
locks_init_lock(fl);
|
||||
fl->fl_flags = FL_POSIX;
|
||||
fl->fl_type = F_RDLCK; /* as good as anything else */
|
||||
start = ntohl(*p++);
|
||||
len = ntohl(*p++);
|
||||
fl->fl_type = F_RDLCK;
|
||||
end = start + len - 1;
|
||||
|
||||
fl->fl_start = s32_to_loff_t(start);
|
||||
|
||||
if (len == 0 || end < 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = s32_to_loff_t(end);
|
||||
return p;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode result of a TEST/TEST_MSG call
|
||||
*/
|
||||
static __be32 *
|
||||
nlm_encode_testres(__be32 *p, struct nlm_res *resp)
|
||||
static bool
|
||||
svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock)
|
||||
{
|
||||
const struct file_lock *fl = &lock->fl;
|
||||
s32 start, len;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
||||
return NULL;
|
||||
*p++ = resp->status;
|
||||
|
||||
if (resp->status == nlm_lck_denied) {
|
||||
struct file_lock *fl = &resp->lock.fl;
|
||||
|
||||
*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
|
||||
*p++ = htonl(resp->lock.svid);
|
||||
|
||||
/* Encode owner handle. */
|
||||
if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
|
||||
return NULL;
|
||||
|
||||
/* exclusive */
|
||||
if (xdr_stream_encode_bool(xdr, fl->fl_type != F_RDLCK) < 0)
|
||||
return false;
|
||||
if (xdr_stream_encode_u32(xdr, lock->svid) < 0)
|
||||
return false;
|
||||
if (!svcxdr_encode_owner(xdr, &lock->oh))
|
||||
return false;
|
||||
start = loff_t_to_s32(fl->fl_start);
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
len = 0;
|
||||
else
|
||||
len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
|
||||
if (xdr_stream_encode_u32(xdr, start) < 0)
|
||||
return false;
|
||||
if (xdr_stream_encode_u32(xdr, len) < 0)
|
||||
return false;
|
||||
|
||||
*p++ = htonl(start);
|
||||
*p++ = htonl(len);
|
||||
return true;
|
||||
}
|
||||
|
||||
return p;
|
||||
static bool
|
||||
svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp)
|
||||
{
|
||||
if (!svcxdr_encode_stats(xdr, resp->status))
|
||||
return false;
|
||||
switch (resp->status) {
|
||||
case nlm_lck_denied:
|
||||
if (!svcxdr_encode_holder(xdr, &resp->lock))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* First, the server side XDR functions
|
||||
* Decode Call arguments
|
||||
*/
|
||||
int
|
||||
nlmsvc_decode_testargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
|
||||
bool
|
||||
nlmsvc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nlmsvc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_testres(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm_encode_testres(p, resp)))
|
||||
return 0;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &argp->block) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
argp->reclaim = ntohl(*p++);
|
||||
argp->state = ntohl(*p++);
|
||||
if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
|
||||
return false;
|
||||
argp->monitor = 1; /* monitor client by default */
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &argp->block) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = nlm_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
argp->lock.fl.fl_type = F_UNLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_argp;
|
||||
|
||||
if (!svcxdr_decode_cookie(xdr, &resp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_decode_stats(xdr, &resp->status))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nlmsvc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_reboot *argp = rqstp->rq_argp;
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
if (len > SM_MAXSTRLEN)
|
||||
return false;
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (!p)
|
||||
return false;
|
||||
argp->len = len;
|
||||
argp->mon = (char *)p;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
|
||||
return false;
|
||||
p = xdr_inline_decode(xdr, SM_PRIV_SIZE);
|
||||
if (!p)
|
||||
return false;
|
||||
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nlmsvc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
@ -268,85 +277,78 @@ nlmsvc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
locks_init_lock(&lock->fl);
|
||||
lock->svid = ~(u32)0;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN))
|
||||
|| !(p = nlm_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm_decode_oh(p, &lock->oh)))
|
||||
return 0;
|
||||
argp->fsm_mode = ntohl(*p++);
|
||||
argp->fsm_access = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
|
||||
return false;
|
||||
if (!svcxdr_decode_fhandle(xdr, &lock->fh))
|
||||
return false;
|
||||
if (!svcxdr_decode_owner(xdr, &lock->oh))
|
||||
return false;
|
||||
/* XXX: Range checks are missing in the original code */
|
||||
if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_shareres(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
*p++ = xdr_zero; /* sequence argument */
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_res(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_notify(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p)
|
||||
|
||||
/*
|
||||
* Encode Reply results
|
||||
*/
|
||||
|
||||
bool
|
||||
nlmsvc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_reboot *argp = rqstp->rq_argp;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
|
||||
p += XDR_QUADLEN(SM_PRIV_SIZE);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_res(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_argp;
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm_decode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
resp->status = *p++;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return svcxdr_encode_cookie(xdr, &resp->cookie) &&
|
||||
svcxdr_encode_testrply(xdr, resp);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_decode_void(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
return svcxdr_encode_cookie(xdr, &resp->cookie) &&
|
||||
svcxdr_encode_stats(xdr, resp->status);
|
||||
}
|
||||
|
||||
int
|
||||
nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlmsvc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!svcxdr_encode_cookie(xdr, &resp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_encode_stats(xdr, resp->status))
|
||||
return false;
|
||||
/* sequence */
|
||||
if (xdr_stream_encode_u32(xdr, 0) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
458
fs/lockd/xdr4.c
458
fs/lockd/xdr4.c
@ -18,14 +18,7 @@
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/lockd/lockd.h>
|
||||
|
||||
#define NLMDBG_FACILITY NLMDBG_XDR
|
||||
|
||||
static inline loff_t
|
||||
s64_to_loff_t(__s64 offset)
|
||||
{
|
||||
return (loff_t)offset;
|
||||
}
|
||||
|
||||
#include "svcxdr.h"
|
||||
|
||||
static inline s64
|
||||
loff_t_to_s64(loff_t offset)
|
||||
@ -40,223 +33,237 @@ loff_t_to_s64(loff_t offset)
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR functions for basic NLM types
|
||||
*/
|
||||
static __be32 *
|
||||
nlm4_decode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
void nlm4svc_set_file_lock_range(struct file_lock *fl, u64 off, u64 len)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = ntohl(*p++);
|
||||
|
||||
if(len==0)
|
||||
{
|
||||
c->len=4;
|
||||
memset(c->data, 0, 4); /* hockeypux brain damage */
|
||||
}
|
||||
else if(len<=NLM_MAXCOOKIELEN)
|
||||
{
|
||||
c->len=len;
|
||||
memcpy(c->data, p, len);
|
||||
p+=XDR_QUADLEN(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintk("lockd: bad cookie size %d (only cookies under "
|
||||
"%d bytes are supported.)\n",
|
||||
len, NLM_MAXCOOKIELEN);
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_encode_cookie(__be32 *p, struct nlm_cookie *c)
|
||||
{
|
||||
*p++ = htonl(c->len);
|
||||
memcpy(p, c->data, c->len);
|
||||
p+=XDR_QUADLEN(c->len);
|
||||
return p;
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_decode_fh(__be32 *p, struct nfs_fh *f)
|
||||
{
|
||||
memset(f->data, 0, sizeof(f->data));
|
||||
f->size = ntohl(*p++);
|
||||
if (f->size > NFS_MAXFHSIZE) {
|
||||
dprintk("lockd: bad fhandle size %d (should be <=%d)\n",
|
||||
f->size, NFS_MAXFHSIZE);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(f->data, p, f->size);
|
||||
return p + XDR_QUADLEN(f->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode and decode owner handle
|
||||
*/
|
||||
static __be32 *
|
||||
nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh)
|
||||
{
|
||||
return xdr_decode_netobj(p, oh);
|
||||
}
|
||||
|
||||
static __be32 *
|
||||
nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
|
||||
{
|
||||
struct file_lock *fl = &lock->fl;
|
||||
__u64 len, start;
|
||||
__s64 end;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN))
|
||||
|| !(p = nlm4_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm4_decode_oh(p, &lock->oh)))
|
||||
return NULL;
|
||||
lock->svid = ntohl(*p++);
|
||||
|
||||
locks_init_lock(fl);
|
||||
fl->fl_flags = FL_POSIX;
|
||||
fl->fl_type = F_RDLCK; /* as good as anything else */
|
||||
p = xdr_decode_hyper(p, &start);
|
||||
p = xdr_decode_hyper(p, &len);
|
||||
end = start + len - 1;
|
||||
|
||||
fl->fl_start = s64_to_loff_t(start);
|
||||
s64 end = off + len - 1;
|
||||
|
||||
fl->fl_start = off;
|
||||
if (len == 0 || end < 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = s64_to_loff_t(end);
|
||||
return p;
|
||||
fl->fl_end = end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode result of a TEST/TEST_MSG call
|
||||
* NLM file handles are defined by specification to be a variable-length
|
||||
* XDR opaque no longer than 1024 bytes. However, this implementation
|
||||
* limits their length to the size of an NFSv3 file handle.
|
||||
*/
|
||||
static __be32 *
|
||||
nlm4_encode_testres(__be32 *p, struct nlm_res *resp)
|
||||
static bool
|
||||
svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
|
||||
{
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
if (len > NFS_MAXFHSIZE)
|
||||
return false;
|
||||
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (!p)
|
||||
return false;
|
||||
fh->size = len;
|
||||
memcpy(fh->data, p, len);
|
||||
memset(fh->data + len, 0, sizeof(fh->data) - len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock)
|
||||
{
|
||||
struct file_lock *fl = &lock->fl;
|
||||
|
||||
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
|
||||
return false;
|
||||
if (!svcxdr_decode_fhandle(xdr, &lock->fh))
|
||||
return false;
|
||||
if (!svcxdr_decode_owner(xdr, &lock->oh))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &lock->svid) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u64(xdr, &lock->lock_start) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u64(xdr, &lock->lock_len) < 0)
|
||||
return false;
|
||||
|
||||
locks_init_lock(fl);
|
||||
fl->fl_flags = FL_POSIX;
|
||||
fl->fl_type = F_RDLCK;
|
||||
nlm4svc_set_file_lock_range(fl, lock->lock_start, lock->lock_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock)
|
||||
{
|
||||
const struct file_lock *fl = &lock->fl;
|
||||
s64 start, len;
|
||||
|
||||
dprintk("xdr: before encode_testres (p %p resp %p)\n", p, resp);
|
||||
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
|
||||
return NULL;
|
||||
*p++ = resp->status;
|
||||
|
||||
if (resp->status == nlm_lck_denied) {
|
||||
struct file_lock *fl = &resp->lock.fl;
|
||||
|
||||
*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
|
||||
*p++ = htonl(resp->lock.svid);
|
||||
|
||||
/* Encode owner handle. */
|
||||
if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
|
||||
return NULL;
|
||||
|
||||
/* exclusive */
|
||||
if (xdr_stream_encode_bool(xdr, fl->fl_type != F_RDLCK) < 0)
|
||||
return false;
|
||||
if (xdr_stream_encode_u32(xdr, lock->svid) < 0)
|
||||
return false;
|
||||
if (!svcxdr_encode_owner(xdr, &lock->oh))
|
||||
return false;
|
||||
start = loff_t_to_s64(fl->fl_start);
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
len = 0;
|
||||
else
|
||||
len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
|
||||
if (xdr_stream_encode_u64(xdr, start) < 0)
|
||||
return false;
|
||||
if (xdr_stream_encode_u64(xdr, len) < 0)
|
||||
return false;
|
||||
|
||||
p = xdr_encode_hyper(p, start);
|
||||
p = xdr_encode_hyper(p, len);
|
||||
dprintk("xdr: encode_testres (status %u pid %d type %d start %Ld end %Ld)\n",
|
||||
resp->status, (int)resp->lock.svid, fl->fl_type,
|
||||
(long long)fl->fl_start, (long long)fl->fl_end);
|
||||
return true;
|
||||
}
|
||||
|
||||
dprintk("xdr: after encode_testres (p %p resp %p)\n", p, resp);
|
||||
return p;
|
||||
static bool
|
||||
svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp)
|
||||
{
|
||||
if (!svcxdr_encode_stats(xdr, resp->status))
|
||||
return false;
|
||||
switch (resp->status) {
|
||||
case nlm_lck_denied:
|
||||
if (!svcxdr_encode_holder(xdr, &resp->lock))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* First, the server side XDR functions
|
||||
* Decode Call arguments
|
||||
*/
|
||||
int
|
||||
nlm4svc_decode_testargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
|
||||
bool
|
||||
nlm4svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nlm4svc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_testres(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm4_encode_testres(p, resp)))
|
||||
return 0;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &argp->block) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
argp->reclaim = ntohl(*p++);
|
||||
argp->state = ntohl(*p++);
|
||||
if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
|
||||
return false;
|
||||
argp->monitor = 1; /* monitor client by default */
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
u32 exclusive;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
|
||||
return 0;
|
||||
argp->block = ntohl(*p++);
|
||||
exclusive = ntohl(*p++);
|
||||
if (!(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &argp->block) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_bool(xdr, &exclusive) < 0)
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
if (exclusive)
|
||||
argp->lock.fl.fl_type = F_WRLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = nlm4_decode_lock(p, &argp->lock)))
|
||||
return 0;
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_decode_lock(xdr, &argp->lock))
|
||||
return false;
|
||||
argp->lock.fl.fl_type = F_UNLCK;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_argp;
|
||||
|
||||
if (!svcxdr_decode_cookie(xdr, &resp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_decode_stats(xdr, &resp->status))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nlm4svc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_reboot *argp = rqstp->rq_argp;
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &len) < 0)
|
||||
return false;
|
||||
if (len > SM_MAXSTRLEN)
|
||||
return false;
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (!p)
|
||||
return false;
|
||||
argp->len = len;
|
||||
argp->mon = (char *)p;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
|
||||
return false;
|
||||
p = xdr_inline_decode(xdr, SM_PRIV_SIZE);
|
||||
if (!p)
|
||||
return false;
|
||||
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nlm4svc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
@ -265,85 +272,78 @@ nlm4svc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
locks_init_lock(&lock->fl);
|
||||
lock->svid = ~(u32)0;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &argp->cookie))
|
||||
|| !(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN))
|
||||
|| !(p = nlm4_decode_fh(p, &lock->fh))
|
||||
|| !(p = nlm4_decode_oh(p, &lock->oh)))
|
||||
return 0;
|
||||
argp->fsm_mode = ntohl(*p++);
|
||||
argp->fsm_access = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
if (!svcxdr_decode_cookie(xdr, &argp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
|
||||
return false;
|
||||
if (!svcxdr_decode_fhandle(xdr, &lock->fh))
|
||||
return false;
|
||||
if (!svcxdr_decode_owner(xdr, &lock->oh))
|
||||
return false;
|
||||
/* XXX: Range checks are missing in the original code */
|
||||
if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0)
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_shareres(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
*p++ = xdr_zero; /* sequence argument */
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_res(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
*p++ = resp->status;
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_notify(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
||||
&lock->len, NLM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->state) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p)
|
||||
|
||||
/*
|
||||
* Encode Reply results
|
||||
*/
|
||||
|
||||
bool
|
||||
nlm4svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_reboot *argp = rqstp->rq_argp;
|
||||
|
||||
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
|
||||
return 0;
|
||||
argp->state = ntohl(*p++);
|
||||
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
|
||||
p += XDR_QUADLEN(SM_PRIV_SIZE);
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_res(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nlm_res *resp = rqstp->rq_argp;
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
|
||||
return 0;
|
||||
resp->status = *p++;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return svcxdr_encode_cookie(xdr, &resp->cookie) &&
|
||||
svcxdr_encode_testrply(xdr, resp);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_decode_void(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
return svcxdr_encode_cookie(xdr, &resp->cookie) &&
|
||||
svcxdr_encode_stats(xdr, resp->status);
|
||||
}
|
||||
|
||||
int
|
||||
nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p)
|
||||
bool
|
||||
nlm4svc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
struct nlm_res *resp = rqstp->rq_resp;
|
||||
|
||||
if (!svcxdr_encode_cookie(xdr, &resp->cookie))
|
||||
return false;
|
||||
if (!svcxdr_encode_stats(xdr, resp->status))
|
||||
return false;
|
||||
/* sequence */
|
||||
if (xdr_stream_encode_u32(xdr, 0) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
100
fs/locks.c
100
fs/locks.c
@ -251,7 +251,7 @@ locks_get_lock_context(struct inode *inode, int type)
|
||||
struct file_lock_context *ctx;
|
||||
|
||||
/* paired with cmpxchg() below */
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (likely(ctx) || type == F_UNLCK)
|
||||
goto out;
|
||||
|
||||
@ -270,7 +270,7 @@ locks_get_lock_context(struct inode *inode, int type)
|
||||
*/
|
||||
if (cmpxchg(&inode->i_flctx, NULL, ctx)) {
|
||||
kmem_cache_free(flctx_cache, ctx);
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
}
|
||||
out:
|
||||
trace_locks_get_lock_context(inode, type, ctx);
|
||||
@ -323,7 +323,7 @@ locks_check_ctx_file_list(struct file *filp, struct list_head *list,
|
||||
void
|
||||
locks_free_lock_context(struct inode *inode)
|
||||
{
|
||||
struct file_lock_context *ctx = inode->i_flctx;
|
||||
struct file_lock_context *ctx = locks_inode_context(inode);
|
||||
|
||||
if (unlikely(ctx)) {
|
||||
locks_check_ctx_lists(inode);
|
||||
@ -376,6 +376,34 @@ void locks_release_private(struct file_lock *fl)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(locks_release_private);
|
||||
|
||||
/**
|
||||
* locks_owner_has_blockers - Check for blocking lock requests
|
||||
* @flctx: file lock context
|
||||
* @owner: lock owner
|
||||
*
|
||||
* Return values:
|
||||
* %true: @owner has at least one blocker
|
||||
* %false: @owner has no blockers
|
||||
*/
|
||||
bool locks_owner_has_blockers(struct file_lock_context *flctx,
|
||||
fl_owner_t owner)
|
||||
{
|
||||
struct file_lock *fl;
|
||||
|
||||
spin_lock(&flctx->flc_lock);
|
||||
list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
|
||||
if (fl->fl_owner != owner)
|
||||
continue;
|
||||
if (!list_empty(&fl->fl_blocked_requests)) {
|
||||
spin_unlock(&flctx->flc_lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
spin_unlock(&flctx->flc_lock);
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(locks_owner_has_blockers);
|
||||
|
||||
/* Free a lock which is not in use. */
|
||||
void locks_free_lock(struct file_lock *fl)
|
||||
{
|
||||
@ -954,20 +982,33 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
|
||||
struct file_lock *cfl;
|
||||
struct file_lock_context *ctx;
|
||||
struct inode *inode = locks_inode(filp);
|
||||
void *owner;
|
||||
void (*func)(void);
|
||||
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx || list_empty_careful(&ctx->flc_posix)) {
|
||||
fl->fl_type = F_UNLCK;
|
||||
return;
|
||||
}
|
||||
|
||||
retry:
|
||||
spin_lock(&ctx->flc_lock);
|
||||
list_for_each_entry(cfl, &ctx->flc_posix, fl_list) {
|
||||
if (posix_locks_conflict(fl, cfl)) {
|
||||
if (!posix_locks_conflict(fl, cfl))
|
||||
continue;
|
||||
if (cfl->fl_lmops && cfl->fl_lmops->lm_lock_expirable
|
||||
&& (*cfl->fl_lmops->lm_lock_expirable)(cfl)) {
|
||||
owner = cfl->fl_lmops->lm_mod_owner;
|
||||
func = cfl->fl_lmops->lm_expire_lock;
|
||||
__module_get(owner);
|
||||
spin_unlock(&ctx->flc_lock);
|
||||
(*func)();
|
||||
module_put(owner);
|
||||
goto retry;
|
||||
}
|
||||
locks_copy_conflock(fl, cfl);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
fl->fl_type = F_UNLCK;
|
||||
out:
|
||||
spin_unlock(&ctx->flc_lock);
|
||||
@ -1140,6 +1181,8 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
|
||||
int error;
|
||||
bool added = false;
|
||||
LIST_HEAD(dispose);
|
||||
void *owner;
|
||||
void (*func)(void);
|
||||
|
||||
ctx = locks_get_lock_context(inode, request->fl_type);
|
||||
if (!ctx)
|
||||
@ -1158,6 +1201,7 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
|
||||
new_fl2 = locks_alloc_lock();
|
||||
}
|
||||
|
||||
retry:
|
||||
percpu_down_read(&file_rwsem);
|
||||
spin_lock(&ctx->flc_lock);
|
||||
/*
|
||||
@ -1169,6 +1213,17 @@ static int posix_lock_inode(struct inode *inode, struct file_lock *request,
|
||||
list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
|
||||
if (!posix_locks_conflict(request, fl))
|
||||
continue;
|
||||
if (fl->fl_lmops && fl->fl_lmops->lm_lock_expirable
|
||||
&& (*fl->fl_lmops->lm_lock_expirable)(fl)) {
|
||||
owner = fl->fl_lmops->lm_mod_owner;
|
||||
func = fl->fl_lmops->lm_expire_lock;
|
||||
__module_get(owner);
|
||||
spin_unlock(&ctx->flc_lock);
|
||||
percpu_up_read(&file_rwsem);
|
||||
(*func)();
|
||||
module_put(owner);
|
||||
goto retry;
|
||||
}
|
||||
if (conflock)
|
||||
locks_copy_conflock(conflock, fl);
|
||||
error = -EAGAIN;
|
||||
@ -1619,7 +1674,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
|
||||
new_fl->fl_flags = type;
|
||||
|
||||
/* typically we will check that ctx is non-NULL before calling */
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx) {
|
||||
WARN_ON_ONCE(1);
|
||||
goto free_lock;
|
||||
@ -1724,7 +1779,7 @@ void lease_get_mtime(struct inode *inode, struct timespec64 *time)
|
||||
struct file_lock_context *ctx;
|
||||
struct file_lock *fl;
|
||||
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (ctx && !list_empty_careful(&ctx->flc_lease)) {
|
||||
spin_lock(&ctx->flc_lock);
|
||||
fl = list_first_entry_or_null(&ctx->flc_lease,
|
||||
@ -1770,7 +1825,7 @@ int fcntl_getlease(struct file *filp)
|
||||
int type = F_UNLCK;
|
||||
LIST_HEAD(dispose);
|
||||
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (ctx && !list_empty_careful(&ctx->flc_lease)) {
|
||||
percpu_down_read(&file_rwsem);
|
||||
spin_lock(&ctx->flc_lock);
|
||||
@ -1808,6 +1863,9 @@ check_conflicting_open(struct file *filp, const long arg, int flags)
|
||||
|
||||
if (flags & FL_LAYOUT)
|
||||
return 0;
|
||||
if (flags & FL_DELEG)
|
||||
/* We leave these checks to the caller */
|
||||
return 0;
|
||||
|
||||
if (arg == F_RDLCK)
|
||||
return inode_is_open_for_write(inode) ? -EAGAIN : 0;
|
||||
@ -1956,7 +2014,7 @@ static int generic_delete_lease(struct file *filp, void *owner)
|
||||
struct file_lock_context *ctx;
|
||||
LIST_HEAD(dispose);
|
||||
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx) {
|
||||
trace_generic_delete_lease(inode, NULL);
|
||||
return error;
|
||||
@ -2536,14 +2594,15 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
|
||||
*/
|
||||
if (!error && file_lock->fl_type != F_UNLCK &&
|
||||
!(file_lock->fl_flags & FL_OFDLCK)) {
|
||||
struct files_struct *files = current->files;
|
||||
/*
|
||||
* We need that spin_lock here - it prevents reordering between
|
||||
* update of i_flctx->flc_posix and check for it done in
|
||||
* close(). rcu_read_lock() wouldn't do.
|
||||
*/
|
||||
spin_lock(¤t->files->file_lock);
|
||||
f = fcheck(fd);
|
||||
spin_unlock(¤t->files->file_lock);
|
||||
spin_lock(&files->file_lock);
|
||||
f = files_lookup_fd_locked(files, fd);
|
||||
spin_unlock(&files->file_lock);
|
||||
if (f != filp) {
|
||||
file_lock->fl_type = F_UNLCK;
|
||||
error = do_lock_file_wait(filp, cmd, file_lock);
|
||||
@ -2667,14 +2726,15 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
|
||||
*/
|
||||
if (!error && file_lock->fl_type != F_UNLCK &&
|
||||
!(file_lock->fl_flags & FL_OFDLCK)) {
|
||||
struct files_struct *files = current->files;
|
||||
/*
|
||||
* We need that spin_lock here - it prevents reordering between
|
||||
* update of i_flctx->flc_posix and check for it done in
|
||||
* close(). rcu_read_lock() wouldn't do.
|
||||
*/
|
||||
spin_lock(¤t->files->file_lock);
|
||||
f = fcheck(fd);
|
||||
spin_unlock(¤t->files->file_lock);
|
||||
spin_lock(&files->file_lock);
|
||||
f = files_lookup_fd_locked(files, fd);
|
||||
spin_unlock(&files->file_lock);
|
||||
if (f != filp) {
|
||||
file_lock->fl_type = F_UNLCK;
|
||||
error = do_lock_file_wait(filp, cmd, file_lock);
|
||||
@ -2705,7 +2765,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
|
||||
* posix_lock_file(). Another process could be setting a lock on this
|
||||
* file at the same time, but we wouldn't remove that lock anyway.
|
||||
*/
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx || list_empty(&ctx->flc_posix))
|
||||
return;
|
||||
|
||||
@ -2778,7 +2838,7 @@ void locks_remove_file(struct file *filp)
|
||||
{
|
||||
struct file_lock_context *ctx;
|
||||
|
||||
ctx = smp_load_acquire(&locks_inode(filp)->i_flctx);
|
||||
ctx = locks_inode_context(locks_inode(filp));
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
@ -2825,7 +2885,7 @@ bool vfs_inode_has_locks(struct inode *inode)
|
||||
struct file_lock_context *ctx;
|
||||
bool ret;
|
||||
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx)
|
||||
return false;
|
||||
|
||||
@ -2970,7 +3030,7 @@ void show_fd_locks(struct seq_file *f,
|
||||
struct file_lock_context *ctx;
|
||||
int id = 0;
|
||||
|
||||
ctx = smp_load_acquire(&inode->i_flctx);
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
|
21
fs/namei.c
21
fs/namei.c
@ -4361,11 +4361,14 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
|
||||
* ->i_mutex on parents, which works but leads to some truly excessive
|
||||
* locking].
|
||||
*/
|
||||
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
struct inode **delegated_inode, unsigned int flags)
|
||||
int vfs_rename(struct renamedata *rd)
|
||||
{
|
||||
int error;
|
||||
struct inode *old_dir = rd->old_dir, *new_dir = rd->new_dir;
|
||||
struct dentry *old_dentry = rd->old_dentry;
|
||||
struct dentry *new_dentry = rd->new_dentry;
|
||||
struct inode **delegated_inode = rd->delegated_inode;
|
||||
unsigned int flags = rd->flags;
|
||||
bool is_dir = d_is_dir(old_dentry);
|
||||
struct inode *source = old_dentry->d_inode;
|
||||
struct inode *target = new_dentry->d_inode;
|
||||
@ -4513,6 +4516,7 @@ EXPORT_SYMBOL_NS(vfs_rename, ANDROID_GKI_VFS_EXPORT_ONLY);
|
||||
int do_renameat2(int olddfd, struct filename *from, int newdfd,
|
||||
struct filename *to, unsigned int flags)
|
||||
{
|
||||
struct renamedata rd;
|
||||
struct dentry *old_dentry, *new_dentry;
|
||||
struct dentry *trap;
|
||||
struct path old_path, new_path;
|
||||
@ -4616,9 +4620,14 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
|
||||
&new_path, new_dentry, flags);
|
||||
if (error)
|
||||
goto exit5;
|
||||
error = vfs_rename(old_path.dentry->d_inode, old_dentry,
|
||||
new_path.dentry->d_inode, new_dentry,
|
||||
&delegated_inode, flags);
|
||||
|
||||
rd.old_dir = old_path.dentry->d_inode;
|
||||
rd.old_dentry = old_dentry;
|
||||
rd.new_dir = new_path.dentry->d_inode;
|
||||
rd.new_dentry = new_dentry;
|
||||
rd.delegated_inode = &delegated_inode;
|
||||
rd.flags = flags;
|
||||
error = vfs_rename(&rd);
|
||||
exit5:
|
||||
dput(new_dentry);
|
||||
exit4:
|
||||
|
@ -699,7 +699,7 @@ bl_alloc_lseg(struct pnfs_layout_hdr *lo, struct nfs4_layoutget_res *lgr,
|
||||
|
||||
xdr_init_decode_pages(&xdr, &buf,
|
||||
lgr->layoutp->pages, lgr->layoutp->len);
|
||||
xdr_set_scratch_buffer(&xdr, page_address(scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(&xdr, scratch);
|
||||
|
||||
status = -EIO;
|
||||
p = xdr_inline_decode(&xdr, 4);
|
||||
|
@ -510,7 +510,7 @@ bl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
goto out;
|
||||
|
||||
xdr_init_decode_pages(&xdr, &buf, pdev->pages, pdev->pglen);
|
||||
xdr_set_scratch_buffer(&xdr, page_address(scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(&xdr, scratch);
|
||||
|
||||
p = xdr_inline_decode(&xdr, sizeof(__be32));
|
||||
if (!p)
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/sunrpc/svcauth_gss.h>
|
||||
#include <linux/sunrpc/bc_xprt.h>
|
||||
|
||||
@ -45,7 +44,7 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
|
||||
int ret;
|
||||
struct nfs_net *nn = net_generic(net, nfs_net_id);
|
||||
|
||||
ret = svc_create_xprt(serv, "tcp", net, PF_INET,
|
||||
ret = svc_xprt_create(serv, "tcp", net, PF_INET,
|
||||
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
|
||||
cred);
|
||||
if (ret <= 0)
|
||||
@ -54,7 +53,7 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
|
||||
dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
|
||||
nn->nfs_callback_tcpport, PF_INET, net->ns.inum);
|
||||
|
||||
ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
|
||||
ret = svc_xprt_create(serv, "tcp", net, PF_INET6,
|
||||
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
|
||||
cred);
|
||||
if (ret > 0) {
|
||||
@ -81,9 +80,6 @@ nfs4_callback_svc(void *vrqstp)
|
||||
set_freezable();
|
||||
|
||||
while (!kthread_freezable_should_stop(NULL)) {
|
||||
|
||||
if (signal_pending(current))
|
||||
flush_signals(current);
|
||||
/*
|
||||
* Listen for a request on the socket
|
||||
*/
|
||||
@ -92,8 +88,8 @@ nfs4_callback_svc(void *vrqstp)
|
||||
continue;
|
||||
svc_process(rqstp);
|
||||
}
|
||||
|
||||
svc_exit_thread(rqstp);
|
||||
module_put_and_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -113,11 +109,7 @@ nfs41_callback_svc(void *vrqstp)
|
||||
set_freezable();
|
||||
|
||||
while (!kthread_freezable_should_stop(NULL)) {
|
||||
|
||||
if (signal_pending(current))
|
||||
flush_signals(current);
|
||||
|
||||
prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
|
||||
prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_IDLE);
|
||||
spin_lock_bh(&serv->sv_cb_lock);
|
||||
if (!list_empty(&serv->sv_cb_list)) {
|
||||
req = list_first_entry(&serv->sv_cb_list,
|
||||
@ -132,12 +124,12 @@ nfs41_callback_svc(void *vrqstp)
|
||||
} else {
|
||||
spin_unlock_bh(&serv->sv_cb_lock);
|
||||
if (!kthread_should_stop())
|
||||
schedule();
|
||||
freezable_schedule();
|
||||
finish_wait(&serv->sv_cb_waitq, &wq);
|
||||
}
|
||||
}
|
||||
|
||||
svc_exit_thread(rqstp);
|
||||
module_put_and_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -169,12 +161,12 @@ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
|
||||
if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS)
|
||||
nrservs = NFS4_MIN_NR_CALLBACK_THREADS;
|
||||
|
||||
if (serv->sv_nrthreads-1 == nrservs)
|
||||
if (serv->sv_nrthreads == nrservs)
|
||||
return 0;
|
||||
|
||||
ret = serv->sv_ops->svo_setup(serv, NULL, nrservs);
|
||||
ret = svc_set_num_threads(serv, NULL, nrservs);
|
||||
if (ret) {
|
||||
serv->sv_ops->svo_setup(serv, NULL, 0);
|
||||
svc_set_num_threads(serv, NULL, 0);
|
||||
return ret;
|
||||
}
|
||||
dprintk("nfs_callback_up: service started\n");
|
||||
@ -189,7 +181,7 @@ static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struc
|
||||
return;
|
||||
|
||||
dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum);
|
||||
svc_shutdown_net(serv, net);
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
}
|
||||
|
||||
static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
|
||||
@ -232,59 +224,17 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct svc_serv_ops nfs40_cb_sv_ops = {
|
||||
.svo_function = nfs4_callback_svc,
|
||||
.svo_enqueue_xprt = svc_xprt_do_enqueue,
|
||||
.svo_setup = svc_set_num_threads_sync,
|
||||
.svo_module = THIS_MODULE,
|
||||
};
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
static const struct svc_serv_ops nfs41_cb_sv_ops = {
|
||||
.svo_function = nfs41_callback_svc,
|
||||
.svo_enqueue_xprt = svc_xprt_do_enqueue,
|
||||
.svo_setup = svc_set_num_threads_sync,
|
||||
.svo_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
|
||||
[0] = &nfs40_cb_sv_ops,
|
||||
[1] = &nfs41_cb_sv_ops,
|
||||
};
|
||||
#else
|
||||
static const struct svc_serv_ops *nfs4_cb_sv_ops[] = {
|
||||
[0] = &nfs40_cb_sv_ops,
|
||||
[1] = NULL,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct svc_serv *nfs_callback_create_svc(int minorversion)
|
||||
{
|
||||
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
|
||||
const struct svc_serv_ops *sv_ops;
|
||||
int (*threadfn)(void *data);
|
||||
struct svc_serv *serv;
|
||||
|
||||
/*
|
||||
* Check whether we're already up and running.
|
||||
*/
|
||||
if (cb_info->serv) {
|
||||
/*
|
||||
* Note: increase service usage, because later in case of error
|
||||
* svc_destroy() will be called.
|
||||
*/
|
||||
svc_get(cb_info->serv);
|
||||
return cb_info->serv;
|
||||
}
|
||||
|
||||
switch (minorversion) {
|
||||
case 0:
|
||||
sv_ops = nfs4_cb_sv_ops[0];
|
||||
break;
|
||||
default:
|
||||
sv_ops = nfs4_cb_sv_ops[1];
|
||||
}
|
||||
|
||||
if (sv_ops == NULL)
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
if (cb_info->serv)
|
||||
return svc_get(cb_info->serv);
|
||||
|
||||
/*
|
||||
* Sanity check: if there's no task,
|
||||
@ -294,7 +244,16 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
|
||||
printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
|
||||
cb_info->users);
|
||||
|
||||
serv = svc_create_pooled(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, sv_ops);
|
||||
threadfn = nfs4_callback_svc;
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
if (minorversion)
|
||||
threadfn = nfs41_callback_svc;
|
||||
#else
|
||||
if (minorversion)
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
#endif
|
||||
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
|
||||
threadfn);
|
||||
if (!serv) {
|
||||
printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -335,16 +294,10 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
|
||||
goto err_start;
|
||||
|
||||
cb_info->users++;
|
||||
/*
|
||||
* svc_create creates the svc_serv with sv_nrthreads == 1, and then
|
||||
* svc_prepare_thread increments that. So we need to call svc_destroy
|
||||
* on both success and failure so that the refcount is 1 when the
|
||||
* thread exits.
|
||||
*/
|
||||
err_net:
|
||||
if (!cb_info->users)
|
||||
cb_info->serv = NULL;
|
||||
svc_destroy(serv);
|
||||
svc_put(serv);
|
||||
err_create:
|
||||
mutex_unlock(&nfs_callback_mutex);
|
||||
return ret;
|
||||
@ -369,8 +322,8 @@ void nfs_callback_down(int minorversion, struct net *net)
|
||||
cb_info->users--;
|
||||
if (cb_info->users == 0) {
|
||||
svc_get(serv);
|
||||
serv->sv_ops->svo_setup(serv, NULL, 0);
|
||||
svc_destroy(serv);
|
||||
svc_set_num_threads(serv, NULL, 0);
|
||||
svc_put(serv);
|
||||
dprintk("nfs_callback_down: service destroyed\n");
|
||||
cb_info->serv = NULL;
|
||||
}
|
||||
@ -429,6 +382,8 @@ check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
|
||||
*/
|
||||
static int nfs_callback_authenticate(struct svc_rqst *rqstp)
|
||||
{
|
||||
rqstp->rq_auth_stat = rpc_autherr_badcred;
|
||||
|
||||
switch (rqstp->rq_authop->flavour) {
|
||||
case RPC_AUTH_NULL:
|
||||
if (rqstp->rq_proc != CB_NULL)
|
||||
@ -439,6 +394,8 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
|
||||
if (svc_is_backchannel(rqstp))
|
||||
return SVC_DENIED;
|
||||
}
|
||||
|
||||
rqstp->rq_auth_stat = rpc_auth_ok;
|
||||
return SVC_OK;
|
||||
}
|
||||
|
||||
|
@ -63,14 +63,13 @@ static __be32 nfs4_callback_null(struct svc_rqst *rqstp)
|
||||
return htonl(NFS4_OK);
|
||||
}
|
||||
|
||||
static int nfs4_decode_void(struct svc_rqst *rqstp, __be32 *p)
|
||||
/*
|
||||
* svc_process_common() looks for an XDR encoder to know when
|
||||
* not to drop a Reply.
|
||||
*/
|
||||
static bool nfs4_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
|
||||
static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len,
|
||||
@ -984,7 +983,17 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)
|
||||
|
||||
out_invalidcred:
|
||||
pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n");
|
||||
return svc_return_autherr(rqstp, rpc_autherr_badcred);
|
||||
rqstp->rq_auth_stat = rpc_autherr_badcred;
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static int
|
||||
nfs_callback_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
||||
{
|
||||
const struct svc_procedure *procp = rqstp->rq_procinfo;
|
||||
|
||||
*statp = procp->pc_func(rqstp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1053,16 +1062,18 @@ static struct callback_op callback_ops[] = {
|
||||
static const struct svc_procedure nfs4_callback_procedures1[] = {
|
||||
[CB_NULL] = {
|
||||
.pc_func = nfs4_callback_null,
|
||||
.pc_decode = nfs4_decode_void,
|
||||
.pc_encode = nfs4_encode_void,
|
||||
.pc_xdrressize = 1,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[CB_COMPOUND] = {
|
||||
.pc_func = nfs4_callback_compound,
|
||||
.pc_encode = nfs4_encode_void,
|
||||
.pc_argsize = 256,
|
||||
.pc_argzero = 256,
|
||||
.pc_ressize = 256,
|
||||
.pc_xdrressize = NFS4_CALLBACK_BUFSIZE,
|
||||
.pc_name = "COMPOUND",
|
||||
}
|
||||
};
|
||||
|
||||
@ -1073,7 +1084,7 @@ const struct svc_version nfs4_callback_version1 = {
|
||||
.vs_proc = nfs4_callback_procedures1,
|
||||
.vs_count = nfs4_callback_count1,
|
||||
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
|
||||
.vs_dispatch = NULL,
|
||||
.vs_dispatch = nfs_callback_dispatch,
|
||||
.vs_hidden = true,
|
||||
.vs_need_cong_ctrl = true,
|
||||
};
|
||||
@ -1085,7 +1096,7 @@ const struct svc_version nfs4_callback_version4 = {
|
||||
.vs_proc = nfs4_callback_procedures1,
|
||||
.vs_count = nfs4_callback_count4,
|
||||
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
|
||||
.vs_dispatch = NULL,
|
||||
.vs_dispatch = nfs_callback_dispatch,
|
||||
.vs_hidden = true,
|
||||
.vs_need_cong_ctrl = true,
|
||||
};
|
||||
|
@ -576,7 +576,7 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
||||
goto out_nopages;
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, xdr_pages, buflen);
|
||||
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
|
||||
do {
|
||||
if (entry->label)
|
||||
|
@ -167,8 +167,25 @@ nfs_get_parent(struct dentry *dentry)
|
||||
return parent;
|
||||
}
|
||||
|
||||
static u64 nfs_fetch_iversion(struct inode *inode)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
|
||||
if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
|
||||
NFS_INO_REVAL_PAGECACHE))
|
||||
__nfs_revalidate_inode(server, inode);
|
||||
return inode_peek_iversion_raw(inode);
|
||||
}
|
||||
|
||||
const struct export_operations nfs_export_ops = {
|
||||
.encode_fh = nfs_encode_fh,
|
||||
.fh_to_dentry = nfs_fh_to_dentry,
|
||||
.get_parent = nfs_get_parent,
|
||||
.fetch_iversion = nfs_fetch_iversion,
|
||||
.flags = EXPORT_OP_NOWCC |
|
||||
EXPORT_OP_NOSUBTREECHK |
|
||||
EXPORT_OP_CLOSE_BEFORE_UNLINK |
|
||||
EXPORT_OP_REMOTE_FS |
|
||||
EXPORT_OP_NOATOMIC_ATTR |
|
||||
EXPORT_OP_FLUSH_ON_CLOSE,
|
||||
};
|
||||
|
@ -798,6 +798,9 @@ int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSLOCK);
|
||||
|
||||
if (fl->fl_flags & FL_RECLAIM)
|
||||
return -ENOGRACE;
|
||||
|
||||
/* No mandatory locks over NFS */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
goto out_err;
|
||||
|
@ -293,8 +293,6 @@ static void filelayout_read_call_done(struct rpc_task *task, void *data)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = data;
|
||||
|
||||
dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
|
||||
|
||||
if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
|
||||
task->tk_status == 0) {
|
||||
nfs41_sequence_done(task, &hdr->res.seq_res);
|
||||
@ -666,7 +664,7 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
|
||||
return -ENOMEM;
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len);
|
||||
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
|
||||
/* 20 = ufl_util (4), first_stripe_index (4), pattern_offset (8),
|
||||
* num_fh (4) */
|
||||
|
@ -82,7 +82,7 @@ nfs4_fl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
goto out_err;
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen);
|
||||
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
|
||||
/* Get the stripe count (number of stripe index) */
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
|
@ -378,7 +378,7 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh,
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages,
|
||||
lgr->layoutp->len);
|
||||
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
|
||||
/* stripe unit and mirror_array_cnt */
|
||||
rc = -EIO;
|
||||
@ -1419,8 +1419,6 @@ static void ff_layout_read_call_done(struct rpc_task *task, void *data)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = data;
|
||||
|
||||
dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
|
||||
|
||||
if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
|
||||
task->tk_status == 0) {
|
||||
nfs4_sequence_done(task, &hdr->res.seq_res);
|
||||
|
@ -69,7 +69,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
INIT_LIST_HEAD(&dsaddrs);
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen);
|
||||
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
|
||||
/* multipath count */
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
|
@ -1536,7 +1536,7 @@ static int nfs4_xdr_dec_listxattrs(struct rpc_rqst *rqstp,
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
xdr_set_scratch_buffer(xdr, page_address(res->scratch), PAGE_SIZE);
|
||||
xdr_set_scratch_page(xdr, res->scratch);
|
||||
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
|
@ -2757,7 +2757,7 @@ static int nfs4_run_state_manager(void *ptr)
|
||||
goto again;
|
||||
|
||||
nfs_put_client(clp);
|
||||
module_put_and_exit(0);
|
||||
module_put_and_kthread_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -6404,10 +6404,8 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
if (res->acl_scratch != NULL) {
|
||||
void *p = page_address(res->acl_scratch);
|
||||
xdr_set_scratch_buffer(xdr, p, PAGE_SIZE);
|
||||
}
|
||||
if (res->acl_scratch != NULL)
|
||||
xdr_set_scratch_page(xdr, res->acl_scratch);
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
|
@ -870,9 +870,6 @@ static void nfs_pgio_result(struct rpc_task *task, void *calldata)
|
||||
struct nfs_pgio_header *hdr = calldata;
|
||||
struct inode *inode = hdr->inode;
|
||||
|
||||
dprintk("NFS: %s: %5u, (status %d)\n", __func__,
|
||||
task->tk_pid, task->tk_status);
|
||||
|
||||
if (hdr->rw_ops->rw_done(task, hdr, inode) != 0)
|
||||
return;
|
||||
if (task->tk_status < 0)
|
||||
|
@ -86,9 +86,11 @@ const struct super_operations nfs_sops = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nfs_sops);
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
static const struct nfs_ssc_client_ops nfs_ssc_clnt_ops_tbl = {
|
||||
.sco_sb_deactive = nfs_sb_deactive,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_V4)
|
||||
static int __init register_nfs4_fs(void)
|
||||
@ -111,6 +113,7 @@ static void unregister_nfs4_fs(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
static void nfs_ssc_register_ops(void)
|
||||
{
|
||||
nfs_ssc_register(&nfs_ssc_clnt_ops_tbl);
|
||||
@ -120,6 +123,7 @@ static void nfs_ssc_unregister_ops(void)
|
||||
{
|
||||
nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl);
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_2 */
|
||||
|
||||
static struct shrinker acl_shrinker = {
|
||||
.count_objects = nfs_access_cache_count,
|
||||
@ -148,7 +152,9 @@ int __init register_nfs_fs(void)
|
||||
ret = register_shrinker(&acl_shrinker);
|
||||
if (ret < 0)
|
||||
goto error_3;
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
nfs_ssc_register_ops();
|
||||
#endif
|
||||
return 0;
|
||||
error_3:
|
||||
nfs_unregister_sysctl();
|
||||
@ -168,7 +174,9 @@ void __exit unregister_nfs_fs(void)
|
||||
unregister_shrinker(&acl_shrinker);
|
||||
nfs_unregister_sysctl();
|
||||
unregister_nfs4_fs();
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
nfs_ssc_unregister_ops();
|
||||
#endif
|
||||
unregister_filesystem(&nfs_fs_type);
|
||||
}
|
||||
|
||||
|
@ -1809,9 +1809,6 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs_commit_data *data = calldata;
|
||||
|
||||
dprintk("NFS: %5u nfs_commit_done (status %d)\n",
|
||||
task->tk_pid, task->tk_status);
|
||||
|
||||
/* Call the NFS version-specific code */
|
||||
NFS_PROTO(data->inode)->commit_done(task, data);
|
||||
trace_nfs_commit_done(task, data);
|
||||
|
@ -7,4 +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
|
||||
obj-$(CONFIG_NFS_V4_2_SSC_HELPER) += nfs_ssc.o
|
||||
|
@ -1,7 +1,5 @@
|
||||
// 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>
|
||||
|
@ -136,6 +136,77 @@ int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfsacl_encode);
|
||||
|
||||
/**
|
||||
* nfs_stream_encode_acl - Encode an NFSv3 ACL
|
||||
*
|
||||
* @xdr: an xdr_stream positioned to receive an encoded ACL
|
||||
* @inode: inode of file whose ACL this is
|
||||
* @acl: posix_acl to encode
|
||||
* @encode_entries: whether to encode ACEs as well
|
||||
* @typeflag: ACL type: NFS_ACL_DEFAULT or zero
|
||||
*
|
||||
* Return values:
|
||||
* %false: The ACL could not be encoded
|
||||
* %true: @xdr is advanced to the next available position
|
||||
*/
|
||||
bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
|
||||
struct posix_acl *acl, int encode_entries,
|
||||
int typeflag)
|
||||
{
|
||||
const size_t elem_size = XDR_UNIT * 3;
|
||||
u32 entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
|
||||
struct nfsacl_encode_desc nfsacl_desc = {
|
||||
.desc = {
|
||||
.elem_size = elem_size,
|
||||
.array_len = encode_entries ? entries : 0,
|
||||
.xcode = xdr_nfsace_encode,
|
||||
},
|
||||
.acl = acl,
|
||||
.typeflag = typeflag,
|
||||
.uid = inode->i_uid,
|
||||
.gid = inode->i_gid,
|
||||
};
|
||||
struct nfsacl_simple_acl aclbuf;
|
||||
unsigned int base;
|
||||
int err;
|
||||
|
||||
if (entries > NFS_ACL_MAX_ENTRIES)
|
||||
return false;
|
||||
if (xdr_stream_encode_u32(xdr, entries) < 0)
|
||||
return false;
|
||||
|
||||
if (encode_entries && acl && acl->a_count == 3) {
|
||||
struct posix_acl *acl2 = &aclbuf.acl;
|
||||
|
||||
/* Avoid the use of posix_acl_alloc(). nfsacl_encode() is
|
||||
* invoked in contexts where a memory allocation failure is
|
||||
* fatal. Fortunately this fake ACL is small enough to
|
||||
* construct on the stack. */
|
||||
posix_acl_init(acl2, 4);
|
||||
|
||||
/* Insert entries in canonical order: other orders seem
|
||||
to confuse Solaris VxFS. */
|
||||
acl2->a_entries[0] = acl->a_entries[0]; /* ACL_USER_OBJ */
|
||||
acl2->a_entries[1] = acl->a_entries[1]; /* ACL_GROUP_OBJ */
|
||||
acl2->a_entries[2] = acl->a_entries[1]; /* ACL_MASK */
|
||||
acl2->a_entries[2].e_tag = ACL_MASK;
|
||||
acl2->a_entries[3] = acl->a_entries[2]; /* ACL_OTHER */
|
||||
nfsacl_desc.acl = acl2;
|
||||
}
|
||||
|
||||
base = xdr_stream_pos(xdr);
|
||||
if (!xdr_reserve_space(xdr, XDR_UNIT +
|
||||
elem_size * nfsacl_desc.desc.array_len))
|
||||
return false;
|
||||
err = xdr_encode_array2(xdr->buf, base, &nfsacl_desc.desc);
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_stream_encode_acl);
|
||||
|
||||
|
||||
struct nfsacl_decode_desc {
|
||||
struct xdr_array2_desc desc;
|
||||
unsigned int count;
|
||||
@ -295,3 +366,55 @@ int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
|
||||
nfsacl_desc.desc.array_len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfsacl_decode);
|
||||
|
||||
/**
|
||||
* nfs_stream_decode_acl - Decode an NFSv3 ACL
|
||||
*
|
||||
* @xdr: an xdr_stream positioned at an encoded ACL
|
||||
* @aclcnt: OUT: count of ACEs in decoded posix_acl
|
||||
* @pacl: OUT: a dynamically-allocated buffer containing the decoded posix_acl
|
||||
*
|
||||
* Return values:
|
||||
* %false: The encoded ACL is not valid
|
||||
* %true: @pacl contains a decoded ACL, and @xdr is advanced
|
||||
*
|
||||
* On a successful return, caller must release *pacl using posix_acl_release().
|
||||
*/
|
||||
bool nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
|
||||
struct posix_acl **pacl)
|
||||
{
|
||||
const size_t elem_size = XDR_UNIT * 3;
|
||||
struct nfsacl_decode_desc nfsacl_desc = {
|
||||
.desc = {
|
||||
.elem_size = elem_size,
|
||||
.xcode = pacl ? xdr_nfsace_decode : NULL,
|
||||
},
|
||||
};
|
||||
unsigned int base;
|
||||
u32 entries;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &entries) < 0)
|
||||
return false;
|
||||
if (entries > NFS_ACL_MAX_ENTRIES)
|
||||
return false;
|
||||
|
||||
base = xdr_stream_pos(xdr);
|
||||
if (!xdr_inline_decode(xdr, XDR_UNIT + elem_size * entries))
|
||||
return false;
|
||||
nfsacl_desc.desc.array_maxlen = entries;
|
||||
if (xdr_decode_array2(xdr->buf, base, &nfsacl_desc.desc))
|
||||
return false;
|
||||
|
||||
if (pacl) {
|
||||
if (entries != nfsacl_desc.desc.array_len ||
|
||||
posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
|
||||
posix_acl_release(nfsacl_desc.acl);
|
||||
return false;
|
||||
}
|
||||
*pacl = nfsacl_desc.acl;
|
||||
}
|
||||
if (aclcnt)
|
||||
*aclcnt = entries;
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_stream_decode_acl);
|
||||
|
@ -8,6 +8,7 @@ config NFSD
|
||||
select SUNRPC
|
||||
select EXPORTFS
|
||||
select NFS_ACL_SUPPORT if NFSD_V2_ACL
|
||||
select NFS_ACL_SUPPORT if NFSD_V3_ACL
|
||||
depends on MULTIUSER
|
||||
help
|
||||
Choose Y here if you want to allow other computers to access
|
||||
@ -26,28 +27,29 @@ config NFSD
|
||||
|
||||
Below you can choose which versions of the NFS protocol are
|
||||
available to clients mounting the NFS server on this system.
|
||||
Support for NFS version 2 (RFC 1094) is always available when
|
||||
Support for NFS version 3 (RFC 1813) is always available when
|
||||
CONFIG_NFSD is selected.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config NFSD_V2_ACL
|
||||
bool
|
||||
depends on NFSD
|
||||
|
||||
config NFSD_V3
|
||||
bool "NFS server support for NFS version 3"
|
||||
config NFSD_V2
|
||||
bool "NFS server support for NFS version 2 (DEPRECATED)"
|
||||
depends on NFSD
|
||||
default n
|
||||
help
|
||||
This option enables support in your system's NFS server for
|
||||
version 3 of the NFS protocol (RFC 1813).
|
||||
NFSv2 (RFC 1094) was the first publicly-released version of NFS.
|
||||
Unless you are hosting ancient (1990's era) NFS clients, you don't
|
||||
need this.
|
||||
|
||||
If unsure, say Y.
|
||||
If unsure, say N.
|
||||
|
||||
config NFSD_V2_ACL
|
||||
bool "NFS server support for the NFSv2 ACL protocol extension"
|
||||
depends on NFSD_V2
|
||||
|
||||
config NFSD_V3_ACL
|
||||
bool "NFS server support for the NFSv3 ACL protocol extension"
|
||||
depends on NFSD_V3
|
||||
select NFSD_V2_ACL
|
||||
depends on NFSD
|
||||
help
|
||||
Solaris NFS servers support an auxiliary NFSv3 ACL protocol that
|
||||
never became an official part of the NFS version 3 protocol.
|
||||
@ -70,13 +72,13 @@ config NFSD_V3_ACL
|
||||
config NFSD_V4
|
||||
bool "NFS server support for NFS version 4"
|
||||
depends on NFSD && PROC_FS
|
||||
select NFSD_V3
|
||||
select FS_POSIX_ACL
|
||||
select SUNRPC_GSS
|
||||
select CRYPTO
|
||||
select CRYPTO_MD5
|
||||
select CRYPTO_SHA256
|
||||
select GRACE_PERIOD
|
||||
select NFS_V4_2_SSC_HELPER if NFS_V4_2
|
||||
help
|
||||
This option enables support in your system's NFS server for
|
||||
version 4 of the NFS protocol (RFC 3530).
|
||||
@ -98,7 +100,7 @@ config NFSD_BLOCKLAYOUT
|
||||
help
|
||||
This option enables support for the exporting pNFS block layouts
|
||||
in the kernel's NFS server. The pNFS block layout enables NFS
|
||||
clients to directly perform I/O to block devices accesible to both
|
||||
clients to directly perform I/O to block devices accessible to both
|
||||
the server and the clients. See RFC 5663 for more details.
|
||||
|
||||
If unsure, say N.
|
||||
@ -112,7 +114,7 @@ config NFSD_SCSILAYOUT
|
||||
help
|
||||
This option enables support for the exporting pNFS SCSI layouts
|
||||
in the kernel's NFS server. The pNFS SCSI layout enables NFS
|
||||
clients to directly perform I/O to SCSI devices accesible to both
|
||||
clients to directly perform I/O to SCSI devices accessible to both
|
||||
the server and the clients. See draft-ietf-nfsv4-scsi-layout for
|
||||
more details.
|
||||
|
||||
@ -126,7 +128,7 @@ config NFSD_FLEXFILELAYOUT
|
||||
This option enables support for the exporting pNFS Flex File
|
||||
layouts in the kernel's NFS server. The pNFS Flex File layout
|
||||
enables NFS clients to directly perform I/O to NFSv3 devices
|
||||
accesible to both the server and the clients. See
|
||||
accessible to both the server and the clients. See
|
||||
draft-ietf-nfsv4-flex-files for more details.
|
||||
|
||||
Warning, this server implements the bare minimum functionality
|
||||
@ -137,7 +139,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
|
||||
depends on NFSD_V4 && NFS_V4_2
|
||||
help
|
||||
This option enables support for NFSv4.2 inter server to
|
||||
server copy where the destination server calls the NFSv4.2
|
||||
|
@ -10,11 +10,11 @@ obj-$(CONFIG_NFSD) += nfsd.o
|
||||
# this one should be compiled first, as the tracing macros can easily blow up
|
||||
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-y += nfssvc.o nfsctl.o nfsfh.o vfs.o \
|
||||
export.o auth.o lockd.o nfscache.o \
|
||||
stats.o filecache.o nfs3proc.o nfs3xdr.o
|
||||
nfsd-$(CONFIG_NFSD_V2) += nfsproc.o nfsxdr.o
|
||||
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
|
||||
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
|
||||
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
|
||||
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
|
||||
nfs4acl.o nfs4callback.o nfs4recover.o
|
||||
|
@ -38,6 +38,8 @@
|
||||
struct nfs4_acl;
|
||||
struct svc_fh;
|
||||
struct svc_rqst;
|
||||
struct nfsd_attrs;
|
||||
enum nfs_ftype4;
|
||||
|
||||
int nfs4_acl_bytes(int entries);
|
||||
int nfs4_acl_get_whotype(char *, u32);
|
||||
@ -45,7 +47,7 @@ __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
|
||||
|
||||
int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
struct nfs4_acl **acl);
|
||||
__be32 nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct nfs4_acl *acl);
|
||||
__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
|
||||
struct nfsd_attrs *attr);
|
||||
|
||||
#endif /* LINUX_NFS4_ACL_H */
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "blocklayoutxdr.h"
|
||||
#include "pnfs.h"
|
||||
#include "filecache.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PNFS
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "nfsd.h"
|
||||
#include "blocklayoutxdr.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PNFS
|
||||
|
||||
|
@ -84,6 +84,6 @@ int nfsd_reply_cache_init(struct nfsd_net *);
|
||||
void nfsd_reply_cache_shutdown(struct nfsd_net *);
|
||||
int nfsd_cache_lookup(struct svc_rqst *);
|
||||
void nfsd_cache_update(struct svc_rqst *, int, __be32 *);
|
||||
int nfsd_reply_cache_stats_open(struct inode *, struct file *);
|
||||
int nfsd_reply_cache_stats_show(struct seq_file *m, void *v);
|
||||
|
||||
#endif /* NFSCACHE_H */
|
||||
|
@ -331,12 +331,29 @@ static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc)
|
||||
fsloc->locations = NULL;
|
||||
}
|
||||
|
||||
static int export_stats_init(struct export_stats *stats)
|
||||
{
|
||||
stats->start_time = ktime_get_seconds();
|
||||
return nfsd_percpu_counters_init(stats->counter, EXP_STATS_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
static void export_stats_reset(struct export_stats *stats)
|
||||
{
|
||||
nfsd_percpu_counters_reset(stats->counter, EXP_STATS_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
static void export_stats_destroy(struct export_stats *stats)
|
||||
{
|
||||
nfsd_percpu_counters_destroy(stats->counter, EXP_STATS_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
static void svc_export_put(struct kref *ref)
|
||||
{
|
||||
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
|
||||
path_put(&exp->ex_path);
|
||||
auth_domain_put(exp->ex_client);
|
||||
nfsd4_fslocs_free(&exp->ex_fslocs);
|
||||
export_stats_destroy(&exp->ex_stats);
|
||||
kfree(exp->ex_uuid);
|
||||
kfree_rcu(exp, ex_rcu);
|
||||
}
|
||||
@ -408,6 +425,12 @@ static int check_export(struct inode *inode, int *flags, unsigned char *uuid)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (inode->i_sb->s_export_op->flags & EXPORT_OP_NOSUBTREECHK &&
|
||||
!(*flags & NFSEXP_NOSUBTREECHECK)) {
|
||||
dprintk("%s: %s does not support subtree checking!\n",
|
||||
__func__, inode->i_sb->s_type->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -686,13 +709,27 @@ static void exp_flags(struct seq_file *m, int flag, int fsid,
|
||||
kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fslocs);
|
||||
static void show_secinfo(struct seq_file *m, struct svc_export *exp);
|
||||
|
||||
static int is_export_stats_file(struct seq_file *m)
|
||||
{
|
||||
/*
|
||||
* The export_stats file uses the same ops as the exports file.
|
||||
* We use the file's name to determine the reported info per export.
|
||||
* There is no rename in nsfdfs, so d_name.name is stable.
|
||||
*/
|
||||
return !strcmp(m->file->f_path.dentry->d_name.name, "export_stats");
|
||||
}
|
||||
|
||||
static int svc_export_show(struct seq_file *m,
|
||||
struct cache_detail *cd,
|
||||
struct cache_head *h)
|
||||
{
|
||||
struct svc_export *exp;
|
||||
bool export_stats = is_export_stats_file(m);
|
||||
|
||||
if (h == NULL) {
|
||||
if (export_stats)
|
||||
seq_puts(m, "#path domain start-time\n#\tstats\n");
|
||||
else
|
||||
seq_puts(m, "#path domain(flags)\n");
|
||||
return 0;
|
||||
}
|
||||
@ -700,6 +737,17 @@ static int svc_export_show(struct seq_file *m,
|
||||
seq_path(m, &exp->ex_path, " \t\n\\");
|
||||
seq_putc(m, '\t');
|
||||
seq_escape(m, exp->ex_client->name, " \t\n\\");
|
||||
if (export_stats) {
|
||||
seq_printf(m, "\t%lld\n", exp->ex_stats.start_time);
|
||||
seq_printf(m, "\tfh_stale: %lld\n",
|
||||
percpu_counter_sum_positive(&exp->ex_stats.counter[EXP_STATS_FH_STALE]));
|
||||
seq_printf(m, "\tio_read: %lld\n",
|
||||
percpu_counter_sum_positive(&exp->ex_stats.counter[EXP_STATS_IO_READ]));
|
||||
seq_printf(m, "\tio_write: %lld\n",
|
||||
percpu_counter_sum_positive(&exp->ex_stats.counter[EXP_STATS_IO_WRITE]));
|
||||
seq_putc(m, '\n');
|
||||
return 0;
|
||||
}
|
||||
seq_putc(m, '(');
|
||||
if (test_bit(CACHE_VALID, &h->flags) &&
|
||||
!test_bit(CACHE_NEGATIVE, &h->flags)) {
|
||||
@ -742,6 +790,7 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem)
|
||||
new->ex_layout_types = 0;
|
||||
new->ex_uuid = NULL;
|
||||
new->cd = item->cd;
|
||||
export_stats_reset(&new->ex_stats);
|
||||
}
|
||||
|
||||
static void export_update(struct cache_head *cnew, struct cache_head *citem)
|
||||
@ -774,10 +823,15 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem)
|
||||
static struct cache_head *svc_export_alloc(void)
|
||||
{
|
||||
struct svc_export *i = kmalloc(sizeof(*i), GFP_KERNEL);
|
||||
if (i)
|
||||
return &i->h;
|
||||
else
|
||||
if (!i)
|
||||
return NULL;
|
||||
|
||||
if (export_stats_init(&i->ex_stats)) {
|
||||
kfree(i);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &i->h;
|
||||
}
|
||||
|
||||
static const struct cache_detail svc_export_cache_template = {
|
||||
@ -1239,9 +1293,13 @@ static int e_show(struct seq_file *m, void *p)
|
||||
struct cache_head *cp = p;
|
||||
struct svc_export *exp = container_of(cp, struct svc_export, h);
|
||||
struct cache_detail *cd = m->private;
|
||||
bool export_stats = is_export_stats_file(m);
|
||||
|
||||
if (p == SEQ_START_TOKEN) {
|
||||
seq_puts(m, "# Version 1.1\n");
|
||||
if (export_stats)
|
||||
seq_puts(m, "# Path Client Start-time\n#\tStats\n");
|
||||
else
|
||||
seq_puts(m, "# Path Client(Flags) # IPs\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define NFSD_EXPORT_H
|
||||
|
||||
#include <linux/sunrpc/cache.h>
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <uapi/linux/nfsd/export.h>
|
||||
#include <linux/nfs4.h>
|
||||
|
||||
@ -46,6 +47,19 @@ struct exp_flavor_info {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/* Per-export stats */
|
||||
enum {
|
||||
EXP_STATS_FH_STALE,
|
||||
EXP_STATS_IO_READ,
|
||||
EXP_STATS_IO_WRITE,
|
||||
EXP_STATS_COUNTERS_NUM
|
||||
};
|
||||
|
||||
struct export_stats {
|
||||
time64_t start_time;
|
||||
struct percpu_counter counter[EXP_STATS_COUNTERS_NUM];
|
||||
};
|
||||
|
||||
struct svc_export {
|
||||
struct cache_head h;
|
||||
struct auth_domain * ex_client;
|
||||
@ -62,6 +76,7 @@ struct svc_export {
|
||||
struct nfsd4_deviceid_map *ex_devid_map;
|
||||
struct cache_detail *cd;
|
||||
struct rcu_head ex_rcu;
|
||||
struct export_stats ex_stats;
|
||||
};
|
||||
|
||||
/* an "export key" (expkey) maps a filehandlefragement to an
|
||||
@ -100,7 +115,6 @@ struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *);
|
||||
int exp_rootfh(struct net *, struct auth_domain *,
|
||||
char *path, struct knfsd_fh *, int maxsize);
|
||||
__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *);
|
||||
__be32 nfserrno(int errno);
|
||||
|
||||
static inline void exp_put(struct svc_export *exp)
|
||||
{
|
||||
|
1199
fs/nfsd/filecache.c
1199
fs/nfsd/filecache.c
File diff suppressed because it is too large
Load Diff
@ -29,23 +29,23 @@ struct nfsd_file_mark {
|
||||
* never be dereferenced, only used for comparison.
|
||||
*/
|
||||
struct nfsd_file {
|
||||
struct hlist_node nf_node;
|
||||
struct list_head nf_lru;
|
||||
struct rcu_head nf_rcu;
|
||||
struct rhlist_head nf_rlist;
|
||||
void *nf_inode;
|
||||
struct file *nf_file;
|
||||
const struct cred *nf_cred;
|
||||
struct net *nf_net;
|
||||
#define NFSD_FILE_HASHED (0)
|
||||
#define NFSD_FILE_PENDING (1)
|
||||
#define NFSD_FILE_BREAK_READ (2)
|
||||
#define NFSD_FILE_BREAK_WRITE (3)
|
||||
#define NFSD_FILE_REFERENCED (4)
|
||||
#define NFSD_FILE_REFERENCED (2)
|
||||
#define NFSD_FILE_GC (3)
|
||||
unsigned long nf_flags;
|
||||
struct inode *nf_inode;
|
||||
unsigned int nf_hashval;
|
||||
refcount_t nf_ref;
|
||||
unsigned char nf_may;
|
||||
|
||||
struct nfsd_file_mark *nf_mark;
|
||||
struct list_head nf_lru;
|
||||
struct rcu_head nf_rcu;
|
||||
ktime_t nf_birthtime;
|
||||
};
|
||||
|
||||
int nfsd_file_cache_init(void);
|
||||
@ -57,7 +57,12 @@ void nfsd_file_put(struct nfsd_file *nf);
|
||||
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
|
||||
void nfsd_file_close_inode_sync(struct inode *inode);
|
||||
bool nfsd_file_is_cached(struct inode *inode);
|
||||
__be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct nfsd_file **nfp);
|
||||
__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct nfsd_file **nfp);
|
||||
int nfsd_file_cache_stats_open(struct inode *, struct file *);
|
||||
__be32 nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
unsigned int may_flags, struct file *file,
|
||||
struct nfsd_file **nfp);
|
||||
int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
|
||||
#endif /* _FS_NFSD_FILECACHE_H */
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "flexfilelayoutxdr.h"
|
||||
#include "pnfs.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PNFS
|
||||
|
||||
@ -61,7 +62,7 @@ nfsd4_ff_proc_layoutget(struct inode *inode, const struct svc_fh *fhp,
|
||||
goto out_error;
|
||||
|
||||
fl->fh.size = fhp->fh_handle.fh_size;
|
||||
memcpy(fl->fh.data, &fhp->fh_handle.fh_base, fl->fh.size);
|
||||
memcpy(fl->fh.data, &fhp->fh_handle.fh_raw, fl->fh.size);
|
||||
|
||||
/* Give whole file layout segments */
|
||||
seg->offset = 0;
|
||||
|
@ -25,18 +25,22 @@
|
||||
* Note: we hold the dentry use count while the file is open.
|
||||
*/
|
||||
static __be32
|
||||
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
|
||||
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp,
|
||||
int mode)
|
||||
{
|
||||
__be32 nfserr;
|
||||
int access;
|
||||
struct svc_fh fh;
|
||||
|
||||
/* must initialize before using! but maxsize doesn't matter */
|
||||
fh_init(&fh,0);
|
||||
fh.fh_handle.fh_size = f->size;
|
||||
memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
|
||||
memcpy(&fh.fh_handle.fh_raw, f->data, f->size);
|
||||
fh.fh_export = NULL;
|
||||
|
||||
nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp);
|
||||
access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
|
||||
access |= NFSD_MAY_LOCK;
|
||||
nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
|
||||
fh_put(&fh);
|
||||
/* We return nlm error codes as nlm doesn't know
|
||||
* about nfsd, but nfsd does know about nlm..
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/netns/generic.h>
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <linux/siphash.h>
|
||||
|
||||
/* Hash tables for nfs4_clientid state */
|
||||
#define CLIENT_HASH_BITS 4
|
||||
@ -21,6 +23,14 @@
|
||||
struct cld_net;
|
||||
struct nfsd4_client_tracking_ops;
|
||||
|
||||
enum {
|
||||
/* cache misses due only to checksum comparison failures */
|
||||
NFSD_NET_PAYLOAD_MISSES,
|
||||
/* amount of memory (in bytes) currently consumed by the DRC */
|
||||
NFSD_NET_DRC_MEM_USAGE,
|
||||
NFSD_NET_COUNTERS_NUM
|
||||
};
|
||||
|
||||
/*
|
||||
* Represents a nfsd "container". With respect to nfsv4 state tracking, the
|
||||
* fields of interest are the *_id_hashtbls and the *_name_tree. These track
|
||||
@ -99,9 +109,8 @@ struct nfsd_net {
|
||||
bool nfsd_net_up;
|
||||
bool lockd_up;
|
||||
|
||||
/* Time of server startup */
|
||||
struct timespec64 nfssvc_boot;
|
||||
seqlock_t boot_lock;
|
||||
seqlock_t writeverf_lock;
|
||||
unsigned char writeverf[8];
|
||||
|
||||
/*
|
||||
* Max number of connections this nfsd container will allow. Defaults
|
||||
@ -114,12 +123,13 @@ struct nfsd_net {
|
||||
u32 clverifier_counter;
|
||||
|
||||
struct svc_serv *nfsd_serv;
|
||||
|
||||
wait_queue_head_t ntf_wq;
|
||||
atomic_t ntf_refcnt;
|
||||
|
||||
/* Allow umount to wait for nfsd state cleanup */
|
||||
struct completion nfsd_shutdown_complete;
|
||||
/* When a listening socket is added to nfsd, keep_active is set
|
||||
* and this justifies a reference on nfsd_serv. This stops
|
||||
* nfsd_serv from being freed. When the number of threads is
|
||||
* set, keep_active is cleared and the reference is dropped. So
|
||||
* when the last thread exits, the service will be destroyed.
|
||||
*/
|
||||
int keep_active;
|
||||
|
||||
/*
|
||||
* clientid and stateid data for construction of net unique COPY
|
||||
@ -149,20 +159,16 @@ struct nfsd_net {
|
||||
|
||||
/*
|
||||
* Stats and other tracking of on the duplicate reply cache.
|
||||
* These fields and the "rc" fields in nfsdstats are modified
|
||||
* with only the per-bucket cache lock, which isn't really safe
|
||||
* and should be fixed if we want the statistics to be
|
||||
* completely accurate.
|
||||
* The longest_chain* fields are modified with only the per-bucket
|
||||
* cache lock, which isn't really safe and should be fixed if we want
|
||||
* these statistics to be completely accurate.
|
||||
*/
|
||||
|
||||
/* total number of entries */
|
||||
atomic_t num_drc_entries;
|
||||
|
||||
/* cache misses due only to checksum comparison failures */
|
||||
unsigned int payload_misses;
|
||||
|
||||
/* amount of memory (in bytes) currently consumed by the DRC */
|
||||
unsigned int drc_mem_usage;
|
||||
/* Per-netns stats counters */
|
||||
struct percpu_counter counter[NFSD_NET_COUNTERS_NUM];
|
||||
|
||||
/* longest hash chain seen */
|
||||
unsigned int longest_chain;
|
||||
@ -171,8 +177,25 @@ struct nfsd_net {
|
||||
unsigned int longest_chain_cachesize;
|
||||
|
||||
struct shrinker nfsd_reply_cache_shrinker;
|
||||
|
||||
/* tracking server-to-server copy mounts */
|
||||
spinlock_t nfsd_ssc_lock;
|
||||
struct list_head nfsd_ssc_mount_list;
|
||||
wait_queue_head_t nfsd_ssc_waitq;
|
||||
|
||||
/* utsname taken from the process that starts the server */
|
||||
char nfsd_name[UNX_MAXNODENAME+1];
|
||||
|
||||
struct nfsd_fcache_disposal *fcache_disposal;
|
||||
|
||||
siphash_key_t siphash_key;
|
||||
|
||||
atomic_t nfs4_client_count;
|
||||
int nfs4_max_clients;
|
||||
|
||||
atomic_t nfsd_courtesy_clients;
|
||||
struct shrinker nfsd_client_shrinker;
|
||||
struct work_struct nfsd_shrinker_work;
|
||||
};
|
||||
|
||||
/* Simple check to find out if a given net was properly initialized */
|
||||
@ -182,6 +205,6 @@ extern void nfsd_netns_free_versions(struct nfsd_net *nn);
|
||||
|
||||
extern unsigned int nfsd_net_id;
|
||||
|
||||
void nfsd_copy_boot_verifier(__be32 verf[2], struct nfsd_net *nn);
|
||||
void nfsd_reset_boot_verifier(struct nfsd_net *nn);
|
||||
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn);
|
||||
void nfsd_reset_write_verifier(struct nfsd_net *nn);
|
||||
#endif /* __NFSD_NETNS_H__ */
|
||||
|
@ -111,7 +111,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
|
||||
if (error)
|
||||
goto out_errno;
|
||||
|
||||
fh_lock(fh);
|
||||
inode_lock(inode);
|
||||
|
||||
error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
|
||||
if (error)
|
||||
@ -120,7 +120,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
|
||||
if (error)
|
||||
goto out_drop_lock;
|
||||
|
||||
fh_unlock(fh);
|
||||
inode_unlock(inode);
|
||||
|
||||
fh_drop_write(fh);
|
||||
|
||||
@ -134,7 +134,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
|
||||
return rpc_success;
|
||||
|
||||
out_drop_lock:
|
||||
fh_unlock(fh);
|
||||
inode_unlock(inode);
|
||||
fh_drop_write(fh);
|
||||
out_errno:
|
||||
resp->status = nfserrno(error);
|
||||
@ -185,161 +185,106 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp)
|
||||
/*
|
||||
* 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)
|
||||
static bool
|
||||
nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_getaclargs *argp = rqstp->rq_argp;
|
||||
|
||||
p = nfs2svc_decode_fh(p, &argp->fh);
|
||||
if (!p)
|
||||
return 0;
|
||||
argp->mask = ntohl(*p); p++;
|
||||
if (!svcxdr_decode_fhandle(xdr, &argp->fh))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->mask) < 0)
|
||||
return false;
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
static bool
|
||||
nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_setaclargs *argp = rqstp->rq_argp;
|
||||
struct kvec *head = rqstp->rq_arg.head;
|
||||
unsigned int base;
|
||||
int n;
|
||||
|
||||
p = nfs2svc_decode_fh(p, &argp->fh);
|
||||
if (!p)
|
||||
return 0;
|
||||
argp->mask = ntohl(*p++);
|
||||
if (argp->mask & ~NFS_ACL_MASK ||
|
||||
!xdr_argsize_check(rqstp, p))
|
||||
return 0;
|
||||
if (!svcxdr_decode_fhandle(xdr, &argp->fh))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->mask) < 0)
|
||||
return false;
|
||||
if (argp->mask & ~NFS_ACL_MASK)
|
||||
return false;
|
||||
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_ACL) ?
|
||||
&argp->acl_access : NULL))
|
||||
return false;
|
||||
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_DFACL) ?
|
||||
&argp->acl_default : NULL))
|
||||
return false;
|
||||
|
||||
base = (char *)p - (char *)head->iov_base;
|
||||
n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
|
||||
(argp->mask & NFS_ACL) ?
|
||||
&argp->acl_access : NULL);
|
||||
if (n > 0)
|
||||
n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
|
||||
(argp->mask & NFS_DFACL) ?
|
||||
&argp->acl_default : NULL);
|
||||
return (n > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
static bool
|
||||
nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd_fhandle *argp = rqstp->rq_argp;
|
||||
struct nfsd3_accessargs *args = rqstp->rq_argp;
|
||||
|
||||
p = nfs2svc_decode_fh(p, &argp->fh);
|
||||
if (!p)
|
||||
return 0;
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
}
|
||||
if (!svcxdr_decode_fhandle(xdr, &args->fh))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &args->access) < 0)
|
||||
return false;
|
||||
|
||||
static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nfsd3_accessargs *argp = rqstp->rq_argp;
|
||||
|
||||
p = nfs2svc_decode_fh(p, &argp->fh);
|
||||
if (!p)
|
||||
return 0;
|
||||
argp->access = ntohl(*p++);
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR encode functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* There must be an encoding function for void results so svc_process
|
||||
* will work properly.
|
||||
*/
|
||||
static int nfsaclsvc_encode_voidres(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
}
|
||||
|
||||
/* GETACL */
|
||||
static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
|
||||
static bool
|
||||
nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_getaclres *resp = rqstp->rq_resp;
|
||||
struct dentry *dentry = resp->fh.fh_dentry;
|
||||
struct inode *inode;
|
||||
struct kvec *head = rqstp->rq_res.head;
|
||||
unsigned int base;
|
||||
int n;
|
||||
int w;
|
||||
|
||||
*p++ = resp->status;
|
||||
if (resp->status != nfs_ok)
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
if (!svcxdr_encode_stat(xdr, resp->status))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Since this is version 2, the check for nfserr in
|
||||
* nfsd_dispatch actually ensures the following cannot happen.
|
||||
* However, it seems fragile to depend on that.
|
||||
*/
|
||||
if (dentry == NULL || d_really_is_negative(dentry))
|
||||
return 0;
|
||||
return true;
|
||||
inode = d_inode(dentry);
|
||||
|
||||
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
|
||||
*p++ = htonl(resp->mask);
|
||||
if (!xdr_ressize_check(rqstp, p))
|
||||
return 0;
|
||||
base = (char *)p - (char *)head->iov_base;
|
||||
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
|
||||
return false;
|
||||
if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
|
||||
return false;
|
||||
|
||||
rqstp->rq_res.page_len = w = nfsacl_size(
|
||||
(resp->mask & NFS_ACL) ? resp->acl_access : NULL,
|
||||
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
|
||||
while (w > 0) {
|
||||
if (!*(rqstp->rq_next_page++))
|
||||
return 0;
|
||||
w -= PAGE_SIZE;
|
||||
}
|
||||
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
|
||||
resp->mask & NFS_ACL, 0))
|
||||
return false;
|
||||
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
|
||||
resp->mask & NFS_DFACL, NFS_ACL_DEFAULT))
|
||||
return false;
|
||||
|
||||
n = nfsacl_encode(&rqstp->rq_res, base, inode,
|
||||
resp->acl_access,
|
||||
resp->mask & NFS_ACL, 0);
|
||||
if (n > 0)
|
||||
n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
|
||||
resp->acl_default,
|
||||
resp->mask & NFS_DFACL,
|
||||
NFS_ACL_DEFAULT);
|
||||
return (n > 0);
|
||||
}
|
||||
|
||||
static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p)
|
||||
{
|
||||
struct nfsd_attrstat *resp = rqstp->rq_resp;
|
||||
|
||||
*p++ = resp->status;
|
||||
if (resp->status != nfs_ok)
|
||||
goto out;
|
||||
|
||||
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
|
||||
out:
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ACCESS */
|
||||
static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, __be32 *p)
|
||||
static bool
|
||||
nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_accessres *resp = rqstp->rq_resp;
|
||||
|
||||
*p++ = resp->status;
|
||||
if (resp->status != nfs_ok)
|
||||
goto out;
|
||||
if (!svcxdr_encode_stat(xdr, resp->status))
|
||||
return false;
|
||||
switch (resp->status) {
|
||||
case nfs_ok:
|
||||
if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
|
||||
return false;
|
||||
if (xdr_stream_encode_u32(xdr, resp->access) < 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
|
||||
*p++ = htonl(resp->access);
|
||||
out:
|
||||
return xdr_ressize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -354,13 +299,6 @@ static void nfsaclsvc_release_getacl(struct svc_rqst *rqstp)
|
||||
posix_acl_release(resp->acl_default);
|
||||
}
|
||||
|
||||
static void nfsaclsvc_release_attrstat(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd_attrstat *resp = rqstp->rq_resp;
|
||||
|
||||
fh_put(&resp->fh);
|
||||
}
|
||||
|
||||
static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_accessres *resp = rqstp->rq_resp;
|
||||
@ -378,12 +316,14 @@ struct nfsd3_voidargs { int dummy; };
|
||||
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_decode = nfssvc_decode_voidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd_voidargs),
|
||||
.pc_argzero = sizeof(struct nfsd_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[ACLPROC2_GETACL] = {
|
||||
.pc_func = nfsacld_proc_getacl,
|
||||
@ -391,29 +331,35 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
|
||||
.pc_encode = nfsaclsvc_encode_getaclres,
|
||||
.pc_release = nfsaclsvc_release_getacl,
|
||||
.pc_argsize = sizeof(struct nfsd3_getaclargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_getaclargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_getaclres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+1+2*(1+ACL),
|
||||
.pc_name = "GETACL",
|
||||
},
|
||||
[ACLPROC2_SETACL] = {
|
||||
.pc_func = nfsacld_proc_setacl,
|
||||
.pc_decode = nfsaclsvc_decode_setaclargs,
|
||||
.pc_encode = nfsaclsvc_encode_attrstatres,
|
||||
.pc_release = nfsaclsvc_release_attrstat,
|
||||
.pc_encode = nfssvc_encode_attrstatres,
|
||||
.pc_release = nfssvc_release_attrstat,
|
||||
.pc_argsize = sizeof(struct nfsd3_setaclargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_setaclargs),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT,
|
||||
.pc_name = "SETACL",
|
||||
},
|
||||
[ACLPROC2_GETATTR] = {
|
||||
.pc_func = nfsacld_proc_getattr,
|
||||
.pc_decode = nfsaclsvc_decode_fhandleargs,
|
||||
.pc_encode = nfsaclsvc_encode_attrstatres,
|
||||
.pc_release = nfsaclsvc_release_attrstat,
|
||||
.pc_decode = nfssvc_decode_fhandleargs,
|
||||
.pc_encode = nfssvc_encode_attrstatres,
|
||||
.pc_release = nfssvc_release_attrstat,
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_argzero = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT,
|
||||
.pc_name = "GETATTR",
|
||||
},
|
||||
[ACLPROC2_ACCESS] = {
|
||||
.pc_func = nfsacld_proc_access,
|
||||
@ -421,9 +367,11 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
|
||||
.pc_encode = nfsaclsvc_encode_accessres,
|
||||
.pc_release = nfsaclsvc_release_access,
|
||||
.pc_argsize = sizeof(struct nfsd3_accessargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_accessargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_accessres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT+1,
|
||||
.pc_name = "SETATTR",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -101,7 +101,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
|
||||
if (error)
|
||||
goto out_errno;
|
||||
|
||||
fh_lock(fh);
|
||||
inode_lock(inode);
|
||||
|
||||
error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
|
||||
if (error)
|
||||
@ -109,7 +109,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
|
||||
error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default);
|
||||
|
||||
out_drop_lock:
|
||||
fh_unlock(fh);
|
||||
inode_unlock(inode);
|
||||
fh_drop_write(fh);
|
||||
out_errno:
|
||||
resp->status = nfserrno(error);
|
||||
@ -124,43 +124,39 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
|
||||
/*
|
||||
* XDR decode functions
|
||||
*/
|
||||
static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
|
||||
static bool
|
||||
nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_getaclargs *args = rqstp->rq_argp;
|
||||
|
||||
p = nfs3svc_decode_fh(p, &args->fh);
|
||||
if (!p)
|
||||
return 0;
|
||||
args->mask = ntohl(*p); p++;
|
||||
if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &args->mask) < 0)
|
||||
return false;
|
||||
|
||||
return xdr_argsize_check(rqstp, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
static bool
|
||||
nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_setaclargs *args = rqstp->rq_argp;
|
||||
struct kvec *head = rqstp->rq_arg.head;
|
||||
unsigned int base;
|
||||
int n;
|
||||
struct nfsd3_setaclargs *argp = rqstp->rq_argp;
|
||||
|
||||
p = nfs3svc_decode_fh(p, &args->fh);
|
||||
if (!p)
|
||||
return 0;
|
||||
args->mask = ntohl(*p++);
|
||||
if (args->mask & ~NFS_ACL_MASK ||
|
||||
!xdr_argsize_check(rqstp, p))
|
||||
return 0;
|
||||
if (!svcxdr_decode_nfs_fh3(xdr, &argp->fh))
|
||||
return false;
|
||||
if (xdr_stream_decode_u32(xdr, &argp->mask) < 0)
|
||||
return false;
|
||||
if (argp->mask & ~NFS_ACL_MASK)
|
||||
return false;
|
||||
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_ACL) ?
|
||||
&argp->acl_access : NULL))
|
||||
return false;
|
||||
if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_DFACL) ?
|
||||
&argp->acl_default : NULL))
|
||||
return false;
|
||||
|
||||
base = (char *)p - (char *)head->iov_base;
|
||||
n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
|
||||
(args->mask & NFS_ACL) ?
|
||||
&args->acl_access : NULL);
|
||||
if (n > 0)
|
||||
n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
|
||||
(args->mask & NFS_DFACL) ?
|
||||
&args->acl_default : NULL);
|
||||
return (n > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -168,59 +164,47 @@ static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
|
||||
*/
|
||||
|
||||
/* GETACL */
|
||||
static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
|
||||
static bool
|
||||
nfs3svc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_getaclres *resp = rqstp->rq_resp;
|
||||
struct dentry *dentry = resp->fh.fh_dentry;
|
||||
struct inode *inode;
|
||||
|
||||
*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);
|
||||
struct kvec *head = rqstp->rq_res.head;
|
||||
unsigned int base;
|
||||
int n;
|
||||
int w;
|
||||
if (!svcxdr_encode_nfsstat3(xdr, resp->status))
|
||||
return false;
|
||||
switch (resp->status) {
|
||||
case nfs_ok:
|
||||
inode = d_inode(dentry);
|
||||
if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
|
||||
return false;
|
||||
if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
|
||||
return false;
|
||||
|
||||
*p++ = htonl(resp->mask);
|
||||
if (!xdr_ressize_check(rqstp, p))
|
||||
return 0;
|
||||
base = (char *)p - (char *)head->iov_base;
|
||||
|
||||
rqstp->rq_res.page_len = w = nfsacl_size(
|
||||
(resp->mask & NFS_ACL) ? resp->acl_access : NULL,
|
||||
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
|
||||
while (w > 0) {
|
||||
if (!*(rqstp->rq_next_page++))
|
||||
return 0;
|
||||
w -= PAGE_SIZE;
|
||||
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
|
||||
resp->mask & NFS_ACL, 0))
|
||||
return false;
|
||||
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
|
||||
resp->mask & NFS_DFACL,
|
||||
NFS_ACL_DEFAULT))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
|
||||
return false;
|
||||
}
|
||||
|
||||
n = nfsacl_encode(&rqstp->rq_res, base, inode,
|
||||
resp->acl_access,
|
||||
resp->mask & NFS_ACL, 0);
|
||||
if (n > 0)
|
||||
n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
|
||||
resp->acl_default,
|
||||
resp->mask & NFS_DFACL,
|
||||
NFS_ACL_DEFAULT);
|
||||
if (n <= 0)
|
||||
return 0;
|
||||
} else
|
||||
if (!xdr_ressize_check(rqstp, p))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* SETACL */
|
||||
static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, __be32 *p)
|
||||
static bool
|
||||
nfs3svc_encode_setaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
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);
|
||||
return svcxdr_encode_nfsstat3(xdr, resp->status) &&
|
||||
svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -245,12 +229,14 @@ struct nfsd3_voidargs { int dummy; };
|
||||
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_decode = nfssvc_decode_voidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd_voidargs),
|
||||
.pc_argzero = sizeof(struct nfsd_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[ACLPROC3_GETACL] = {
|
||||
.pc_func = nfsd3_proc_getacl,
|
||||
@ -258,9 +244,11 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
|
||||
.pc_encode = nfs3svc_encode_getaclres,
|
||||
.pc_release = nfs3svc_release_getacl,
|
||||
.pc_argsize = sizeof(struct nfsd3_getaclargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_getaclargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_getaclres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+1+2*(1+ACL),
|
||||
.pc_name = "GETACL",
|
||||
},
|
||||
[ACLPROC3_SETACL] = {
|
||||
.pc_func = nfsd3_proc_setacl,
|
||||
@ -268,9 +256,11 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
|
||||
.pc_encode = nfs3svc_encode_setaclres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_setaclargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_setaclargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_attrstat),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT,
|
||||
.pc_name = "SETACL",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -8,10 +8,12 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ext2_fs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "cache.h"
|
||||
#include "xdr3.h"
|
||||
#include "vfs.h"
|
||||
#include "filecache.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PROC
|
||||
|
||||
@ -66,12 +68,15 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_sattrargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_attrstat *resp = rqstp->rq_resp;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = &argp->attrs,
|
||||
};
|
||||
|
||||
dprintk("nfsd: SETATTR(3) %s\n",
|
||||
SVCFH_fmt(&argp->fh));
|
||||
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,
|
||||
resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs,
|
||||
argp->check_guard, argp->guardtime);
|
||||
return rpc_success;
|
||||
}
|
||||
@ -124,7 +129,7 @@ nfsd3_proc_access(struct svc_rqst *rqstp)
|
||||
static __be32
|
||||
nfsd3_proc_readlink(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_readlinkargs *argp = rqstp->rq_argp;
|
||||
struct nfsd_fhandle *argp = rqstp->rq_argp;
|
||||
struct nfsd3_readlinkres *resp = rqstp->rq_resp;
|
||||
|
||||
dprintk("nfsd: READLINK(3) %s\n", SVCFH_fmt(&argp->fh));
|
||||
@ -132,7 +137,9 @@ nfsd3_proc_readlink(struct svc_rqst *rqstp)
|
||||
/* Read the symlink. */
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->len = NFS3_MAXPATHLEN;
|
||||
resp->status = nfsd_readlink(rqstp, &resp->fh, argp->buffer, &resp->len);
|
||||
resp->pages = rqstp->rq_next_page++;
|
||||
resp->status = nfsd_readlink(rqstp, &resp->fh,
|
||||
page_address(*resp->pages), &resp->len);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -144,25 +151,43 @@ nfsd3_proc_read(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_readargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_readres *resp = rqstp->rq_resp;
|
||||
u32 max_blocksize = svc_max_payload(rqstp);
|
||||
unsigned long cnt = min(argp->count, max_blocksize);
|
||||
unsigned int len;
|
||||
int v;
|
||||
|
||||
dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
(unsigned long) argp->count,
|
||||
(unsigned long long) argp->offset);
|
||||
|
||||
argp->count = min_t(u32, argp->count, svc_max_payload(rqstp));
|
||||
argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen);
|
||||
if (argp->offset > (u64)OFFSET_MAX)
|
||||
argp->offset = (u64)OFFSET_MAX;
|
||||
if (argp->offset + argp->count > (u64)OFFSET_MAX)
|
||||
argp->count = (u64)OFFSET_MAX - argp->offset;
|
||||
|
||||
v = 0;
|
||||
len = argp->count;
|
||||
resp->pages = rqstp->rq_next_page;
|
||||
while (len > 0) {
|
||||
struct page *page = *(rqstp->rq_next_page++);
|
||||
|
||||
rqstp->rq_vec[v].iov_base = page_address(page);
|
||||
rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
|
||||
len -= rqstp->rq_vec[v].iov_len;
|
||||
v++;
|
||||
}
|
||||
|
||||
/* Obtain buffer pointer for payload.
|
||||
* 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
|
||||
* + 1 (xdr opaque byte count) = 26
|
||||
*/
|
||||
resp->count = cnt;
|
||||
resp->count = argp->count;
|
||||
svc_reserve_auth(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4);
|
||||
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_read(rqstp, &resp->fh, argp->offset,
|
||||
rqstp->rq_vec, argp->vlen, &resp->count,
|
||||
&resp->eof);
|
||||
rqstp->rq_vec, v, &resp->count, &resp->eof);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -190,32 +215,147 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
|
||||
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->committed = argp->stable;
|
||||
nvecs = svc_fill_write_vector(rqstp, rqstp->rq_arg.pages,
|
||||
&argp->first, cnt);
|
||||
if (!nvecs) {
|
||||
resp->status = nfserr_io;
|
||||
goto out;
|
||||
}
|
||||
nvecs = svc_fill_write_vector(rqstp, &argp->payload);
|
||||
|
||||
resp->status = nfsd_write(rqstp, &resp->fh, argp->offset,
|
||||
rqstp->rq_vec, nvecs, &cnt,
|
||||
resp->committed, resp->verf);
|
||||
resp->count = cnt;
|
||||
out:
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
/*
|
||||
* With NFSv3, CREATE processing is a lot easier than with NFSv2.
|
||||
* At least in theory; we'll see how it fares in practice when the
|
||||
* first reports about SunOS compatibility problems start to pour in...
|
||||
* Implement NFSv3's unchecked, guarded, and exclusive CREATE
|
||||
* semantics for regular files. Except for the created file,
|
||||
* this operation is stateless on the server.
|
||||
*
|
||||
* Upon return, caller must release @fhp and @resfhp.
|
||||
*/
|
||||
static __be32
|
||||
nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct svc_fh *resfhp, struct nfsd3_createargs *argp)
|
||||
{
|
||||
struct iattr *iap = &argp->attrs;
|
||||
struct dentry *parent, *child;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = iap,
|
||||
};
|
||||
__u32 v_mtime, v_atime;
|
||||
struct inode *inode;
|
||||
__be32 status;
|
||||
int host_err;
|
||||
|
||||
if (isdotent(argp->name, argp->len))
|
||||
return nfserr_exist;
|
||||
if (!(iap->ia_valid & ATTR_MODE))
|
||||
iap->ia_mode = 0;
|
||||
|
||||
status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
|
||||
if (status != nfs_ok)
|
||||
return status;
|
||||
|
||||
parent = fhp->fh_dentry;
|
||||
inode = d_inode(parent);
|
||||
|
||||
host_err = fh_want_write(fhp);
|
||||
if (host_err)
|
||||
return nfserrno(host_err);
|
||||
|
||||
inode_lock_nested(inode, I_MUTEX_PARENT);
|
||||
|
||||
child = lookup_one_len(argp->name, parent, argp->len);
|
||||
if (IS_ERR(child)) {
|
||||
status = nfserrno(PTR_ERR(child));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (d_really_is_negative(child)) {
|
||||
status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
|
||||
if (status != nfs_ok)
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = fh_compose(resfhp, fhp->fh_export, child, fhp);
|
||||
if (status != nfs_ok)
|
||||
goto out;
|
||||
|
||||
v_mtime = 0;
|
||||
v_atime = 0;
|
||||
if (argp->createmode == NFS3_CREATE_EXCLUSIVE) {
|
||||
u32 *verifier = (u32 *)argp->verf;
|
||||
|
||||
/*
|
||||
* Solaris 7 gets confused (bugid 4218508) if these have
|
||||
* the high bit set, as do xfs filesystems without the
|
||||
* "bigtime" feature. So just clear the high bits.
|
||||
*/
|
||||
v_mtime = verifier[0] & 0x7fffffff;
|
||||
v_atime = verifier[1] & 0x7fffffff;
|
||||
}
|
||||
|
||||
if (d_really_is_positive(child)) {
|
||||
status = nfs_ok;
|
||||
|
||||
switch (argp->createmode) {
|
||||
case NFS3_CREATE_UNCHECKED:
|
||||
if (!d_is_reg(child))
|
||||
break;
|
||||
iap->ia_valid &= ATTR_SIZE;
|
||||
goto set_attr;
|
||||
case NFS3_CREATE_GUARDED:
|
||||
status = nfserr_exist;
|
||||
break;
|
||||
case NFS3_CREATE_EXCLUSIVE:
|
||||
if (d_inode(child)->i_mtime.tv_sec == v_mtime &&
|
||||
d_inode(child)->i_atime.tv_sec == v_atime &&
|
||||
d_inode(child)->i_size == 0) {
|
||||
break;
|
||||
}
|
||||
status = nfserr_exist;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!IS_POSIXACL(inode))
|
||||
iap->ia_mode &= ~current_umask();
|
||||
|
||||
fh_fill_pre_attrs(fhp);
|
||||
host_err = vfs_create(inode, child, iap->ia_mode, true);
|
||||
if (host_err < 0) {
|
||||
status = nfserrno(host_err);
|
||||
goto out;
|
||||
}
|
||||
fh_fill_post_attrs(fhp);
|
||||
|
||||
/* A newly created file already has a file size of zero. */
|
||||
if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
|
||||
iap->ia_valid &= ~ATTR_SIZE;
|
||||
if (argp->createmode == NFS3_CREATE_EXCLUSIVE) {
|
||||
iap->ia_valid = ATTR_MTIME | ATTR_ATIME |
|
||||
ATTR_MTIME_SET | ATTR_ATIME_SET;
|
||||
iap->ia_mtime.tv_sec = v_mtime;
|
||||
iap->ia_atime.tv_sec = v_atime;
|
||||
iap->ia_mtime.tv_nsec = 0;
|
||||
iap->ia_atime.tv_nsec = 0;
|
||||
}
|
||||
|
||||
set_attr:
|
||||
status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
|
||||
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
if (child && !IS_ERR(child))
|
||||
dput(child);
|
||||
fh_drop_write(fhp);
|
||||
return status;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd3_proc_create(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_createargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_diropres *resp = rqstp->rq_resp;
|
||||
svc_fh *dirfhp, *newfhp = NULL;
|
||||
struct iattr *attr;
|
||||
svc_fh *dirfhp, *newfhp;
|
||||
|
||||
dprintk("nfsd: CREATE(3) %s %.*s\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
@ -224,21 +364,8 @@ nfsd3_proc_create(struct svc_rqst *rqstp)
|
||||
|
||||
dirfhp = fh_copy(&resp->dirfh, &argp->fh);
|
||||
newfhp = fh_init(&resp->fh, NFS3_FHSIZE);
|
||||
attr = &argp->attrs;
|
||||
|
||||
/* Unfudge the mode bits */
|
||||
attr->ia_mode &= ~S_IFMT;
|
||||
if (!(attr->ia_valid & ATTR_MODE)) {
|
||||
attr->ia_valid |= ATTR_MODE;
|
||||
attr->ia_mode = S_IFREG;
|
||||
} else {
|
||||
attr->ia_mode = (attr->ia_mode & ~S_IFMT) | S_IFREG;
|
||||
}
|
||||
|
||||
/* Now create the file and set attributes */
|
||||
resp->status = do_nfsd_create(rqstp, dirfhp, argp->name, argp->len,
|
||||
attr, newfhp, argp->createmode,
|
||||
(u32 *)argp->verf, NULL, NULL);
|
||||
resp->status = nfsd3_create_file(rqstp, dirfhp, newfhp, argp);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -250,6 +377,9 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_createargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_diropres *resp = rqstp->rq_resp;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = &argp->attrs,
|
||||
};
|
||||
|
||||
dprintk("nfsd: MKDIR(3) %s %.*s\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
@ -260,8 +390,7 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->dirfh, &argp->fh);
|
||||
fh_init(&resp->fh, NFS3_FHSIZE);
|
||||
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
|
||||
&argp->attrs, S_IFDIR, 0, &resp->fh);
|
||||
fh_unlock(&resp->dirfh);
|
||||
&attrs, S_IFDIR, 0, &resp->fh);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -270,6 +399,9 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_symlinkargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_diropres *resp = rqstp->rq_resp;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = &argp->attrs,
|
||||
};
|
||||
|
||||
if (argp->tlen == 0) {
|
||||
resp->status = nfserr_inval;
|
||||
@ -296,7 +428,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->dirfh, &argp->ffh);
|
||||
fh_init(&resp->fh, NFS3_FHSIZE);
|
||||
resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname,
|
||||
argp->flen, argp->tname, &resp->fh);
|
||||
argp->flen, argp->tname, &attrs, &resp->fh);
|
||||
kfree(argp->tname);
|
||||
out:
|
||||
return rpc_success;
|
||||
@ -310,6 +442,9 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_mknodargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_diropres *resp = rqstp->rq_resp;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = &argp->attrs,
|
||||
};
|
||||
int type;
|
||||
dev_t rdev = 0;
|
||||
|
||||
@ -335,8 +470,7 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp)
|
||||
|
||||
type = nfs3_ftypes[argp->ftype];
|
||||
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
|
||||
&argp->attrs, type, rdev, &resp->fh);
|
||||
fh_unlock(&resp->dirfh);
|
||||
&attrs, type, rdev, &resp->fh);
|
||||
out:
|
||||
return rpc_success;
|
||||
}
|
||||
@ -359,7 +493,6 @@ nfsd3_proc_remove(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR,
|
||||
argp->name, argp->len);
|
||||
fh_unlock(&resp->fh);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -380,7 +513,6 @@ nfsd3_proc_rmdir(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_unlink(rqstp, &resp->fh, S_IFDIR,
|
||||
argp->name, argp->len);
|
||||
fh_unlock(&resp->fh);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -426,6 +558,26 @@ nfsd3_proc_link(struct svc_rqst *rqstp)
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static void nfsd3_init_dirlist_pages(struct svc_rqst *rqstp,
|
||||
struct nfsd3_readdirres *resp,
|
||||
u32 count)
|
||||
{
|
||||
struct xdr_buf *buf = &resp->dirlist;
|
||||
struct xdr_stream *xdr = &resp->xdr;
|
||||
unsigned int sendbuf = min_t(unsigned int, rqstp->rq_res.buflen,
|
||||
svc_max_payload(rqstp));
|
||||
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
|
||||
/* Reserve room for the NULL ptr & eof flag (-2 words) */
|
||||
buf->buflen = clamp(count, (u32)(XDR_UNIT * 2), sendbuf);
|
||||
buf->buflen -= XDR_UNIT * 2;
|
||||
buf->pages = rqstp->rq_next_page;
|
||||
rqstp->rq_next_page += (buf->buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
|
||||
xdr_init_encode_pages(xdr, buf, buf->pages, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a portion of a directory.
|
||||
*/
|
||||
@ -434,53 +586,26 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_readdirargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_readdirres *resp = rqstp->rq_resp;
|
||||
int count = 0;
|
||||
struct page **p;
|
||||
caddr_t page_addr = NULL;
|
||||
loff_t offset;
|
||||
|
||||
dprintk("nfsd: READDIR(3) %s %d bytes at %d\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
argp->count, (u32) argp->cookie);
|
||||
|
||||
/* Make sure we've room for the NULL ptr & eof flag, and shrink to
|
||||
* client read size */
|
||||
count = (argp->count >> 2) - 2;
|
||||
nfsd3_init_dirlist_pages(rqstp, resp, argp->count);
|
||||
|
||||
/* Read directory and encode entries on the fly */
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
|
||||
resp->buflen = count;
|
||||
resp->common.err = nfs_ok;
|
||||
resp->buffer = argp->buffer;
|
||||
resp->cookie_offset = 0;
|
||||
resp->rqstp = rqstp;
|
||||
resp->status = nfsd_readdir(rqstp, &resp->fh, (loff_t *)&argp->cookie,
|
||||
&resp->common, nfs3svc_encode_entry);
|
||||
offset = argp->cookie;
|
||||
resp->status = nfsd_readdir(rqstp, &resp->fh, &offset,
|
||||
&resp->common, nfs3svc_encode_entry3);
|
||||
memcpy(resp->verf, argp->verf, 8);
|
||||
count = 0;
|
||||
for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) {
|
||||
page_addr = page_address(*p);
|
||||
nfs3svc_encode_cookie3(resp, offset);
|
||||
|
||||
if (((caddr_t)resp->buffer >= page_addr) &&
|
||||
((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) {
|
||||
count += (caddr_t)resp->buffer - page_addr;
|
||||
break;
|
||||
}
|
||||
count += PAGE_SIZE;
|
||||
}
|
||||
resp->count = count >> 2;
|
||||
if (resp->offset) {
|
||||
loff_t offset = argp->cookie;
|
||||
|
||||
if (unlikely(resp->offset1)) {
|
||||
/* we ended up with offset on a page boundary */
|
||||
*resp->offset = htonl(offset >> 32);
|
||||
*resp->offset1 = htonl(offset & 0xffffffff);
|
||||
resp->offset1 = NULL;
|
||||
} else {
|
||||
xdr_encode_hyper(resp->offset, offset);
|
||||
}
|
||||
resp->offset = NULL;
|
||||
}
|
||||
/* Recycle only pages that were part of the reply */
|
||||
rqstp->rq_next_page = resp->xdr.page_ptr + 1;
|
||||
|
||||
return rpc_success;
|
||||
}
|
||||
@ -494,25 +619,17 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_readdirargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_readdirres *resp = rqstp->rq_resp;
|
||||
int count = 0;
|
||||
loff_t offset;
|
||||
struct page **p;
|
||||
caddr_t page_addr = NULL;
|
||||
|
||||
dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
argp->count, (u32) argp->cookie);
|
||||
|
||||
/* Convert byte count to number of words (i.e. >> 2),
|
||||
* and reserve room for the NULL ptr & eof flag (-2 words) */
|
||||
resp->count = (argp->count >> 2) - 2;
|
||||
nfsd3_init_dirlist_pages(rqstp, resp, argp->count);
|
||||
|
||||
/* Read directory and encode entries on the fly */
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
|
||||
resp->common.err = nfs_ok;
|
||||
resp->buffer = argp->buffer;
|
||||
resp->buflen = resp->count;
|
||||
resp->cookie_offset = 0;
|
||||
resp->rqstp = rqstp;
|
||||
offset = argp->cookie;
|
||||
|
||||
@ -526,30 +643,12 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp)
|
||||
}
|
||||
|
||||
resp->status = nfsd_readdir(rqstp, &resp->fh, &offset,
|
||||
&resp->common, nfs3svc_encode_entry_plus);
|
||||
&resp->common, nfs3svc_encode_entryplus3);
|
||||
memcpy(resp->verf, argp->verf, 8);
|
||||
for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) {
|
||||
page_addr = page_address(*p);
|
||||
nfs3svc_encode_cookie3(resp, offset);
|
||||
|
||||
if (((caddr_t)resp->buffer >= page_addr) &&
|
||||
((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) {
|
||||
count += (caddr_t)resp->buffer - page_addr;
|
||||
break;
|
||||
}
|
||||
count += PAGE_SIZE;
|
||||
}
|
||||
resp->count = count >> 2;
|
||||
if (resp->offset) {
|
||||
if (unlikely(resp->offset1)) {
|
||||
/* we ended up with offset on a page boundary */
|
||||
*resp->offset = htonl(offset >> 32);
|
||||
*resp->offset1 = htonl(offset & 0xffffffff);
|
||||
resp->offset1 = NULL;
|
||||
} else {
|
||||
xdr_encode_hyper(resp->offset, offset);
|
||||
}
|
||||
resp->offset = NULL;
|
||||
}
|
||||
/* Recycle only pages that were part of the reply */
|
||||
rqstp->rq_next_page = resp->xdr.page_ptr + 1;
|
||||
|
||||
out:
|
||||
return rpc_success;
|
||||
@ -665,20 +764,21 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd3_commitargs *argp = rqstp->rq_argp;
|
||||
struct nfsd3_commitres *resp = rqstp->rq_resp;
|
||||
struct nfsd_file *nf;
|
||||
|
||||
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) {
|
||||
resp->status = nfserr_inval;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_commit(rqstp, &resp->fh, argp->offset,
|
||||
resp->status = nfsd_file_acquire_gc(rqstp, &resp->fh, NFSD_MAY_WRITE |
|
||||
NFSD_MAY_NOT_BREAK_LEASE, &nf);
|
||||
if (resp->status)
|
||||
goto out;
|
||||
resp->status = nfsd_commit(rqstp, &resp->fh, nf, argp->offset,
|
||||
argp->count, resp->verf);
|
||||
nfsd_file_put(nf);
|
||||
out:
|
||||
return rpc_success;
|
||||
}
|
||||
@ -688,18 +788,14 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
|
||||
* NFSv3 Server procedures.
|
||||
* Only the results of non-idempotent operations are cached.
|
||||
*/
|
||||
#define nfs3svc_decode_fhandleargs nfs3svc_decode_fhandle
|
||||
#define nfs3svc_encode_attrstatres nfs3svc_encode_attrstat
|
||||
#define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat
|
||||
#define nfsd3_mkdirargs nfsd3_createargs
|
||||
#define nfsd3_readdirplusargs nfsd3_readdirargs
|
||||
#define nfsd3_fhandleargs nfsd_fhandle
|
||||
#define nfsd3_fhandleres nfsd3_attrstat
|
||||
#define nfsd3_attrstatres nfsd3_attrstat
|
||||
#define nfsd3_wccstatres nfsd3_attrstat
|
||||
#define nfsd3_createres nfsd3_diropres
|
||||
#define nfsd3_voidres nfsd3_voidargs
|
||||
struct nfsd3_voidargs { int dummy; };
|
||||
|
||||
#define ST 1 /* status*/
|
||||
#define FH 17 /* filehandle with length */
|
||||
@ -710,22 +806,26 @@ 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),
|
||||
.pc_decode = nfssvc_decode_voidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd_voidargs),
|
||||
.pc_argzero = sizeof(struct nfsd_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[NFS3PROC_GETATTR] = {
|
||||
.pc_func = nfsd3_proc_getattr,
|
||||
.pc_decode = nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = nfs3svc_encode_attrstatres,
|
||||
.pc_encode = nfs3svc_encode_getattrres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_argzero = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd3_attrstatres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT,
|
||||
.pc_name = "GETATTR",
|
||||
},
|
||||
[NFS3PROC_SETATTR] = {
|
||||
.pc_func = nfsd3_proc_setattr,
|
||||
@ -733,19 +833,23 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_wccstatres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_sattrargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_sattrargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_wccstatres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC,
|
||||
.pc_name = "SETATTR",
|
||||
},
|
||||
[NFS3PROC_LOOKUP] = {
|
||||
.pc_func = nfsd3_proc_lookup,
|
||||
.pc_decode = nfs3svc_decode_diropargs,
|
||||
.pc_encode = nfs3svc_encode_diropres,
|
||||
.pc_encode = nfs3svc_encode_lookupres,
|
||||
.pc_release = nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_diropargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_diropres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+FH+pAT+pAT,
|
||||
.pc_name = "LOOKUP",
|
||||
},
|
||||
[NFS3PROC_ACCESS] = {
|
||||
.pc_func = nfsd3_proc_access,
|
||||
@ -753,19 +857,23 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_accessres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_accessargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_accessargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_accessres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+1,
|
||||
.pc_name = "ACCESS",
|
||||
},
|
||||
[NFS3PROC_READLINK] = {
|
||||
.pc_func = nfsd3_proc_readlink,
|
||||
.pc_decode = nfs3svc_decode_readlinkargs,
|
||||
.pc_decode = nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = nfs3svc_encode_readlinkres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readlinkargs),
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_argzero = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd3_readlinkres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+1+NFS3_MAXPATHLEN/4,
|
||||
.pc_name = "READLINK",
|
||||
},
|
||||
[NFS3PROC_READ] = {
|
||||
.pc_func = nfsd3_proc_read,
|
||||
@ -773,9 +881,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_readres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_readargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_readres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+4+NFSSVC_MAXBLKSIZE/4,
|
||||
.pc_name = "READ",
|
||||
},
|
||||
[NFS3PROC_WRITE] = {
|
||||
.pc_func = nfsd3_proc_write,
|
||||
@ -783,9 +893,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_writeres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_writeargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_writeargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_writeres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC+4,
|
||||
.pc_name = "WRITE",
|
||||
},
|
||||
[NFS3PROC_CREATE] = {
|
||||
.pc_func = nfsd3_proc_create,
|
||||
@ -793,9 +905,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_createres,
|
||||
.pc_release = nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_createargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_createargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
.pc_name = "CREATE",
|
||||
},
|
||||
[NFS3PROC_MKDIR] = {
|
||||
.pc_func = nfsd3_proc_mkdir,
|
||||
@ -803,9 +917,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_createres,
|
||||
.pc_release = nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_mkdirargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_mkdirargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
.pc_name = "MKDIR",
|
||||
},
|
||||
[NFS3PROC_SYMLINK] = {
|
||||
.pc_func = nfsd3_proc_symlink,
|
||||
@ -813,9 +929,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_createres,
|
||||
.pc_release = nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_symlinkargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_symlinkargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
.pc_name = "SYMLINK",
|
||||
},
|
||||
[NFS3PROC_MKNOD] = {
|
||||
.pc_func = nfsd3_proc_mknod,
|
||||
@ -823,9 +941,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_createres,
|
||||
.pc_release = nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_mknodargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_mknodargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
.pc_name = "MKNOD",
|
||||
},
|
||||
[NFS3PROC_REMOVE] = {
|
||||
.pc_func = nfsd3_proc_remove,
|
||||
@ -833,9 +953,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_wccstatres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_diropargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_wccstatres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC,
|
||||
.pc_name = "REMOVE",
|
||||
},
|
||||
[NFS3PROC_RMDIR] = {
|
||||
.pc_func = nfsd3_proc_rmdir,
|
||||
@ -843,9 +965,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_wccstatres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_diropargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_wccstatres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC,
|
||||
.pc_name = "RMDIR",
|
||||
},
|
||||
[NFS3PROC_RENAME] = {
|
||||
.pc_func = nfsd3_proc_rename,
|
||||
@ -853,9 +977,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_renameres,
|
||||
.pc_release = nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_renameargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_renameargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_renameres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC+WC,
|
||||
.pc_name = "RENAME",
|
||||
},
|
||||
[NFS3PROC_LINK] = {
|
||||
.pc_func = nfsd3_proc_link,
|
||||
@ -863,9 +989,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_linkres,
|
||||
.pc_release = nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_linkargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_linkargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_linkres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+pAT+WC,
|
||||
.pc_name = "LINK",
|
||||
},
|
||||
[NFS3PROC_READDIR] = {
|
||||
.pc_func = nfsd3_proc_readdir,
|
||||
@ -873,8 +1001,10 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_readdirres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readdirargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_readdirargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_readdirres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_name = "READDIR",
|
||||
},
|
||||
[NFS3PROC_READDIRPLUS] = {
|
||||
.pc_func = nfsd3_proc_readdirplus,
|
||||
@ -882,35 +1012,43 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_readdirres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readdirplusargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_readdirplusargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_readdirres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_name = "READDIRPLUS",
|
||||
},
|
||||
[NFS3PROC_FSSTAT] = {
|
||||
.pc_func = nfsd3_proc_fsstat,
|
||||
.pc_decode = nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = nfs3svc_encode_fsstatres,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_fsstatres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+2*6+1,
|
||||
.pc_name = "FSSTAT",
|
||||
},
|
||||
[NFS3PROC_FSINFO] = {
|
||||
.pc_func = nfsd3_proc_fsinfo,
|
||||
.pc_decode = nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = nfs3svc_encode_fsinfores,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_fsinfores),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+12,
|
||||
.pc_name = "FSINFO",
|
||||
},
|
||||
[NFS3PROC_PATHCONF] = {
|
||||
.pc_func = nfsd3_proc_pathconf,
|
||||
.pc_decode = nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = nfs3svc_encode_pathconfres,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_pathconfres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+6,
|
||||
.pc_name = "PATHCONF",
|
||||
},
|
||||
[NFS3PROC_COMMIT] = {
|
||||
.pc_func = nfsd3_proc_commit,
|
||||
@ -918,9 +1056,11 @@ static const struct svc_procedure nfsd_procedures3[22] = {
|
||||
.pc_encode = nfs3svc_encode_commitres,
|
||||
.pc_release = nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_commitargs),
|
||||
.pc_argzero = sizeof(struct nfsd3_commitargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_commitres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+WC+2,
|
||||
.pc_name = "COMMIT",
|
||||
},
|
||||
};
|
||||
|
||||
|
1759
fs/nfsd/nfs3xdr.c
1759
fs/nfsd/nfs3xdr.c
File diff suppressed because it is too large
Load Diff
@ -751,57 +751,26 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
|
||||
return ret;
|
||||
}
|
||||
|
||||
__be32
|
||||
nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct nfs4_acl *acl)
|
||||
__be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl,
|
||||
struct nfsd_attrs *attr)
|
||||
{
|
||||
__be32 error;
|
||||
int host_error;
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
struct posix_acl *pacl = NULL, *dpacl = NULL;
|
||||
unsigned int flags = 0;
|
||||
|
||||
/* Get inode */
|
||||
error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
|
||||
if (error)
|
||||
return error;
|
||||
if (!acl)
|
||||
return nfs_ok;
|
||||
|
||||
dentry = fhp->fh_dentry;
|
||||
inode = d_inode(dentry);
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
if (type == NF4DIR)
|
||||
flags = NFS4_ACL_DIR;
|
||||
|
||||
host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
|
||||
host_error = nfs4_acl_nfsv4_to_posix(acl, &attr->na_pacl,
|
||||
&attr->na_dpacl, flags);
|
||||
if (host_error == -EINVAL)
|
||||
return nfserr_attrnotsupp;
|
||||
if (host_error < 0)
|
||||
goto out_nfserr;
|
||||
|
||||
fh_lock(fhp);
|
||||
|
||||
host_error = set_posix_acl(inode, ACL_TYPE_ACCESS, pacl);
|
||||
if (host_error < 0)
|
||||
goto out_drop_lock;
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
host_error = set_posix_acl(inode, ACL_TYPE_DEFAULT, dpacl);
|
||||
}
|
||||
|
||||
out_drop_lock:
|
||||
fh_unlock(fhp);
|
||||
|
||||
posix_acl_release(pacl);
|
||||
posix_acl_release(dpacl);
|
||||
out_nfserr:
|
||||
if (host_error == -EOPNOTSUPP)
|
||||
return nfserr_attrnotsupp;
|
||||
else
|
||||
return nfserrno(host_error);
|
||||
}
|
||||
|
||||
|
||||
static short
|
||||
ace2type(struct nfs4_ace *ace)
|
||||
{
|
||||
|
@ -76,6 +76,17 @@ static __be32 *xdr_encode_empty_array(__be32 *p)
|
||||
* 1 Protocol"
|
||||
*/
|
||||
|
||||
static void encode_uint32(struct xdr_stream *xdr, u32 n)
|
||||
{
|
||||
WARN_ON_ONCE(xdr_stream_encode_u32(xdr, n) < 0);
|
||||
}
|
||||
|
||||
static void encode_bitmap4(struct xdr_stream *xdr, const __u32 *bitmap,
|
||||
size_t len)
|
||||
{
|
||||
WARN_ON_ONCE(xdr_stream_encode_uint32_array(xdr, bitmap, len) < 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs_cb_opnum4
|
||||
*
|
||||
@ -121,7 +132,7 @@ static void encode_nfs_fh4(struct xdr_stream *xdr, const struct knfsd_fh *fh)
|
||||
|
||||
BUG_ON(length > NFS4_FHSIZE);
|
||||
p = xdr_reserve_space(xdr, 4 + length);
|
||||
xdr_encode_opaque(p, &fh->fh_base, length);
|
||||
xdr_encode_opaque(p, &fh->fh_raw, length);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -328,6 +339,24 @@ static void encode_cb_recall4args(struct xdr_stream *xdr,
|
||||
hdr->nops++;
|
||||
}
|
||||
|
||||
/*
|
||||
* CB_RECALLANY4args
|
||||
*
|
||||
* struct CB_RECALLANY4args {
|
||||
* uint32_t craa_objects_to_keep;
|
||||
* bitmap4 craa_type_mask;
|
||||
* };
|
||||
*/
|
||||
static void
|
||||
encode_cb_recallany4args(struct xdr_stream *xdr,
|
||||
struct nfs4_cb_compound_hdr *hdr, struct nfsd4_cb_recall_any *ra)
|
||||
{
|
||||
encode_nfs_cb_opnum4(xdr, OP_CB_RECALL_ANY);
|
||||
encode_uint32(xdr, ra->ra_keep);
|
||||
encode_bitmap4(xdr, ra->ra_bmval, ARRAY_SIZE(ra->ra_bmval));
|
||||
hdr->nops++;
|
||||
}
|
||||
|
||||
/*
|
||||
* CB_SEQUENCE4args
|
||||
*
|
||||
@ -482,6 +511,26 @@ static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
|
||||
encode_cb_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
|
||||
*/
|
||||
static void
|
||||
nfs4_xdr_enc_cb_recall_any(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr, const void *data)
|
||||
{
|
||||
const struct nfsd4_callback *cb = data;
|
||||
struct nfsd4_cb_recall_any *ra;
|
||||
struct nfs4_cb_compound_hdr hdr = {
|
||||
.ident = cb->cb_clp->cl_cb_ident,
|
||||
.minorversion = cb->cb_clp->cl_minorversion,
|
||||
};
|
||||
|
||||
ra = container_of(cb, struct nfsd4_cb_recall_any, ra_cb);
|
||||
encode_cb_compound4args(xdr, &hdr);
|
||||
encode_cb_sequence4args(xdr, cb, &hdr);
|
||||
encode_cb_recallany4args(xdr, &hdr, ra);
|
||||
encode_cb_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* NFSv4.0 and NFSv4.1 XDR decode functions
|
||||
@ -520,6 +569,28 @@ static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
|
||||
return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
|
||||
*/
|
||||
static int
|
||||
nfs4_xdr_dec_cb_recall_any(struct rpc_rqst *rqstp,
|
||||
struct xdr_stream *xdr,
|
||||
void *data)
|
||||
{
|
||||
struct nfsd4_callback *cb = data;
|
||||
struct nfs4_cb_compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
status = decode_cb_compound4res(xdr, &hdr);
|
||||
if (unlikely(status))
|
||||
return status;
|
||||
status = decode_cb_sequence4res(xdr, cb);
|
||||
if (unlikely(status || cb->cb_seq_status))
|
||||
return status;
|
||||
status = decode_cb_op_status(xdr, OP_CB_RECALL_ANY, &cb->cb_status);
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFSD_PNFS
|
||||
/*
|
||||
* CB_LAYOUTRECALL4args
|
||||
@ -688,21 +759,22 @@ static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
|
||||
* };
|
||||
*/
|
||||
static void encode_offload_info4(struct xdr_stream *xdr,
|
||||
__be32 nfserr,
|
||||
const struct nfsd4_copy *cp)
|
||||
const struct nfsd4_cb_offload *cbo)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
*p++ = nfserr;
|
||||
if (!nfserr) {
|
||||
*p = cbo->co_nfserr;
|
||||
switch (cbo->co_nfserr) {
|
||||
case nfs_ok:
|
||||
p = xdr_reserve_space(xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
|
||||
p = xdr_encode_empty_array(p);
|
||||
p = xdr_encode_hyper(p, cp->cp_res.wr_bytes_written);
|
||||
*p++ = cpu_to_be32(cp->cp_res.wr_stable_how);
|
||||
p = xdr_encode_opaque_fixed(p, cp->cp_res.wr_verifier.data,
|
||||
p = xdr_encode_hyper(p, cbo->co_res.wr_bytes_written);
|
||||
*p++ = cpu_to_be32(cbo->co_res.wr_stable_how);
|
||||
p = xdr_encode_opaque_fixed(p, cbo->co_res.wr_verifier.data,
|
||||
NFS4_VERIFIER_SIZE);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
p = xdr_reserve_space(xdr, 8);
|
||||
/* We always return success if bytes were written */
|
||||
p = xdr_encode_hyper(p, 0);
|
||||
@ -710,18 +782,16 @@ static void encode_offload_info4(struct xdr_stream *xdr,
|
||||
}
|
||||
|
||||
static void encode_cb_offload4args(struct xdr_stream *xdr,
|
||||
__be32 nfserr,
|
||||
const struct knfsd_fh *fh,
|
||||
const struct nfsd4_copy *cp,
|
||||
const struct nfsd4_cb_offload *cbo,
|
||||
struct nfs4_cb_compound_hdr *hdr)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
*p++ = cpu_to_be32(OP_CB_OFFLOAD);
|
||||
encode_nfs_fh4(xdr, fh);
|
||||
encode_stateid4(xdr, &cp->cp_res.cb_stateid);
|
||||
encode_offload_info4(xdr, nfserr, cp);
|
||||
*p = cpu_to_be32(OP_CB_OFFLOAD);
|
||||
encode_nfs_fh4(xdr, &cbo->co_fh);
|
||||
encode_stateid4(xdr, &cbo->co_res.cb_stateid);
|
||||
encode_offload_info4(xdr, cbo);
|
||||
|
||||
hdr->nops++;
|
||||
}
|
||||
@ -731,8 +801,8 @@ static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
|
||||
const void *data)
|
||||
{
|
||||
const struct nfsd4_callback *cb = data;
|
||||
const struct nfsd4_copy *cp =
|
||||
container_of(cb, struct nfsd4_copy, cp_cb);
|
||||
const struct nfsd4_cb_offload *cbo =
|
||||
container_of(cb, struct nfsd4_cb_offload, co_cb);
|
||||
struct nfs4_cb_compound_hdr hdr = {
|
||||
.ident = 0,
|
||||
.minorversion = cb->cb_clp->cl_minorversion,
|
||||
@ -740,7 +810,7 @@ static void nfs4_xdr_enc_cb_offload(struct rpc_rqst *req,
|
||||
|
||||
encode_cb_compound4args(xdr, &hdr);
|
||||
encode_cb_sequence4args(xdr, cb, &hdr);
|
||||
encode_cb_offload4args(xdr, cp->nfserr, &cp->fh, cp, &hdr);
|
||||
encode_cb_offload4args(xdr, cbo, &hdr);
|
||||
encode_cb_nops(&hdr);
|
||||
}
|
||||
|
||||
@ -784,6 +854,7 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = {
|
||||
#endif
|
||||
PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock),
|
||||
PROC(CB_OFFLOAD, COMPOUND, cb_offload, cb_offload),
|
||||
PROC(CB_RECALL_ANY, COMPOUND, cb_recall_any, cb_recall_any),
|
||||
};
|
||||
|
||||
static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)];
|
||||
@ -941,37 +1012,43 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
|
||||
clp->cl_cb_conn.cb_xprt = conn->cb_xprt;
|
||||
clp->cl_cb_client = client;
|
||||
clp->cl_cb_cred = cred;
|
||||
trace_nfsd_cb_setup(clp);
|
||||
rcu_read_lock();
|
||||
trace_nfsd_cb_setup(clp, rpc_peeraddr2str(client, RPC_DISPLAY_NETID),
|
||||
args.authflavor);
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nfsd4_mark_cb_state(struct nfs4_client *clp, int newstate)
|
||||
{
|
||||
if (clp->cl_cb_state != newstate) {
|
||||
clp->cl_cb_state = newstate;
|
||||
trace_nfsd_cb_state(clp);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
|
||||
{
|
||||
if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
|
||||
return;
|
||||
clp->cl_cb_state = NFSD4_CB_DOWN;
|
||||
trace_nfsd_cb_state(clp);
|
||||
nfsd4_mark_cb_state(clp, NFSD4_CB_DOWN);
|
||||
}
|
||||
|
||||
static void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason)
|
||||
{
|
||||
if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
|
||||
return;
|
||||
clp->cl_cb_state = NFSD4_CB_FAULT;
|
||||
trace_nfsd_cb_state(clp);
|
||||
nfsd4_mark_cb_state(clp, NFSD4_CB_FAULT);
|
||||
}
|
||||
|
||||
static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
|
||||
|
||||
trace_nfsd_cb_done(clp, task->tk_status);
|
||||
if (task->tk_status)
|
||||
nfsd4_mark_cb_down(clp, task->tk_status);
|
||||
else {
|
||||
clp->cl_cb_state = NFSD4_CB_UP;
|
||||
trace_nfsd_cb_state(clp);
|
||||
}
|
||||
else
|
||||
nfsd4_mark_cb_state(clp, NFSD4_CB_UP);
|
||||
}
|
||||
|
||||
static void nfsd4_cb_probe_release(void *calldata)
|
||||
@ -995,8 +1072,8 @@ static const struct rpc_call_ops nfsd4_cb_probe_ops = {
|
||||
*/
|
||||
void nfsd4_probe_callback(struct nfs4_client *clp)
|
||||
{
|
||||
clp->cl_cb_state = NFSD4_CB_UNKNOWN;
|
||||
trace_nfsd_cb_state(clp);
|
||||
trace_nfsd_cb_probe(clp);
|
||||
nfsd4_mark_cb_state(clp, NFSD4_CB_UNKNOWN);
|
||||
set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags);
|
||||
nfsd4_run_cb(&clp->cl_cb_null);
|
||||
}
|
||||
@ -1009,11 +1086,10 @@ void nfsd4_probe_callback_sync(struct nfs4_client *clp)
|
||||
|
||||
void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
|
||||
{
|
||||
clp->cl_cb_state = NFSD4_CB_UNKNOWN;
|
||||
nfsd4_mark_cb_state(clp, NFSD4_CB_UNKNOWN);
|
||||
spin_lock(&clp->cl_lock);
|
||||
memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
|
||||
spin_unlock(&clp->cl_lock);
|
||||
trace_nfsd_cb_state(clp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1170,8 +1246,6 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_client *clp = cb->cb_clp;
|
||||
|
||||
trace_nfsd_cb_done(clp, task->tk_status);
|
||||
|
||||
if (!nfsd4_cb_sequence_done(task, cb))
|
||||
return;
|
||||
|
||||
@ -1231,6 +1305,9 @@ void nfsd4_destroy_callback_queue(void)
|
||||
/* must be called under the state lock */
|
||||
void nfsd4_shutdown_callback(struct nfs4_client *clp)
|
||||
{
|
||||
if (clp->cl_cb_state != NFSD4_CB_UNKNOWN)
|
||||
trace_nfsd_cb_shutdown(clp);
|
||||
|
||||
set_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags);
|
||||
/*
|
||||
* Note this won't actually result in a null callback;
|
||||
@ -1276,7 +1353,6 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
|
||||
* kill the old client:
|
||||
*/
|
||||
if (clp->cl_cb_client) {
|
||||
trace_nfsd_cb_shutdown(clp);
|
||||
rpc_shutdown_client(clp->cl_cb_client);
|
||||
clp->cl_cb_client = NULL;
|
||||
put_cred(clp->cl_cb_cred);
|
||||
@ -1322,8 +1398,6 @@ nfsd4_run_cb_work(struct work_struct *work)
|
||||
struct rpc_clnt *clnt;
|
||||
int flags;
|
||||
|
||||
trace_nfsd_cb_work(clp, cb->cb_msg.rpc_proc->p_name);
|
||||
|
||||
if (cb->cb_need_restart) {
|
||||
cb->cb_need_restart = false;
|
||||
} else {
|
||||
@ -1345,7 +1419,7 @@ nfsd4_run_cb_work(struct work_struct *work)
|
||||
* Don't send probe messages for 4.1 or later.
|
||||
*/
|
||||
if (!cb->cb_ops && clp->cl_minorversion) {
|
||||
clp->cl_cb_state = NFSD4_CB_UP;
|
||||
nfsd4_mark_cb_state(clp, NFSD4_CB_UP);
|
||||
nfsd41_destroy_cb(cb);
|
||||
return;
|
||||
}
|
||||
@ -1371,11 +1445,21 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
|
||||
cb->cb_holds_slot = false;
|
||||
}
|
||||
|
||||
void nfsd4_run_cb(struct nfsd4_callback *cb)
|
||||
/**
|
||||
* nfsd4_run_cb - queue up a callback job to run
|
||||
* @cb: callback to queue
|
||||
*
|
||||
* Kick off a callback to do its thing. Returns false if it was already
|
||||
* on a queue, true otherwise.
|
||||
*/
|
||||
bool nfsd4_run_cb(struct nfsd4_callback *cb)
|
||||
{
|
||||
struct nfs4_client *clp = cb->cb_clp;
|
||||
bool queued;
|
||||
|
||||
nfsd41_cb_inflight_begin(clp);
|
||||
if (!nfsd4_queue_cb(cb))
|
||||
queued = nfsd4_queue_cb(cb);
|
||||
if (!queued)
|
||||
nfsd41_cb_inflight_end(clp);
|
||||
return queued;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "idmap.h"
|
||||
#include "nfsd.h"
|
||||
#include "netns.h"
|
||||
#include "vfs.h"
|
||||
|
||||
/*
|
||||
* Turn off idmapping when using AUTH_SYS.
|
||||
@ -82,8 +83,8 @@ ent_init(struct cache_head *cnew, struct cache_head *citm)
|
||||
new->id = itm->id;
|
||||
new->type = itm->type;
|
||||
|
||||
strlcpy(new->name, itm->name, sizeof(new->name));
|
||||
strlcpy(new->authname, itm->authname, sizeof(new->authname));
|
||||
strscpy(new->name, itm->name, sizeof(new->name));
|
||||
strscpy(new->authname, itm->authname, sizeof(new->authname));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -548,7 +549,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen
|
||||
return nfserr_badowner;
|
||||
memcpy(key.name, name, namelen);
|
||||
key.name[namelen] = '\0';
|
||||
strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
|
||||
strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
|
||||
ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item);
|
||||
if (ret == -ENOENT)
|
||||
return nfserr_badowner;
|
||||
@ -584,7 +585,7 @@ static __be32 idmap_id_to_name(struct xdr_stream *xdr,
|
||||
int ret;
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
|
||||
strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
|
||||
strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
|
||||
ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);
|
||||
if (ret == -ENOENT)
|
||||
return encode_ascii_id(xdr, id);
|
||||
|
@ -421,7 +421,7 @@ nfsd4_insert_layout(struct nfsd4_layoutget *lgp, struct nfs4_layout_stateid *ls)
|
||||
new = kmem_cache_alloc(nfs4_layout_cache, GFP_KERNEL);
|
||||
if (!new)
|
||||
return nfserr_jukebox;
|
||||
memcpy(&new->lo_seg, seg, sizeof(lp->lo_seg));
|
||||
memcpy(&new->lo_seg, seg, sizeof(new->lo_seg));
|
||||
new->lo_state = ls;
|
||||
|
||||
spin_lock(&fp->fi_lock);
|
||||
@ -657,7 +657,7 @@ nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task)
|
||||
ktime_t now, cutoff;
|
||||
const struct nfsd4_layout_ops *ops;
|
||||
|
||||
|
||||
trace_nfsd_cb_layout_done(&ls->ls_stid.sc_stateid, task);
|
||||
switch (task->tk_status) {
|
||||
case 0:
|
||||
case -NFS4ERR_DELAY:
|
||||
|
1057
fs/nfsd/nfs4proc.c
1057
fs/nfsd/nfs4proc.c
File diff suppressed because it is too large
Load Diff
@ -626,7 +626,7 @@ nfsd4_legacy_tracking_init(struct net *net)
|
||||
status = nfsd4_load_reboot_recovery_data(net);
|
||||
if (status)
|
||||
goto err;
|
||||
printk("NFSD: Using legacy client tracking operations.\n");
|
||||
pr_info("NFSD: Using legacy client tracking operations.\n");
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -807,17 +807,17 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
|
||||
if (get_user(namelen, &ci->cc_name.cn_len))
|
||||
return -EFAULT;
|
||||
name.data = memdup_user(&ci->cc_name.cn_id, namelen);
|
||||
if (IS_ERR_OR_NULL(name.data))
|
||||
return -EFAULT;
|
||||
if (IS_ERR(name.data))
|
||||
return PTR_ERR(name.data);
|
||||
name.len = namelen;
|
||||
get_user(princhashlen, &ci->cc_princhash.cp_len);
|
||||
if (princhashlen > 0) {
|
||||
princhash.data = memdup_user(
|
||||
&ci->cc_princhash.cp_data,
|
||||
princhashlen);
|
||||
if (IS_ERR_OR_NULL(princhash.data)) {
|
||||
if (IS_ERR(princhash.data)) {
|
||||
kfree(name.data);
|
||||
return -EFAULT;
|
||||
return PTR_ERR(princhash.data);
|
||||
}
|
||||
princhash.len = princhashlen;
|
||||
} else
|
||||
@ -829,8 +829,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
|
||||
if (get_user(namelen, &cnm->cn_len))
|
||||
return -EFAULT;
|
||||
name.data = memdup_user(&cnm->cn_id, namelen);
|
||||
if (IS_ERR_OR_NULL(name.data))
|
||||
return -EFAULT;
|
||||
if (IS_ERR(name.data))
|
||||
return PTR_ERR(name.data);
|
||||
name.len = namelen;
|
||||
}
|
||||
if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) {
|
||||
@ -1030,7 +1030,7 @@ nfsd4_init_cld_pipe(struct net *net)
|
||||
|
||||
status = __nfsd4_init_cld_pipe(net);
|
||||
if (!status)
|
||||
printk("NFSD: Using old nfsdcld client tracking operations.\n");
|
||||
pr_info("NFSD: Using old nfsdcld client tracking operations.\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1607,7 +1607,7 @@ nfsd4_cld_tracking_init(struct net *net)
|
||||
nfs4_release_reclaim(nn);
|
||||
goto err_remove;
|
||||
} else
|
||||
printk("NFSD: Using nfsdcld client tracking operations.\n");
|
||||
pr_info("NFSD: Using nfsdcld client tracking operations.\n");
|
||||
return 0;
|
||||
|
||||
err_remove:
|
||||
@ -1866,7 +1866,7 @@ nfsd4_umh_cltrack_init(struct net *net)
|
||||
ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
|
||||
kfree(grace_start);
|
||||
if (!ret)
|
||||
printk("NFSD: Using UMH upcall client tracking operations.\n");
|
||||
pr_info("NFSD: Using UMH upcall client tracking operations.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
1705
fs/nfsd/nfs4state.c
1705
fs/nfsd/nfs4state.c
File diff suppressed because it is too large
Load Diff
3739
fs/nfsd/nfs4xdr.c
3739
fs/nfsd/nfs4xdr.c
File diff suppressed because it is too large
Load Diff
@ -84,12 +84,6 @@ nfsd_hashsize(unsigned int limit)
|
||||
return roundup_pow_of_two(limit / TARGET_BUCKET_SIZE);
|
||||
}
|
||||
|
||||
static u32
|
||||
nfsd_cache_hash(__be32 xid, struct nfsd_net *nn)
|
||||
{
|
||||
return hash_32(be32_to_cpu(xid), nn->maskbits);
|
||||
}
|
||||
|
||||
static struct svc_cacherep *
|
||||
nfsd_reply_cache_alloc(struct svc_rqst *rqstp, __wsum csum,
|
||||
struct nfsd_net *nn)
|
||||
@ -121,14 +115,14 @@ nfsd_reply_cache_free_locked(struct nfsd_drc_bucket *b, struct svc_cacherep *rp,
|
||||
struct nfsd_net *nn)
|
||||
{
|
||||
if (rp->c_type == RC_REPLBUFF && rp->c_replvec.iov_base) {
|
||||
nn->drc_mem_usage -= rp->c_replvec.iov_len;
|
||||
nfsd_stats_drc_mem_usage_sub(nn, rp->c_replvec.iov_len);
|
||||
kfree(rp->c_replvec.iov_base);
|
||||
}
|
||||
if (rp->c_state != RC_UNUSED) {
|
||||
rb_erase(&rp->c_node, &b->rb_head);
|
||||
list_del(&rp->c_lru);
|
||||
atomic_dec(&nn->num_drc_entries);
|
||||
nn->drc_mem_usage -= sizeof(*rp);
|
||||
nfsd_stats_drc_mem_usage_sub(nn, sizeof(*rp));
|
||||
}
|
||||
kmem_cache_free(drc_slab, rp);
|
||||
}
|
||||
@ -154,6 +148,16 @@ void nfsd_drc_slab_free(void)
|
||||
kmem_cache_destroy(drc_slab);
|
||||
}
|
||||
|
||||
static int nfsd_reply_cache_stats_init(struct nfsd_net *nn)
|
||||
{
|
||||
return nfsd_percpu_counters_init(nn->counter, NFSD_NET_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
static void nfsd_reply_cache_stats_destroy(struct nfsd_net *nn)
|
||||
{
|
||||
nfsd_percpu_counters_destroy(nn->counter, NFSD_NET_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
int nfsd_reply_cache_init(struct nfsd_net *nn)
|
||||
{
|
||||
unsigned int hashsize;
|
||||
@ -165,12 +169,16 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
|
||||
hashsize = nfsd_hashsize(nn->max_drc_entries);
|
||||
nn->maskbits = ilog2(hashsize);
|
||||
|
||||
status = nfsd_reply_cache_stats_init(nn);
|
||||
if (status)
|
||||
goto out_nomem;
|
||||
|
||||
nn->nfsd_reply_cache_shrinker.scan_objects = nfsd_reply_cache_scan;
|
||||
nn->nfsd_reply_cache_shrinker.count_objects = nfsd_reply_cache_count;
|
||||
nn->nfsd_reply_cache_shrinker.seeks = 1;
|
||||
status = register_shrinker(&nn->nfsd_reply_cache_shrinker);
|
||||
if (status)
|
||||
goto out_nomem;
|
||||
goto out_stats_destroy;
|
||||
|
||||
nn->drc_hashtbl = kvzalloc(array_size(hashsize,
|
||||
sizeof(*nn->drc_hashtbl)), GFP_KERNEL);
|
||||
@ -186,6 +194,8 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
|
||||
return 0;
|
||||
out_shrinker:
|
||||
unregister_shrinker(&nn->nfsd_reply_cache_shrinker);
|
||||
out_stats_destroy:
|
||||
nfsd_reply_cache_stats_destroy(nn);
|
||||
out_nomem:
|
||||
printk(KERN_ERR "nfsd: failed to allocate reply cache\n");
|
||||
return -ENOMEM;
|
||||
@ -206,6 +216,7 @@ void nfsd_reply_cache_shutdown(struct nfsd_net *nn)
|
||||
rp, nn);
|
||||
}
|
||||
}
|
||||
nfsd_reply_cache_stats_destroy(nn);
|
||||
|
||||
kvfree(nn->drc_hashtbl);
|
||||
nn->drc_hashtbl = NULL;
|
||||
@ -224,8 +235,16 @@ lru_put_end(struct nfsd_drc_bucket *b, struct svc_cacherep *rp)
|
||||
list_move_tail(&rp->c_lru, &b->lru_head);
|
||||
}
|
||||
|
||||
static long
|
||||
prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn)
|
||||
static noinline struct nfsd_drc_bucket *
|
||||
nfsd_cache_bucket_find(__be32 xid, struct nfsd_net *nn)
|
||||
{
|
||||
unsigned int hash = hash_32((__force u32)xid, nn->maskbits);
|
||||
|
||||
return &nn->drc_hashtbl[hash];
|
||||
}
|
||||
|
||||
static long prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn,
|
||||
unsigned int max)
|
||||
{
|
||||
struct svc_cacherep *rp, *tmp;
|
||||
long freed = 0;
|
||||
@ -241,11 +260,17 @@ prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn)
|
||||
time_before(jiffies, rp->c_timestamp + RC_EXPIRE))
|
||||
break;
|
||||
nfsd_reply_cache_free_locked(b, rp, nn);
|
||||
freed++;
|
||||
if (max && freed++ > max)
|
||||
break;
|
||||
}
|
||||
return freed;
|
||||
}
|
||||
|
||||
static long nfsd_prune_bucket(struct nfsd_drc_bucket *b, struct nfsd_net *nn)
|
||||
{
|
||||
return prune_bucket(b, nn, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the LRU list and prune off entries that are older than RC_EXPIRE.
|
||||
* Also prune the oldest ones when the total exceeds the max number of entries.
|
||||
@ -262,7 +287,7 @@ prune_cache_entries(struct nfsd_net *nn)
|
||||
if (list_empty(&b->lru_head))
|
||||
continue;
|
||||
spin_lock(&b->cache_lock);
|
||||
freed += prune_bucket(b, nn);
|
||||
freed += prune_bucket(b, nn, 0);
|
||||
spin_unlock(&b->cache_lock);
|
||||
}
|
||||
return freed;
|
||||
@ -324,7 +349,7 @@ nfsd_cache_key_cmp(const struct svc_cacherep *key,
|
||||
{
|
||||
if (key->c_key.k_xid == rp->c_key.k_xid &&
|
||||
key->c_key.k_csum != rp->c_key.k_csum) {
|
||||
++nn->payload_misses;
|
||||
nfsd_stats_payload_misses_inc(nn);
|
||||
trace_nfsd_drc_mismatch(nn, key, rp);
|
||||
}
|
||||
|
||||
@ -396,18 +421,16 @@ nfsd_cache_insert(struct nfsd_drc_bucket *b, struct svc_cacherep *key,
|
||||
*/
|
||||
int nfsd_cache_lookup(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
struct nfsd_net *nn;
|
||||
struct svc_cacherep *rp, *found;
|
||||
__be32 xid = rqstp->rq_xid;
|
||||
__wsum csum;
|
||||
u32 hash = nfsd_cache_hash(xid, nn);
|
||||
struct nfsd_drc_bucket *b = &nn->drc_hashtbl[hash];
|
||||
struct nfsd_drc_bucket *b;
|
||||
int type = rqstp->rq_cachetype;
|
||||
int rtn = RC_DOIT;
|
||||
|
||||
rqstp->rq_cacherep = NULL;
|
||||
if (type == RC_NOCACHE) {
|
||||
nfsdstats.rcnocache++;
|
||||
nfsd_stats_rc_nocache_inc();
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -417,27 +440,25 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp)
|
||||
* Since the common case is a cache miss followed by an insert,
|
||||
* preallocate an entry.
|
||||
*/
|
||||
nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
rp = nfsd_reply_cache_alloc(rqstp, csum, nn);
|
||||
if (!rp)
|
||||
goto out;
|
||||
|
||||
b = nfsd_cache_bucket_find(rqstp->rq_xid, nn);
|
||||
spin_lock(&b->cache_lock);
|
||||
found = nfsd_cache_insert(b, rp, nn);
|
||||
if (found != rp) {
|
||||
nfsd_reply_cache_free_locked(NULL, rp, nn);
|
||||
rp = found;
|
||||
if (found != rp)
|
||||
goto found_entry;
|
||||
}
|
||||
|
||||
nfsdstats.rcmisses++;
|
||||
nfsd_stats_rc_misses_inc();
|
||||
rqstp->rq_cacherep = rp;
|
||||
rp->c_state = RC_INPROG;
|
||||
|
||||
atomic_inc(&nn->num_drc_entries);
|
||||
nn->drc_mem_usage += sizeof(*rp);
|
||||
nfsd_stats_drc_mem_usage_add(nn, sizeof(*rp));
|
||||
|
||||
/* go ahead and prune the cache */
|
||||
prune_bucket(b, nn);
|
||||
nfsd_prune_bucket(b, nn);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&b->cache_lock);
|
||||
@ -446,8 +467,10 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp)
|
||||
|
||||
found_entry:
|
||||
/* We found a matching entry which is either in progress or done. */
|
||||
nfsdstats.rchits++;
|
||||
nfsd_reply_cache_free_locked(NULL, rp, nn);
|
||||
nfsd_stats_rc_hits_inc();
|
||||
rtn = RC_DROPIT;
|
||||
rp = found;
|
||||
|
||||
/* Request being processed */
|
||||
if (rp->c_state == RC_INPROG)
|
||||
@ -506,7 +529,6 @@ void nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
struct svc_cacherep *rp = rqstp->rq_cacherep;
|
||||
struct kvec *resv = &rqstp->rq_res.head[0], *cachv;
|
||||
u32 hash;
|
||||
struct nfsd_drc_bucket *b;
|
||||
int len;
|
||||
size_t bufsize = 0;
|
||||
@ -514,8 +536,7 @@ void nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
|
||||
if (!rp)
|
||||
return;
|
||||
|
||||
hash = nfsd_cache_hash(rp->c_key.k_xid, nn);
|
||||
b = &nn->drc_hashtbl[hash];
|
||||
b = nfsd_cache_bucket_find(rp->c_key.k_xid, nn);
|
||||
|
||||
len = resv->iov_len - ((char*)statp - (char*)resv->iov_base);
|
||||
len >>= 2;
|
||||
@ -548,7 +569,7 @@ void nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
|
||||
return;
|
||||
}
|
||||
spin_lock(&b->cache_lock);
|
||||
nn->drc_mem_usage += bufsize;
|
||||
nfsd_stats_drc_mem_usage_add(nn, bufsize);
|
||||
lru_put_end(b, rp);
|
||||
rp->c_secure = test_bit(RQ_SECURE, &rqstp->rq_flags);
|
||||
rp->c_type = cachetype;
|
||||
@ -582,28 +603,26 @@ nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
|
||||
* scraping this file for info should test the labels to ensure they're
|
||||
* getting the correct field.
|
||||
*/
|
||||
static int nfsd_reply_cache_stats_show(struct seq_file *m, void *v)
|
||||
int nfsd_reply_cache_stats_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct nfsd_net *nn = m->private;
|
||||
struct nfsd_net *nn = net_generic(file_inode(m->file)->i_sb->s_fs_info,
|
||||
nfsd_net_id);
|
||||
|
||||
seq_printf(m, "max entries: %u\n", nn->max_drc_entries);
|
||||
seq_printf(m, "num entries: %u\n",
|
||||
atomic_read(&nn->num_drc_entries));
|
||||
seq_printf(m, "hash buckets: %u\n", 1 << nn->maskbits);
|
||||
seq_printf(m, "mem usage: %u\n", nn->drc_mem_usage);
|
||||
seq_printf(m, "cache hits: %u\n", nfsdstats.rchits);
|
||||
seq_printf(m, "cache misses: %u\n", nfsdstats.rcmisses);
|
||||
seq_printf(m, "not cached: %u\n", nfsdstats.rcnocache);
|
||||
seq_printf(m, "payload misses: %u\n", nn->payload_misses);
|
||||
seq_printf(m, "mem usage: %lld\n",
|
||||
percpu_counter_sum_positive(&nn->counter[NFSD_NET_DRC_MEM_USAGE]));
|
||||
seq_printf(m, "cache hits: %lld\n",
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_HITS]));
|
||||
seq_printf(m, "cache misses: %lld\n",
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_MISSES]));
|
||||
seq_printf(m, "not cached: %lld\n",
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_NOCACHE]));
|
||||
seq_printf(m, "payload misses: %lld\n",
|
||||
percpu_counter_sum_positive(&nn->counter[NFSD_NET_PAYLOAD_MISSES]));
|
||||
seq_printf(m, "longest chain len: %u\n", nn->longest_chain);
|
||||
seq_printf(m, "cachesize at longest: %u\n", nn->longest_chain_cachesize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfsd_reply_cache_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(file_inode(file)->i_sb->s_fs_info,
|
||||
nfsd_net_id);
|
||||
|
||||
return single_open(file, nfsd_reply_cache_stats_show, nn);
|
||||
}
|
||||
|
157
fs/nfsd/nfsctl.c
157
fs/nfsd/nfsctl.c
@ -25,6 +25,7 @@
|
||||
#include "state.h"
|
||||
#include "netns.h"
|
||||
#include "pnfs.h"
|
||||
#include "filecache.h"
|
||||
|
||||
/*
|
||||
* We have a single directory with several nodes in it.
|
||||
@ -32,6 +33,7 @@
|
||||
enum {
|
||||
NFSD_Root = 1,
|
||||
NFSD_List,
|
||||
NFSD_Export_Stats,
|
||||
NFSD_Export_features,
|
||||
NFSD_Fh,
|
||||
NFSD_FO_UnlockIP,
|
||||
@ -44,6 +46,7 @@ enum {
|
||||
NFSD_Ports,
|
||||
NFSD_MaxBlkSize,
|
||||
NFSD_MaxConnections,
|
||||
NFSD_Filecache,
|
||||
NFSD_SupportedEnctypes,
|
||||
/*
|
||||
* The below MUST come last. Otherwise we leave a hole in nfsd_files[]
|
||||
@ -182,17 +185,7 @@ static int export_features_show(struct seq_file *m, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int export_features_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, export_features_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations export_features_operations = {
|
||||
.open = export_features_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(export_features);
|
||||
|
||||
#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
|
||||
static int supported_enctypes_show(struct seq_file *m, void *v)
|
||||
@ -201,17 +194,7 @@ static int supported_enctypes_show(struct seq_file *m, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int supported_enctypes_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, supported_enctypes_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations supported_enctypes_ops = {
|
||||
.open = supported_enctypes_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(supported_enctypes);
|
||||
#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
|
||||
|
||||
static const struct file_operations pool_stats_operations = {
|
||||
@ -221,12 +204,9 @@ static const struct file_operations pool_stats_operations = {
|
||||
.release = nfsd_pool_stats_release,
|
||||
};
|
||||
|
||||
static const struct file_operations reply_cache_stats_operations = {
|
||||
.open = nfsd_reply_cache_stats_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(nfsd_reply_cache_stats);
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(nfsd_file_cache_stats);
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*
|
||||
@ -397,7 +377,7 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
|
||||
|
||||
mesg = buf;
|
||||
len = SIMPLE_TRANSACTION_LIMIT;
|
||||
qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
|
||||
qword_addhex(&mesg, &len, fh.fh_raw, fh.fh_size);
|
||||
mesg[-1] = '\n';
|
||||
return mesg - buf;
|
||||
}
|
||||
@ -601,7 +581,9 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
|
||||
|
||||
cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET;
|
||||
switch(num) {
|
||||
#ifdef CONFIG_NFSD_V2
|
||||
case 2:
|
||||
#endif
|
||||
case 3:
|
||||
nfsd_vers(nn, num, cmd);
|
||||
break;
|
||||
@ -621,6 +603,8 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Ignore requests to disable non-existent versions */
|
||||
if (cmd == NFSD_SET)
|
||||
return -EINVAL;
|
||||
}
|
||||
vers += len + 1;
|
||||
@ -632,7 +616,6 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
|
||||
}
|
||||
|
||||
/* Now write current state into reply buffer */
|
||||
len = 0;
|
||||
sep = "";
|
||||
remaining = SIMPLE_TRANSACTION_LIMIT;
|
||||
for (num=2 ; num <= 4 ; num++) {
|
||||
@ -726,28 +709,25 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred
|
||||
char *mesg = buf;
|
||||
int fd, err;
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv;
|
||||
|
||||
err = get_int(&mesg, &fd);
|
||||
if (err != 0 || fd < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (svc_alien_sock(net, fd)) {
|
||||
printk(KERN_ERR "%s: socket net is different to NFSd's one\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = nfsd_create_serv(net);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
|
||||
if (err < 0) {
|
||||
nfsd_destroy(net);
|
||||
return err;
|
||||
}
|
||||
serv = nn->nfsd_serv;
|
||||
err = svc_addsock(serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
|
||||
|
||||
/* Decrease the count, but don't shut down the service */
|
||||
nn->nfsd_serv->sv_nrthreads--;
|
||||
if (err < 0 && !serv->sv_nrthreads && !nn->keep_active)
|
||||
nfsd_last_thread(net);
|
||||
else if (err >= 0 && !serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
|
||||
svc_get(serv);
|
||||
|
||||
svc_put(serv);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -761,6 +741,7 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
|
||||
struct svc_xprt *xprt;
|
||||
int port, err;
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv;
|
||||
|
||||
if (sscanf(buf, "%15s %5u", transport, &port) != 2)
|
||||
return -EINVAL;
|
||||
@ -772,30 +753,33 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
err = svc_create_xprt(nn->nfsd_serv, transport, net,
|
||||
serv = nn->nfsd_serv;
|
||||
err = svc_xprt_create(serv, transport, net,
|
||||
PF_INET, port, SVC_SOCK_ANONYMOUS, cred);
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
|
||||
err = svc_create_xprt(nn->nfsd_serv, transport, net,
|
||||
err = svc_xprt_create(serv, transport, net,
|
||||
PF_INET6, port, SVC_SOCK_ANONYMOUS, cred);
|
||||
if (err < 0 && err != -EAFNOSUPPORT)
|
||||
goto out_close;
|
||||
|
||||
/* Decrease the count, but don't shut down the service */
|
||||
nn->nfsd_serv->sv_nrthreads--;
|
||||
if (!serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
|
||||
svc_get(serv);
|
||||
|
||||
svc_put(serv);
|
||||
return 0;
|
||||
out_close:
|
||||
xprt = svc_find_xprt(nn->nfsd_serv, transport, net, PF_INET, port);
|
||||
xprt = svc_find_xprt(serv, transport, net, PF_INET, port);
|
||||
if (xprt != NULL) {
|
||||
svc_close_xprt(xprt);
|
||||
svc_xprt_close(xprt);
|
||||
svc_xprt_put(xprt);
|
||||
}
|
||||
out_err:
|
||||
if (!list_empty(&nn->nfsd_serv->sv_permsocks))
|
||||
nn->nfsd_serv->sv_nrthreads--;
|
||||
else
|
||||
nfsd_destroy(net);
|
||||
if (!serv->sv_nrthreads && !nn->keep_active)
|
||||
nfsd_last_thread(net);
|
||||
|
||||
svc_put(serv);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1168,6 +1152,7 @@ static struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode)
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inc_nlink(inode);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1269,7 +1254,8 @@ static void nfsdfs_remove_files(struct dentry *root)
|
||||
/* XXX: cut'n'paste from simple_fill_super; figure out if we could share
|
||||
* code instead. */
|
||||
static int nfsdfs_create_files(struct dentry *root,
|
||||
const struct tree_descr *files)
|
||||
const struct tree_descr *files,
|
||||
struct dentry **fdentries)
|
||||
{
|
||||
struct inode *dir = d_inode(root);
|
||||
struct inode *inode;
|
||||
@ -1278,8 +1264,6 @@ static int nfsdfs_create_files(struct dentry *root,
|
||||
|
||||
inode_lock(dir);
|
||||
for (i = 0; files->name && files->name[0]; i++, files++) {
|
||||
if (!files->name)
|
||||
continue;
|
||||
dentry = d_alloc_name(root, files->name);
|
||||
if (!dentry)
|
||||
goto out;
|
||||
@ -1293,6 +1277,8 @@ static int nfsdfs_create_files(struct dentry *root,
|
||||
inode->i_private = __get_nfsdfs_client(dir);
|
||||
d_add(dentry, inode);
|
||||
fsnotify_create(dir, dentry);
|
||||
if (fdentries)
|
||||
fdentries[i] = dentry;
|
||||
}
|
||||
inode_unlock(dir);
|
||||
return 0;
|
||||
@ -1305,7 +1291,8 @@ static int nfsdfs_create_files(struct dentry *root,
|
||||
/* on success, returns positive number unique to that client. */
|
||||
struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
|
||||
struct nfsdfs_client *ncl, u32 id,
|
||||
const struct tree_descr *files)
|
||||
const struct tree_descr *files,
|
||||
struct dentry **fdentries)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
char name[11];
|
||||
@ -1316,7 +1303,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
|
||||
dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name);
|
||||
if (IS_ERR(dentry)) /* XXX: tossing errors? */
|
||||
return NULL;
|
||||
ret = nfsdfs_create_files(dentry, files);
|
||||
ret = nfsdfs_create_files(dentry, files, fdentries);
|
||||
if (ret) {
|
||||
nfsd_client_rmdir(dentry);
|
||||
return NULL;
|
||||
@ -1352,8 +1339,10 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
static const struct tree_descr nfsd_files[] = {
|
||||
[NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO},
|
||||
/* Per-export io stats use same ops as exports file */
|
||||
[NFSD_Export_Stats] = {"export_stats", &exports_nfsd_operations, S_IRUGO},
|
||||
[NFSD_Export_features] = {"export_features",
|
||||
&export_features_operations, S_IRUGO},
|
||||
&export_features_fops, S_IRUGO},
|
||||
[NFSD_FO_UnlockIP] = {"unlock_ip",
|
||||
&transaction_ops, S_IWUSR|S_IRUSR},
|
||||
[NFSD_FO_UnlockFS] = {"unlock_filesystem",
|
||||
@ -1362,13 +1351,16 @@ static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
|
||||
[NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
|
||||
[NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
|
||||
[NFSD_Reply_Cache_Stats] = {"reply_cache_stats", &reply_cache_stats_operations, S_IRUGO},
|
||||
[NFSD_Reply_Cache_Stats] = {"reply_cache_stats",
|
||||
&nfsd_reply_cache_stats_fops, S_IRUGO},
|
||||
[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
|
||||
[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
|
||||
[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
|
||||
[NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO},
|
||||
[NFSD_Filecache] = {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO},
|
||||
#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
|
||||
[NFSD_SupportedEnctypes] = {"supported_krb5_enctypes", &supported_enctypes_ops, S_IRUGO},
|
||||
[NFSD_SupportedEnctypes] = {"supported_krb5_enctypes",
|
||||
&supported_enctypes_fops, S_IRUGO},
|
||||
#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
|
||||
@ -1468,25 +1460,16 @@ static __net_init int nfsd_init_net(struct net *net)
|
||||
goto out_idmap_error;
|
||||
nn->nfsd_versions = NULL;
|
||||
nn->nfsd4_minorversions = NULL;
|
||||
nfsd4_init_leases_net(nn);
|
||||
retval = nfsd_reply_cache_init(nn);
|
||||
if (retval)
|
||||
goto out_drc_error;
|
||||
nn->nfsd4_lease = 90; /* default lease time */
|
||||
nn->nfsd4_grace = 90;
|
||||
nn->somebody_reclaimed = false;
|
||||
nn->track_reclaim_completes = false;
|
||||
nn->clverifier_counter = prandom_u32();
|
||||
nn->clientid_base = prandom_u32();
|
||||
nn->clientid_counter = nn->clientid_base + 1;
|
||||
nn->s2s_cp_cl_id = nn->clientid_counter++;
|
||||
|
||||
atomic_set(&nn->ntf_refcnt, 0);
|
||||
init_waitqueue_head(&nn->ntf_wq);
|
||||
seqlock_init(&nn->boot_lock);
|
||||
goto out_cache_error;
|
||||
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
|
||||
seqlock_init(&nn->writeverf_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
out_drc_error:
|
||||
out_cache_error:
|
||||
nfsd_idmap_shutdown(net);
|
||||
out_idmap_error:
|
||||
nfsd_export_shutdown(net);
|
||||
@ -1514,7 +1497,6 @@ static struct pernet_operations nfsd_net_ops = {
|
||||
static int __init init_nfsd(void)
|
||||
{
|
||||
int retval;
|
||||
printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
|
||||
|
||||
retval = nfsd4_init_slabs();
|
||||
if (retval)
|
||||
@ -1522,7 +1504,9 @@ static int __init init_nfsd(void)
|
||||
retval = nfsd4_init_pnfs();
|
||||
if (retval)
|
||||
goto out_free_slabs;
|
||||
nfsd_stat_init(); /* Statistics */
|
||||
retval = nfsd_stat_init(); /* Statistics */
|
||||
if (retval)
|
||||
goto out_free_pnfs;
|
||||
retval = nfsd_drc_slab_create();
|
||||
if (retval)
|
||||
goto out_free_stat;
|
||||
@ -1530,20 +1514,25 @@ static int __init init_nfsd(void)
|
||||
retval = create_proc_exports_entry();
|
||||
if (retval)
|
||||
goto out_free_lockd;
|
||||
retval = register_filesystem(&nfsd_fs_type);
|
||||
if (retval)
|
||||
goto out_free_exports;
|
||||
retval = register_pernet_subsys(&nfsd_net_ops);
|
||||
if (retval < 0)
|
||||
goto out_free_filesystem;
|
||||
goto out_free_exports;
|
||||
retval = register_cld_notifier();
|
||||
if (retval)
|
||||
goto out_free_subsys;
|
||||
retval = nfsd4_create_laundry_wq();
|
||||
if (retval)
|
||||
goto out_free_cld;
|
||||
retval = register_filesystem(&nfsd_fs_type);
|
||||
if (retval)
|
||||
goto out_free_all;
|
||||
return 0;
|
||||
out_free_all:
|
||||
nfsd4_destroy_laundry_wq();
|
||||
out_free_cld:
|
||||
unregister_cld_notifier();
|
||||
out_free_subsys:
|
||||
unregister_pernet_subsys(&nfsd_net_ops);
|
||||
out_free_filesystem:
|
||||
unregister_filesystem(&nfsd_fs_type);
|
||||
out_free_exports:
|
||||
remove_proc_entry("fs/nfs/exports", NULL);
|
||||
remove_proc_entry("fs/nfs", NULL);
|
||||
@ -1552,6 +1541,7 @@ static int __init init_nfsd(void)
|
||||
nfsd_drc_slab_free();
|
||||
out_free_stat:
|
||||
nfsd_stat_shutdown();
|
||||
out_free_pnfs:
|
||||
nfsd4_exit_pnfs();
|
||||
out_free_slabs:
|
||||
nfsd4_free_slabs();
|
||||
@ -1560,6 +1550,8 @@ static int __init init_nfsd(void)
|
||||
|
||||
static void __exit exit_nfsd(void)
|
||||
{
|
||||
unregister_filesystem(&nfsd_fs_type);
|
||||
nfsd4_destroy_laundry_wq();
|
||||
unregister_cld_notifier();
|
||||
unregister_pernet_subsys(&nfsd_net_ops);
|
||||
nfsd_drc_slab_free();
|
||||
@ -1569,7 +1561,6 @@ static void __exit exit_nfsd(void)
|
||||
nfsd_lockd_shutdown();
|
||||
nfsd4_free_slabs();
|
||||
nfsd4_exit_pnfs();
|
||||
unregister_filesystem(&nfsd_fs_type);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
|
||||
|
@ -24,8 +24,8 @@
|
||||
#include <uapi/linux/nfsd/debug.h>
|
||||
|
||||
#include "netns.h"
|
||||
#include "stats.h"
|
||||
#include "export.h"
|
||||
#include "stats.h"
|
||||
|
||||
#undef ifdebug
|
||||
#ifdef CONFIG_SUNRPC_DEBUG
|
||||
@ -64,8 +64,7 @@ struct readdir_cd {
|
||||
|
||||
|
||||
extern struct svc_program nfsd_program;
|
||||
extern const struct svc_version nfsd_version2, nfsd_version3,
|
||||
nfsd_version4;
|
||||
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
|
||||
extern struct mutex nfsd_mutex;
|
||||
extern spinlock_t nfsd_drc_lock;
|
||||
extern unsigned long nfsd_drc_max_mem;
|
||||
@ -73,6 +72,16 @@ extern unsigned long nfsd_drc_mem_used;
|
||||
|
||||
extern const struct seq_operations nfs_exports_op;
|
||||
|
||||
/*
|
||||
* Common void argument and result helpers
|
||||
*/
|
||||
struct nfsd_voidargs { };
|
||||
struct nfsd_voidres { };
|
||||
bool nfssvc_decode_voidarg(struct svc_rqst *rqstp,
|
||||
struct xdr_stream *xdr);
|
||||
bool nfssvc_encode_voidres(struct svc_rqst *rqstp,
|
||||
struct xdr_stream *xdr);
|
||||
|
||||
/*
|
||||
* Function prototypes.
|
||||
*/
|
||||
@ -87,8 +96,6 @@ int nfsd_pool_stats_open(struct inode *, struct file *);
|
||||
int nfsd_pool_stats_release(struct inode *, struct file *);
|
||||
void nfsd_shutdown_threads(struct net *net);
|
||||
|
||||
void nfsd_destroy(struct net *net);
|
||||
|
||||
bool i_am_nfsd(void);
|
||||
|
||||
struct nfsdfs_client {
|
||||
@ -98,7 +105,9 @@ struct nfsdfs_client {
|
||||
|
||||
struct nfsdfs_client *get_nfsdfs_client(struct inode *);
|
||||
struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
|
||||
struct nfsdfs_client *ncl, u32 id, const struct tree_descr *);
|
||||
struct nfsdfs_client *ncl, u32 id,
|
||||
const struct tree_descr *,
|
||||
struct dentry **fdentries);
|
||||
void nfsd_client_rmdir(struct dentry *dentry);
|
||||
|
||||
|
||||
@ -122,6 +131,7 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
|
||||
int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
|
||||
void nfsd_reset_versions(struct nfsd_net *nn);
|
||||
int nfsd_create_serv(struct net *net);
|
||||
void nfsd_last_thread(struct net *net);
|
||||
|
||||
extern int nfsd_max_blksize;
|
||||
|
||||
@ -150,6 +160,9 @@ void nfs4_state_shutdown_net(struct net *net);
|
||||
int nfs4_reset_recoverydir(char *recdir);
|
||||
char * nfs4_recoverydir(void);
|
||||
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
|
||||
int nfsd4_create_laundry_wq(void);
|
||||
void nfsd4_destroy_laundry_wq(void);
|
||||
bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode);
|
||||
#else
|
||||
static inline int nfsd4_init_slabs(void) { return 0; }
|
||||
static inline void nfsd4_free_slabs(void) { }
|
||||
@ -163,6 +176,13 @@ static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline int nfsd4_create_laundry_wq(void) { return 0; };
|
||||
static inline void nfsd4_destroy_laundry_wq(void) {};
|
||||
static inline bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp,
|
||||
struct inode *inode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -324,6 +344,10 @@ void nfsd_lockd_shutdown(void);
|
||||
#define COMPOUND_ERR_SLACK_SPACE 16 /* OP_SETATTR */
|
||||
|
||||
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
|
||||
#define NFSD_COURTESY_CLIENT_TIMEOUT (24 * 60 * 60) /* seconds */
|
||||
#define NFSD_CLIENT_MAX_TRIM_PER_RUN 128
|
||||
#define NFS4_CLIENTS_PER_GB 1024
|
||||
#define NFSD_DELEGRETURN_TIMEOUT (HZ / 34) /* 30ms */
|
||||
|
||||
/*
|
||||
* The following attributes are currently not supported by the NFSv4 server:
|
||||
@ -352,7 +376,7 @@ void nfsd_lockd_shutdown(void);
|
||||
| FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \
|
||||
| FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \
|
||||
| FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \
|
||||
| FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA \
|
||||
| FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_CREATE \
|
||||
| FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
|
||||
|
||||
#define NFSD4_SUPPORTED_ATTRS_WORD2 0
|
||||
@ -386,7 +410,6 @@ void nfsd_lockd_shutdown(void);
|
||||
|
||||
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
|
||||
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
|
||||
FATTR4_WORD2_CHANGE_ATTR_TYPE | \
|
||||
FATTR4_WORD2_MODE_UMASK | \
|
||||
NFSD4_2_SECURITY_ATTRS | \
|
||||
FATTR4_WORD2_XATTR_SUPPORT)
|
||||
@ -449,7 +472,8 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
|
||||
(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
|
||||
#define NFSD_WRITEABLE_ATTRS_WORD1 \
|
||||
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
|
||||
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
|
||||
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_CREATE \
|
||||
| FATTR4_WORD1_TIME_MODIFY_SET)
|
||||
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
|
||||
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL \
|
||||
FATTR4_WORD2_SECURITY_LABEL
|
||||
@ -475,12 +499,20 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
|
||||
extern int nfsd4_is_junction(struct dentry *dentry);
|
||||
extern int register_cld_notifier(void);
|
||||
extern void unregister_cld_notifier(void);
|
||||
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||
extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn);
|
||||
#endif
|
||||
|
||||
extern void nfsd4_init_leases_net(struct nfsd_net *nn);
|
||||
|
||||
#else /* CONFIG_NFSD_V4 */
|
||||
static inline int nfsd4_is_junction(struct dentry *dentry)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void nfsd4_init_leases_net(struct nfsd_net *nn) { };
|
||||
|
||||
#define register_cld_notifier() 0
|
||||
#define unregister_cld_notifier() do { } while(0)
|
||||
|
||||
|
199
fs/nfsd/nfsfh.c
199
fs/nfsd/nfsfh.c
@ -153,11 +153,12 @@ static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
|
||||
static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
{
|
||||
struct knfsd_fh *fh = &fhp->fh_handle;
|
||||
struct fid *fid = NULL, sfid;
|
||||
struct fid *fid = NULL;
|
||||
struct svc_export *exp;
|
||||
struct dentry *dentry;
|
||||
int fileid_type;
|
||||
int data_left = fh->fh_size/4;
|
||||
int len;
|
||||
__be32 error;
|
||||
|
||||
error = nfserr_stale;
|
||||
@ -166,8 +167,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
if (rqstp->rq_vers == 4 && fh->fh_size == 0)
|
||||
return nfserr_nofilehandle;
|
||||
|
||||
if (fh->fh_version == 1) {
|
||||
int len;
|
||||
if (fh->fh_version != 1)
|
||||
return error;
|
||||
|
||||
if (--data_left < 0)
|
||||
return error;
|
||||
@ -195,19 +196,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
return error;
|
||||
exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid);
|
||||
fid = (struct fid *)(fh->fh_fsid + len);
|
||||
} else {
|
||||
__u32 tfh[2];
|
||||
dev_t xdev;
|
||||
ino_t xino;
|
||||
|
||||
if (fh->fh_size != NFS_FHSIZE)
|
||||
return error;
|
||||
/* assume old filehandle format */
|
||||
xdev = old_decode_dev(fh->ofh_xdev);
|
||||
xino = u32_to_ino_t(fh->ofh_xino);
|
||||
mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
|
||||
exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
|
||||
}
|
||||
|
||||
error = nfserr_stale;
|
||||
if (IS_ERR(exp)) {
|
||||
@ -252,28 +240,25 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
if (rqstp->rq_vers > 2)
|
||||
error = nfserr_badhandle;
|
||||
|
||||
if (fh->fh_version != 1) {
|
||||
sfid.i32.ino = fh->ofh_ino;
|
||||
sfid.i32.gen = fh->ofh_generation;
|
||||
sfid.i32.parent_ino = fh->ofh_dirino;
|
||||
fid = &sfid;
|
||||
data_left = 3;
|
||||
if (fh->ofh_dirino == 0)
|
||||
fileid_type = FILEID_INO32_GEN;
|
||||
else
|
||||
fileid_type = FILEID_INO32_GEN_PARENT;
|
||||
} else
|
||||
fileid_type = fh->fh_fileid_type;
|
||||
|
||||
if (fileid_type == FILEID_ROOT)
|
||||
dentry = dget(exp->ex_path.dentry);
|
||||
else {
|
||||
dentry = exportfs_decode_fh(exp->ex_path.mnt, fid,
|
||||
dentry = exportfs_decode_fh_raw(exp->ex_path.mnt, fid,
|
||||
data_left, fileid_type,
|
||||
nfsd_acceptable, exp);
|
||||
if (IS_ERR_OR_NULL(dentry))
|
||||
if (IS_ERR_OR_NULL(dentry)) {
|
||||
trace_nfsd_set_fh_dentry_badhandle(rqstp, fhp,
|
||||
dentry ? PTR_ERR(dentry) : -ESTALE);
|
||||
switch (PTR_ERR(dentry)) {
|
||||
case -ENOMEM:
|
||||
case -ETIMEDOUT:
|
||||
break;
|
||||
default:
|
||||
dentry = ERR_PTR(-ESTALE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dentry == NULL)
|
||||
goto out;
|
||||
@ -291,6 +276,20 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
|
||||
fhp->fh_dentry = dentry;
|
||||
fhp->fh_export = exp;
|
||||
|
||||
switch (rqstp->rq_vers) {
|
||||
case 4:
|
||||
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
|
||||
fhp->fh_no_atomic_attr = true;
|
||||
break;
|
||||
case 3:
|
||||
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC)
|
||||
fhp->fh_no_wcc = true;
|
||||
break;
|
||||
case 2:
|
||||
fhp->fh_no_wcc = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
exp_put(exp);
|
||||
@ -327,7 +326,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
__be32
|
||||
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
|
||||
{
|
||||
struct svc_export *exp;
|
||||
struct svc_export *exp = NULL;
|
||||
struct dentry *dentry;
|
||||
__be32 error;
|
||||
|
||||
@ -400,7 +399,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
|
||||
}
|
||||
out:
|
||||
if (error == nfserr_stale)
|
||||
nfsdstats.fh_stale++;
|
||||
nfsd_stats_fh_stale_inc(exp);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -429,20 +428,6 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* for composing old style file handles
|
||||
*/
|
||||
static inline void _fh_update_old(struct dentry *dentry,
|
||||
struct svc_export *exp,
|
||||
struct knfsd_fh *fh)
|
||||
{
|
||||
fh->ofh_ino = ino_t_to_u32(d_inode(dentry)->i_ino);
|
||||
fh->ofh_generation = d_inode(dentry)->i_generation;
|
||||
if (d_is_dir(dentry) ||
|
||||
(exp->ex_flags & NFSEXP_NOSUBTREECHECK))
|
||||
fh->ofh_dirino = 0;
|
||||
}
|
||||
|
||||
static bool is_root_export(struct svc_export *exp)
|
||||
{
|
||||
return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root;
|
||||
@ -539,9 +524,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
/* ref_fh is a reference file handle.
|
||||
* if it is non-null and for the same filesystem, then we should compose
|
||||
* a filehandle which is of the same version, where possible.
|
||||
* Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
|
||||
* Then create a 32byte filehandle using nfs_fhbase_old
|
||||
*
|
||||
*/
|
||||
|
||||
struct inode * inode = d_inode(dentry);
|
||||
@ -559,10 +541,13 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
*/
|
||||
set_version_and_fsid_type(fhp, exp, ref_fh);
|
||||
|
||||
/* If we have a ref_fh, then copy the fh_no_wcc setting from it. */
|
||||
fhp->fh_no_wcc = ref_fh ? ref_fh->fh_no_wcc : false;
|
||||
|
||||
if (ref_fh == fhp)
|
||||
fh_put(ref_fh);
|
||||
|
||||
if (fhp->fh_locked || fhp->fh_dentry) {
|
||||
if (fhp->fh_dentry) {
|
||||
printk(KERN_ERR "fh_compose: fh %pd2 not initialized!\n",
|
||||
dentry);
|
||||
}
|
||||
@ -574,19 +559,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
fhp->fh_dentry = dget(dentry); /* our internal copy */
|
||||
fhp->fh_export = exp_get(exp);
|
||||
|
||||
if (fhp->fh_handle.fh_version == 0xca) {
|
||||
/* old style filehandle please */
|
||||
memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
|
||||
fhp->fh_handle.fh_size = NFS_FHSIZE;
|
||||
fhp->fh_handle.ofh_dcookie = 0xfeebbaca;
|
||||
fhp->fh_handle.ofh_dev = old_encode_dev(ex_dev);
|
||||
fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
|
||||
fhp->fh_handle.ofh_xino =
|
||||
ino_t_to_u32(d_inode(exp->ex_path.dentry)->i_ino);
|
||||
fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
|
||||
if (inode)
|
||||
_fh_update_old(dentry, exp, &fhp->fh_handle);
|
||||
} else {
|
||||
fhp->fh_handle.fh_size =
|
||||
key_len(fhp->fh_handle.fh_fsid_type) + 4;
|
||||
fhp->fh_handle.fh_auth_type = 0;
|
||||
@ -603,7 +575,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
||||
fh_put(fhp);
|
||||
return nfserr_opnotsupp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -623,16 +594,12 @@ fh_update(struct svc_fh *fhp)
|
||||
dentry = fhp->fh_dentry;
|
||||
if (d_really_is_negative(dentry))
|
||||
goto out_negative;
|
||||
if (fhp->fh_handle.fh_version != 1) {
|
||||
_fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
|
||||
} else {
|
||||
if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT)
|
||||
return 0;
|
||||
|
||||
_fh_update(fhp, fhp->fh_export, dentry);
|
||||
if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID)
|
||||
return nfserr_opnotsupp;
|
||||
}
|
||||
return 0;
|
||||
out_bad:
|
||||
printk(KERN_ERR "fh_update: fh not verified!\n");
|
||||
@ -643,6 +610,85 @@ fh_update(struct svc_fh *fhp)
|
||||
return nfserr_serverfault;
|
||||
}
|
||||
|
||||
/**
|
||||
* fh_fill_pre_attrs - Fill in pre-op attributes
|
||||
* @fhp: file handle to be updated
|
||||
*
|
||||
*/
|
||||
void fh_fill_pre_attrs(struct svc_fh *fhp)
|
||||
{
|
||||
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
|
||||
struct inode *inode;
|
||||
struct kstat stat;
|
||||
__be32 err;
|
||||
|
||||
if (fhp->fh_no_wcc || fhp->fh_pre_saved)
|
||||
return;
|
||||
|
||||
inode = d_inode(fhp->fh_dentry);
|
||||
err = fh_getattr(fhp, &stat);
|
||||
if (err) {
|
||||
/* Grab the times from inode anyway */
|
||||
stat.mtime = inode->i_mtime;
|
||||
stat.ctime = inode->i_ctime;
|
||||
stat.size = inode->i_size;
|
||||
}
|
||||
if (v4)
|
||||
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
|
||||
|
||||
fhp->fh_pre_mtime = stat.mtime;
|
||||
fhp->fh_pre_ctime = stat.ctime;
|
||||
fhp->fh_pre_size = stat.size;
|
||||
fhp->fh_pre_saved = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* fh_fill_post_attrs - Fill in post-op attributes
|
||||
* @fhp: file handle to be updated
|
||||
*
|
||||
*/
|
||||
void fh_fill_post_attrs(struct svc_fh *fhp)
|
||||
{
|
||||
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
|
||||
struct inode *inode = d_inode(fhp->fh_dentry);
|
||||
__be32 err;
|
||||
|
||||
if (fhp->fh_no_wcc)
|
||||
return;
|
||||
|
||||
if (fhp->fh_post_saved)
|
||||
printk("nfsd: inode locked twice during operation.\n");
|
||||
|
||||
err = fh_getattr(fhp, &fhp->fh_post_attr);
|
||||
if (err) {
|
||||
fhp->fh_post_saved = false;
|
||||
fhp->fh_post_attr.ctime = inode->i_ctime;
|
||||
} else
|
||||
fhp->fh_post_saved = true;
|
||||
if (v4)
|
||||
fhp->fh_post_change =
|
||||
nfsd4_change_attribute(&fhp->fh_post_attr, inode);
|
||||
}
|
||||
|
||||
/**
|
||||
* fh_fill_both_attrs - Fill pre-op and post-op attributes
|
||||
* @fhp: file handle to be updated
|
||||
*
|
||||
* This is used when the directory wasn't changed, but wcc attributes
|
||||
* are needed anyway.
|
||||
*/
|
||||
void fh_fill_both_attrs(struct svc_fh *fhp)
|
||||
{
|
||||
fh_fill_post_attrs(fhp);
|
||||
if (!fhp->fh_post_saved)
|
||||
return;
|
||||
fhp->fh_pre_change = fhp->fh_post_change;
|
||||
fhp->fh_pre_mtime = fhp->fh_post_attr.mtime;
|
||||
fhp->fh_pre_ctime = fhp->fh_post_attr.ctime;
|
||||
fhp->fh_pre_size = fhp->fh_post_attr.size;
|
||||
fhp->fh_pre_saved = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release a file handle.
|
||||
*/
|
||||
@ -652,16 +698,16 @@ fh_put(struct svc_fh *fhp)
|
||||
struct dentry * dentry = fhp->fh_dentry;
|
||||
struct svc_export * exp = fhp->fh_export;
|
||||
if (dentry) {
|
||||
fh_unlock(fhp);
|
||||
fhp->fh_dentry = NULL;
|
||||
dput(dentry);
|
||||
fh_clear_wcc(fhp);
|
||||
fh_clear_pre_post_attrs(fhp);
|
||||
}
|
||||
fh_drop_write(fhp);
|
||||
if (exp) {
|
||||
exp_put(exp);
|
||||
fhp->fh_export = NULL;
|
||||
}
|
||||
fhp->fh_no_wcc = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -671,20 +717,15 @@ fh_put(struct svc_fh *fhp)
|
||||
char * SVCFH_fmt(struct svc_fh *fhp)
|
||||
{
|
||||
struct knfsd_fh *fh = &fhp->fh_handle;
|
||||
static char buf[2+1+1+64*3+1];
|
||||
|
||||
static char buf[80];
|
||||
sprintf(buf, "%d: %08x %08x %08x %08x %08x %08x",
|
||||
fh->fh_size,
|
||||
fh->fh_base.fh_pad[0],
|
||||
fh->fh_base.fh_pad[1],
|
||||
fh->fh_base.fh_pad[2],
|
||||
fh->fh_base.fh_pad[3],
|
||||
fh->fh_base.fh_pad[4],
|
||||
fh->fh_base.fh_pad[5]);
|
||||
if (fh->fh_size < 0 || fh->fh_size> 64)
|
||||
return "bad-fh";
|
||||
sprintf(buf, "%d: %*ph", fh->fh_size, fh->fh_size, fh->fh_raw);
|
||||
return buf;
|
||||
}
|
||||
|
||||
enum fsid_source fsid_source(struct svc_fh *fhp)
|
||||
enum fsid_source fsid_source(const struct svc_fh *fhp)
|
||||
{
|
||||
if (fhp->fh_handle.fh_version != 1)
|
||||
return FSIDSOURCE_DEV;
|
||||
|
165
fs/nfsd/nfsfh.h
165
fs/nfsd/nfsfh.h
@ -10,8 +10,56 @@
|
||||
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include <uapi/linux/nfsd/nfsfh.h>
|
||||
#include <linux/iversion.h>
|
||||
#include <linux/exportfs.h>
|
||||
#include <linux/nfs4.h>
|
||||
|
||||
/*
|
||||
* The file handle starts with a sequence of four-byte words.
|
||||
* The first word contains a version number (1) and three descriptor bytes
|
||||
* that tell how the remaining 3 variable length fields should be handled.
|
||||
* These three bytes are auth_type, fsid_type and fileid_type.
|
||||
*
|
||||
* All four-byte values are in host-byte-order.
|
||||
*
|
||||
* The auth_type field is deprecated and must be set to 0.
|
||||
*
|
||||
* The fsid_type identifies how the filesystem (or export point) is
|
||||
* encoded.
|
||||
* Current values:
|
||||
* 0 - 4 byte device id (ms-2-bytes major, ls-2-bytes minor), 4byte inode number
|
||||
* NOTE: we cannot use the kdev_t device id value, because kdev_t.h
|
||||
* says we mustn't. We must break it up and reassemble.
|
||||
* 1 - 4 byte user specified identifier
|
||||
* 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED
|
||||
* 3 - 4 byte device id, encoded for user-space, 4 byte inode number
|
||||
* 4 - 4 byte inode number and 4 byte uuid
|
||||
* 5 - 8 byte uuid
|
||||
* 6 - 16 byte uuid
|
||||
* 7 - 8 byte inode number and 16 byte uuid
|
||||
*
|
||||
* The fileid_type identifies how the file within the filesystem is encoded.
|
||||
* The values for this field are filesystem specific, exccept that
|
||||
* filesystems must not use the values '0' or '0xff'. 'See enum fid_type'
|
||||
* in include/linux/exportfs.h for currently registered values.
|
||||
*/
|
||||
|
||||
struct knfsd_fh {
|
||||
unsigned int fh_size; /*
|
||||
* Points to the current size while
|
||||
* building a new file handle.
|
||||
*/
|
||||
union {
|
||||
char fh_raw[NFS4_FHSIZE];
|
||||
struct {
|
||||
u8 fh_version; /* == 1 */
|
||||
u8 fh_auth_type; /* deprecated */
|
||||
u8 fh_fsid_type;
|
||||
u8 fh_fileid_type;
|
||||
u32 fh_fsid[]; /* flexible-array member */
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static inline __u32 ino_t_to_u32(ino_t ino)
|
||||
{
|
||||
@ -33,14 +81,18 @@ typedef struct svc_fh {
|
||||
struct dentry * fh_dentry; /* validated dentry */
|
||||
struct svc_export * fh_export; /* export pointer */
|
||||
|
||||
bool fh_locked; /* inode locked by us */
|
||||
bool fh_want_write; /* remount protection taken */
|
||||
bool fh_no_wcc; /* no wcc data needed */
|
||||
bool fh_no_atomic_attr;
|
||||
/*
|
||||
* wcc data is not atomic with
|
||||
* operation
|
||||
*/
|
||||
int fh_flags; /* FH flags */
|
||||
#ifdef CONFIG_NFSD_V3
|
||||
bool fh_post_saved; /* post-op attrs saved */
|
||||
bool fh_pre_saved; /* pre-op attrs saved */
|
||||
|
||||
/* Pre-op attributes saved during fh_lock */
|
||||
/* Pre-op attributes saved when inode is locked */
|
||||
__u64 fh_pre_size; /* size before operation */
|
||||
struct timespec64 fh_pre_mtime; /* mtime before oper */
|
||||
struct timespec64 fh_pre_ctime; /* ctime before oper */
|
||||
@ -50,11 +102,9 @@ typedef struct svc_fh {
|
||||
*/
|
||||
u64 fh_pre_change;
|
||||
|
||||
/* Post-op attributes saved in fh_unlock */
|
||||
/* Post-op attributes saved in fh_fill_post_attrs() */
|
||||
struct kstat fh_post_attr; /* full attrs after operation */
|
||||
u64 fh_post_change; /* nfsv4 change; see above */
|
||||
#endif /* CONFIG_NFSD_V3 */
|
||||
|
||||
} svc_fh;
|
||||
#define NFSD4_FH_FOREIGN (1<<0)
|
||||
#define SET_FH_FLAG(c, f) ((c)->fh_flags |= (f))
|
||||
@ -76,7 +126,7 @@ enum fsid_source {
|
||||
FSIDSOURCE_FSID,
|
||||
FSIDSOURCE_UUID,
|
||||
};
|
||||
extern enum fsid_source fsid_source(struct svc_fh *fhp);
|
||||
extern enum fsid_source fsid_source(const struct svc_fh *fhp);
|
||||
|
||||
|
||||
/*
|
||||
@ -170,19 +220,19 @@ __be32 fh_update(struct svc_fh *);
|
||||
void fh_put(struct svc_fh *);
|
||||
|
||||
static __inline__ struct svc_fh *
|
||||
fh_copy(struct svc_fh *dst, struct svc_fh *src)
|
||||
fh_copy(struct svc_fh *dst, const struct svc_fh *src)
|
||||
{
|
||||
WARN_ON(src->fh_dentry || src->fh_locked);
|
||||
WARN_ON(src->fh_dentry);
|
||||
|
||||
*dst = *src;
|
||||
return dst;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fh_copy_shallow(struct knfsd_fh *dst, struct knfsd_fh *src)
|
||||
fh_copy_shallow(struct knfsd_fh *dst, const struct knfsd_fh *src)
|
||||
{
|
||||
dst->fh_size = src->fh_size;
|
||||
memcpy(&dst->fh_base, &src->fh_base, src->fh_size);
|
||||
memcpy(&dst->fh_raw, &src->fh_raw, src->fh_size);
|
||||
}
|
||||
|
||||
static __inline__ struct svc_fh *
|
||||
@ -193,16 +243,18 @@ fh_init(struct svc_fh *fhp, int maxsize)
|
||||
return fhp;
|
||||
}
|
||||
|
||||
static inline bool fh_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
|
||||
static inline bool fh_match(const struct knfsd_fh *fh1,
|
||||
const struct knfsd_fh *fh2)
|
||||
{
|
||||
if (fh1->fh_size != fh2->fh_size)
|
||||
return false;
|
||||
if (memcmp(fh1->fh_base.fh_pad, fh2->fh_base.fh_pad, fh1->fh_size) != 0)
|
||||
if (memcmp(fh1->fh_raw, fh2->fh_raw, fh1->fh_size) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool fh_fsid_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
|
||||
static inline bool fh_fsid_match(const struct knfsd_fh *fh1,
|
||||
const struct knfsd_fh *fh2)
|
||||
{
|
||||
if (fh1->fh_fsid_type != fh2->fh_fsid_type)
|
||||
return false;
|
||||
@ -219,27 +271,23 @@ static inline bool fh_fsid_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
|
||||
* returns a crc32 hash for the filehandle that is compatible with
|
||||
* the one displayed by "wireshark".
|
||||
*/
|
||||
|
||||
static inline u32
|
||||
knfsd_fh_hash(struct knfsd_fh *fh)
|
||||
static inline u32 knfsd_fh_hash(const struct knfsd_fh *fh)
|
||||
{
|
||||
return ~crc32_le(0xFFFFFFFF, (unsigned char *)&fh->fh_base, fh->fh_size);
|
||||
return ~crc32_le(0xFFFFFFFF, fh->fh_raw, fh->fh_size);
|
||||
}
|
||||
#else
|
||||
static inline u32
|
||||
knfsd_fh_hash(struct knfsd_fh *fh)
|
||||
static inline u32 knfsd_fh_hash(const struct knfsd_fh *fh)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFSD_V3
|
||||
/*
|
||||
* The wcc data stored in current_fh should be cleared
|
||||
* between compound ops.
|
||||
/**
|
||||
* fh_clear_pre_post_attrs - Reset pre/post attributes
|
||||
* @fhp: file handle to be updated
|
||||
*
|
||||
*/
|
||||
static inline void
|
||||
fh_clear_wcc(struct svc_fh *fhp)
|
||||
static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
|
||||
{
|
||||
fhp->fh_post_saved = false;
|
||||
fhp->fh_pre_saved = false;
|
||||
@ -259,6 +307,9 @@ fh_clear_wcc(struct svc_fh *fhp)
|
||||
static inline u64 nfsd4_change_attribute(struct kstat *stat,
|
||||
struct inode *inode)
|
||||
{
|
||||
if (inode->i_sb->s_export_op->fetch_iversion)
|
||||
return inode->i_sb->s_export_op->fetch_iversion(inode);
|
||||
else if (IS_I_VERSION(inode)) {
|
||||
u64 chattr;
|
||||
|
||||
chattr = stat->ctime.tv_sec;
|
||||
@ -266,61 +317,11 @@ static inline u64 nfsd4_change_attribute(struct kstat *stat,
|
||||
chattr += stat->ctime.tv_nsec;
|
||||
chattr += inode_query_iversion(inode);
|
||||
return chattr;
|
||||
} else
|
||||
return time_to_chattr(&stat->ctime);
|
||||
}
|
||||
|
||||
extern void fill_pre_wcc(struct svc_fh *fhp);
|
||||
extern void fill_post_wcc(struct svc_fh *fhp);
|
||||
#else
|
||||
#define fh_clear_wcc(ignored)
|
||||
#define fill_pre_wcc(ignored)
|
||||
#define fill_post_wcc(notused)
|
||||
#endif /* CONFIG_NFSD_V3 */
|
||||
|
||||
|
||||
/*
|
||||
* Lock a file handle/inode
|
||||
* NOTE: both fh_lock and fh_unlock are done "by hand" in
|
||||
* vfs.c:nfsd_rename as it needs to grab 2 i_mutex's at once
|
||||
* so, any changes here should be reflected there.
|
||||
*/
|
||||
|
||||
static inline void
|
||||
fh_lock_nested(struct svc_fh *fhp, unsigned int subclass)
|
||||
{
|
||||
struct dentry *dentry = fhp->fh_dentry;
|
||||
struct inode *inode;
|
||||
|
||||
BUG_ON(!dentry);
|
||||
|
||||
if (fhp->fh_locked) {
|
||||
printk(KERN_WARNING "fh_lock: %pd2 already locked!\n",
|
||||
dentry);
|
||||
return;
|
||||
}
|
||||
|
||||
inode = d_inode(dentry);
|
||||
inode_lock_nested(inode, subclass);
|
||||
fill_pre_wcc(fhp);
|
||||
fhp->fh_locked = true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fh_lock(struct svc_fh *fhp)
|
||||
{
|
||||
fh_lock_nested(fhp, I_MUTEX_NORMAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock a file handle/inode
|
||||
*/
|
||||
static inline void
|
||||
fh_unlock(struct svc_fh *fhp)
|
||||
{
|
||||
if (fhp->fh_locked) {
|
||||
fill_post_wcc(fhp);
|
||||
inode_unlock(d_inode(fhp->fh_dentry));
|
||||
fhp->fh_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
extern void fh_fill_pre_attrs(struct svc_fh *fhp);
|
||||
extern void fh_fill_post_attrs(struct svc_fh *fhp);
|
||||
extern void fh_fill_both_attrs(struct svc_fh *fhp);
|
||||
#endif /* _LINUX_NFSD_NFSFH_H */
|
||||
|
@ -51,6 +51,9 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
|
||||
struct nfsd_sattrargs *argp = rqstp->rq_argp;
|
||||
struct nfsd_attrstat *resp = rqstp->rq_resp;
|
||||
struct iattr *iap = &argp->attrs;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = iap,
|
||||
};
|
||||
struct svc_fh *fhp;
|
||||
|
||||
dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
|
||||
@ -100,7 +103,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
|
||||
}
|
||||
}
|
||||
|
||||
resp->status = nfsd_setattr(rqstp, fhp, iap, 0, (time64_t)0);
|
||||
resp->status = nfsd_setattr(rqstp, fhp, &attrs, 0, (time64_t)0);
|
||||
if (resp->status != nfs_ok)
|
||||
goto out;
|
||||
|
||||
@ -149,14 +152,16 @@ nfsd_proc_lookup(struct svc_rqst *rqstp)
|
||||
static __be32
|
||||
nfsd_proc_readlink(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd_readlinkargs *argp = rqstp->rq_argp;
|
||||
struct nfsd_fhandle *argp = rqstp->rq_argp;
|
||||
struct nfsd_readlinkres *resp = rqstp->rq_resp;
|
||||
|
||||
dprintk("nfsd: READLINK %s\n", SVCFH_fmt(&argp->fh));
|
||||
|
||||
/* Read the symlink. */
|
||||
resp->len = NFS_MAXPATHLEN;
|
||||
resp->status = nfsd_readlink(rqstp, &argp->fh, argp->buffer, &resp->len);
|
||||
resp->page = *(rqstp->rq_next_page++);
|
||||
resp->status = nfsd_readlink(rqstp, &argp->fh,
|
||||
page_address(resp->page), &resp->len);
|
||||
|
||||
fh_put(&argp->fh);
|
||||
return rpc_success;
|
||||
@ -171,36 +176,42 @@ nfsd_proc_read(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd_readargs *argp = rqstp->rq_argp;
|
||||
struct nfsd_readres *resp = rqstp->rq_resp;
|
||||
unsigned int len;
|
||||
u32 eof;
|
||||
int v;
|
||||
|
||||
dprintk("nfsd: READ %s %d bytes at %d\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
argp->count, argp->offset);
|
||||
|
||||
argp->count = min_t(u32, argp->count, NFSSVC_MAXBLKSIZE_V2);
|
||||
argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen);
|
||||
|
||||
v = 0;
|
||||
len = argp->count;
|
||||
resp->pages = rqstp->rq_next_page;
|
||||
while (len > 0) {
|
||||
struct page *page = *(rqstp->rq_next_page++);
|
||||
|
||||
rqstp->rq_vec[v].iov_base = page_address(page);
|
||||
rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
|
||||
len -= rqstp->rq_vec[v].iov_len;
|
||||
v++;
|
||||
}
|
||||
|
||||
/* Obtain buffer pointer for payload. 19 is 1 word for
|
||||
* status, 17 words for fattr, and 1 word for the byte count.
|
||||
*/
|
||||
|
||||
if (NFSSVC_MAXBLKSIZE_V2 < argp->count) {
|
||||
char buf[RPC_MAX_ADDRBUFLEN];
|
||||
printk(KERN_NOTICE
|
||||
"oversized read request from %s (%d bytes)\n",
|
||||
svc_print_addr(rqstp, buf, sizeof(buf)),
|
||||
argp->count);
|
||||
argp->count = NFSSVC_MAXBLKSIZE_V2;
|
||||
}
|
||||
svc_reserve_auth(rqstp, (19<<2) + argp->count + 4);
|
||||
|
||||
resp->count = argp->count;
|
||||
resp->status = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
|
||||
argp->offset,
|
||||
rqstp->rq_vec, argp->vlen,
|
||||
&resp->count,
|
||||
&eof);
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_read(rqstp, &resp->fh, argp->offset,
|
||||
rqstp->rq_vec, v, &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;
|
||||
set_bit(RQ_DROPME, &rqstp->rq_flags);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -227,12 +238,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
|
||||
SVCFH_fmt(&argp->fh),
|
||||
argp->len, argp->offset);
|
||||
|
||||
nvecs = svc_fill_write_vector(rqstp, rqstp->rq_arg.pages,
|
||||
&argp->first, cnt);
|
||||
if (!nvecs) {
|
||||
resp->status = nfserr_io;
|
||||
goto out;
|
||||
}
|
||||
nvecs = svc_fill_write_vector(rqstp, &argp->payload);
|
||||
|
||||
resp->status = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
|
||||
argp->offset, rqstp->rq_vec, nvecs,
|
||||
@ -240,8 +246,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
|
||||
if (resp->status == nfs_ok)
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
else if (resp->status == nfserr_jukebox)
|
||||
return rpc_drop_reply;
|
||||
out:
|
||||
set_bit(RQ_DROPME, &rqstp->rq_flags);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -259,6 +264,9 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
||||
svc_fh *dirfhp = &argp->fh;
|
||||
svc_fh *newfhp = &resp->fh;
|
||||
struct iattr *attr = &argp->attrs;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = attr,
|
||||
};
|
||||
struct inode *inode;
|
||||
struct dentry *dchild;
|
||||
int type, mode;
|
||||
@ -284,7 +292,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
||||
goto done;
|
||||
}
|
||||
|
||||
fh_lock_nested(dirfhp, I_MUTEX_PARENT);
|
||||
inode_lock_nested(dirfhp->fh_dentry->d_inode, I_MUTEX_PARENT);
|
||||
dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
|
||||
if (IS_ERR(dchild)) {
|
||||
resp->status = nfserrno(PTR_ERR(dchild));
|
||||
@ -383,9 +391,8 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
||||
resp->status = nfs_ok;
|
||||
if (!inode) {
|
||||
/* File doesn't exist. Create it and set attrs */
|
||||
resp->status = nfsd_create_locked(rqstp, dirfhp, argp->name,
|
||||
argp->len, attr, type, rdev,
|
||||
newfhp);
|
||||
resp->status = nfsd_create_locked(rqstp, dirfhp, &attrs, 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);
|
||||
@ -395,13 +402,12 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
||||
*/
|
||||
attr->ia_valid &= ATTR_SIZE;
|
||||
if (attr->ia_valid)
|
||||
resp->status = nfsd_setattr(rqstp, newfhp, attr, 0,
|
||||
resp->status = nfsd_setattr(rqstp, newfhp, &attrs, 0,
|
||||
(time64_t)0);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
/* We don't really need to unlock, as fh_put does it. */
|
||||
fh_unlock(dirfhp);
|
||||
inode_unlock(dirfhp->fh_dentry->d_inode);
|
||||
fh_drop_write(dirfhp);
|
||||
done:
|
||||
fh_put(dirfhp);
|
||||
@ -471,6 +477,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd_symlinkargs *argp = rqstp->rq_argp;
|
||||
struct nfsd_stat *resp = rqstp->rq_resp;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = &argp->attrs,
|
||||
};
|
||||
struct svc_fh newfh;
|
||||
|
||||
if (argp->tlen > NFS_MAXPATHLEN) {
|
||||
@ -492,7 +501,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
|
||||
|
||||
fh_init(&newfh, NFS_FHSIZE);
|
||||
resp->status = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
|
||||
argp->tname, &newfh);
|
||||
argp->tname, &attrs, &newfh);
|
||||
|
||||
kfree(argp->tname);
|
||||
fh_put(&argp->ffh);
|
||||
@ -510,6 +519,9 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd_createargs *argp = rqstp->rq_argp;
|
||||
struct nfsd_diropres *resp = rqstp->rq_resp;
|
||||
struct nfsd_attrs attrs = {
|
||||
.na_iattr = &argp->attrs,
|
||||
};
|
||||
|
||||
dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
|
||||
|
||||
@ -521,7 +533,7 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp)
|
||||
argp->attrs.ia_valid &= ~ATTR_SIZE;
|
||||
fh_init(&resp->fh, NFS_FHSIZE);
|
||||
resp->status = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
|
||||
&argp->attrs, S_IFDIR, 0, &resp->fh);
|
||||
&attrs, S_IFDIR, 0, &resp->fh);
|
||||
fh_put(&argp->fh);
|
||||
if (resp->status != nfs_ok)
|
||||
goto out;
|
||||
@ -548,6 +560,24 @@ nfsd_proc_rmdir(struct svc_rqst *rqstp)
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
static void nfsd_init_dirlist_pages(struct svc_rqst *rqstp,
|
||||
struct nfsd_readdirres *resp,
|
||||
u32 count)
|
||||
{
|
||||
struct xdr_buf *buf = &resp->dirlist;
|
||||
struct xdr_stream *xdr = &resp->xdr;
|
||||
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
|
||||
/* Reserve room for the NULL ptr & eof flag (-2 words) */
|
||||
buf->buflen = clamp(count, (u32)(XDR_UNIT * 2), (u32)PAGE_SIZE);
|
||||
buf->buflen -= XDR_UNIT * 2;
|
||||
buf->pages = rqstp->rq_next_page;
|
||||
rqstp->rq_next_page++;
|
||||
|
||||
xdr_init_encode_pages(xdr, buf, buf->pages, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a portion of a directory.
|
||||
*/
|
||||
@ -556,33 +586,20 @@ nfsd_proc_readdir(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd_readdirargs *argp = rqstp->rq_argp;
|
||||
struct nfsd_readdirres *resp = rqstp->rq_resp;
|
||||
int count;
|
||||
loff_t offset;
|
||||
|
||||
dprintk("nfsd: READDIR %s %d bytes at %d\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
argp->count, argp->cookie);
|
||||
|
||||
/* Shrink to the client read size */
|
||||
count = (argp->count >> 2) - 2;
|
||||
nfsd_init_dirlist_pages(rqstp, resp, argp->count);
|
||||
|
||||
/* Make sure we've room for the NULL ptr & eof flag */
|
||||
count -= 2;
|
||||
if (count < 0)
|
||||
count = 0;
|
||||
|
||||
resp->buffer = argp->buffer;
|
||||
resp->offset = NULL;
|
||||
resp->buflen = count;
|
||||
resp->common.err = nfs_ok;
|
||||
/* Read directory and encode entries on the fly */
|
||||
resp->cookie_offset = 0;
|
||||
offset = argp->cookie;
|
||||
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);
|
||||
nfssvc_encode_nfscookie(resp, offset);
|
||||
|
||||
fh_put(&argp->fh);
|
||||
return rpc_success;
|
||||
@ -609,7 +626,6 @@ nfsd_proc_statfs(struct svc_rqst *rqstp)
|
||||
* NFSv2 Server procedures.
|
||||
* Only the results of non-idempotent operations are cached.
|
||||
*/
|
||||
struct nfsd_void { int dummy; };
|
||||
|
||||
#define ST 1 /* status */
|
||||
#define FH 8 /* filehandle */
|
||||
@ -618,41 +634,49 @@ struct nfsd_void { int dummy; };
|
||||
static const struct svc_procedure nfsd_procedures2[18] = {
|
||||
[NFSPROC_NULL] = {
|
||||
.pc_func = nfsd_proc_null,
|
||||
.pc_decode = nfssvc_decode_void,
|
||||
.pc_encode = nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_void),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_decode = nfssvc_decode_voidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd_voidargs),
|
||||
.pc_argzero = sizeof(struct nfsd_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "NULL",
|
||||
},
|
||||
[NFSPROC_GETATTR] = {
|
||||
.pc_func = nfsd_proc_getattr,
|
||||
.pc_decode = nfssvc_decode_fhandle,
|
||||
.pc_encode = nfssvc_encode_attrstat,
|
||||
.pc_decode = nfssvc_decode_fhandleargs,
|
||||
.pc_encode = nfssvc_encode_attrstatres,
|
||||
.pc_release = nfssvc_release_attrstat,
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_argzero = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT,
|
||||
.pc_name = "GETATTR",
|
||||
},
|
||||
[NFSPROC_SETATTR] = {
|
||||
.pc_func = nfsd_proc_setattr,
|
||||
.pc_decode = nfssvc_decode_sattrargs,
|
||||
.pc_encode = nfssvc_encode_attrstat,
|
||||
.pc_encode = nfssvc_encode_attrstatres,
|
||||
.pc_release = nfssvc_release_attrstat,
|
||||
.pc_argsize = sizeof(struct nfsd_sattrargs),
|
||||
.pc_argzero = sizeof(struct nfsd_sattrargs),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+AT,
|
||||
.pc_name = "SETATTR",
|
||||
},
|
||||
[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_decode = nfssvc_decode_voidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd_voidargs),
|
||||
.pc_argzero = sizeof(struct nfsd_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "ROOT",
|
||||
},
|
||||
[NFSPROC_LOOKUP] = {
|
||||
.pc_func = nfsd_proc_lookup,
|
||||
@ -660,18 +684,22 @@ static const struct svc_procedure nfsd_procedures2[18] = {
|
||||
.pc_encode = nfssvc_encode_diropres,
|
||||
.pc_release = nfssvc_release_diropres,
|
||||
.pc_argsize = sizeof(struct nfsd_diropargs),
|
||||
.pc_argzero = sizeof(struct nfsd_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd_diropres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+FH+AT,
|
||||
.pc_name = "LOOKUP",
|
||||
},
|
||||
[NFSPROC_READLINK] = {
|
||||
.pc_func = nfsd_proc_readlink,
|
||||
.pc_decode = nfssvc_decode_readlinkargs,
|
||||
.pc_decode = nfssvc_decode_fhandleargs,
|
||||
.pc_encode = nfssvc_encode_readlinkres,
|
||||
.pc_argsize = sizeof(struct nfsd_readlinkargs),
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_argzero = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd_readlinkres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+1+NFS_MAXPATHLEN/4,
|
||||
.pc_name = "READLINK",
|
||||
},
|
||||
[NFSPROC_READ] = {
|
||||
.pc_func = nfsd_proc_read,
|
||||
@ -679,28 +707,34 @@ static const struct svc_procedure nfsd_procedures2[18] = {
|
||||
.pc_encode = nfssvc_encode_readres,
|
||||
.pc_release = nfssvc_release_readres,
|
||||
.pc_argsize = sizeof(struct nfsd_readargs),
|
||||
.pc_argzero = sizeof(struct nfsd_readargs),
|
||||
.pc_ressize = sizeof(struct nfsd_readres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4,
|
||||
.pc_name = "READ",
|
||||
},
|
||||
[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_decode = nfssvc_decode_voidarg,
|
||||
.pc_encode = nfssvc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd_voidargs),
|
||||
.pc_argzero = sizeof(struct nfsd_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = 0,
|
||||
.pc_name = "WRITECACHE",
|
||||
},
|
||||
[NFSPROC_WRITE] = {
|
||||
.pc_func = nfsd_proc_write,
|
||||
.pc_decode = nfssvc_decode_writeargs,
|
||||
.pc_encode = nfssvc_encode_attrstat,
|
||||
.pc_encode = nfssvc_encode_attrstatres,
|
||||
.pc_release = nfssvc_release_attrstat,
|
||||
.pc_argsize = sizeof(struct nfsd_writeargs),
|
||||
.pc_argzero = sizeof(struct nfsd_writeargs),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+AT,
|
||||
.pc_name = "WRITE",
|
||||
},
|
||||
[NFSPROC_CREATE] = {
|
||||
.pc_func = nfsd_proc_create,
|
||||
@ -708,45 +742,55 @@ static const struct svc_procedure nfsd_procedures2[18] = {
|
||||
.pc_encode = nfssvc_encode_diropres,
|
||||
.pc_release = nfssvc_release_diropres,
|
||||
.pc_argsize = sizeof(struct nfsd_createargs),
|
||||
.pc_argzero = sizeof(struct nfsd_createargs),
|
||||
.pc_ressize = sizeof(struct nfsd_diropres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+FH+AT,
|
||||
.pc_name = "CREATE",
|
||||
},
|
||||
[NFSPROC_REMOVE] = {
|
||||
.pc_func = nfsd_proc_remove,
|
||||
.pc_decode = nfssvc_decode_diropargs,
|
||||
.pc_encode = nfssvc_encode_stat,
|
||||
.pc_encode = nfssvc_encode_statres,
|
||||
.pc_argsize = sizeof(struct nfsd_diropargs),
|
||||
.pc_argzero = sizeof(struct nfsd_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd_stat),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "REMOVE",
|
||||
},
|
||||
[NFSPROC_RENAME] = {
|
||||
.pc_func = nfsd_proc_rename,
|
||||
.pc_decode = nfssvc_decode_renameargs,
|
||||
.pc_encode = nfssvc_encode_stat,
|
||||
.pc_encode = nfssvc_encode_statres,
|
||||
.pc_argsize = sizeof(struct nfsd_renameargs),
|
||||
.pc_argzero = sizeof(struct nfsd_renameargs),
|
||||
.pc_ressize = sizeof(struct nfsd_stat),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "RENAME",
|
||||
},
|
||||
[NFSPROC_LINK] = {
|
||||
.pc_func = nfsd_proc_link,
|
||||
.pc_decode = nfssvc_decode_linkargs,
|
||||
.pc_encode = nfssvc_encode_stat,
|
||||
.pc_encode = nfssvc_encode_statres,
|
||||
.pc_argsize = sizeof(struct nfsd_linkargs),
|
||||
.pc_argzero = sizeof(struct nfsd_linkargs),
|
||||
.pc_ressize = sizeof(struct nfsd_stat),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "LINK",
|
||||
},
|
||||
[NFSPROC_SYMLINK] = {
|
||||
.pc_func = nfsd_proc_symlink,
|
||||
.pc_decode = nfssvc_decode_symlinkargs,
|
||||
.pc_encode = nfssvc_encode_stat,
|
||||
.pc_encode = nfssvc_encode_statres,
|
||||
.pc_argsize = sizeof(struct nfsd_symlinkargs),
|
||||
.pc_argzero = sizeof(struct nfsd_symlinkargs),
|
||||
.pc_ressize = sizeof(struct nfsd_stat),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "SYMLINK",
|
||||
},
|
||||
[NFSPROC_MKDIR] = {
|
||||
.pc_func = nfsd_proc_mkdir,
|
||||
@ -754,35 +798,43 @@ static const struct svc_procedure nfsd_procedures2[18] = {
|
||||
.pc_encode = nfssvc_encode_diropres,
|
||||
.pc_release = nfssvc_release_diropres,
|
||||
.pc_argsize = sizeof(struct nfsd_createargs),
|
||||
.pc_argzero = sizeof(struct nfsd_createargs),
|
||||
.pc_ressize = sizeof(struct nfsd_diropres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+FH+AT,
|
||||
.pc_name = "MKDIR",
|
||||
},
|
||||
[NFSPROC_RMDIR] = {
|
||||
.pc_func = nfsd_proc_rmdir,
|
||||
.pc_decode = nfssvc_decode_diropargs,
|
||||
.pc_encode = nfssvc_encode_stat,
|
||||
.pc_encode = nfssvc_encode_statres,
|
||||
.pc_argsize = sizeof(struct nfsd_diropargs),
|
||||
.pc_argzero = sizeof(struct nfsd_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd_stat),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
.pc_name = "RMDIR",
|
||||
},
|
||||
[NFSPROC_READDIR] = {
|
||||
.pc_func = nfsd_proc_readdir,
|
||||
.pc_decode = nfssvc_decode_readdirargs,
|
||||
.pc_encode = nfssvc_encode_readdirres,
|
||||
.pc_argsize = sizeof(struct nfsd_readdirargs),
|
||||
.pc_argzero = sizeof(struct nfsd_readdirargs),
|
||||
.pc_ressize = sizeof(struct nfsd_readdirres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_name = "READDIR",
|
||||
},
|
||||
[NFSPROC_STATFS] = {
|
||||
.pc_func = nfsd_proc_statfs,
|
||||
.pc_decode = nfssvc_decode_fhandle,
|
||||
.pc_decode = nfssvc_decode_fhandleargs,
|
||||
.pc_encode = nfssvc_encode_statfsres,
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_argzero = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd_statfsres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+5,
|
||||
.pc_name = "STATFS",
|
||||
},
|
||||
};
|
||||
|
||||
@ -796,61 +848,3 @@ const struct svc_version nfsd_version2 = {
|
||||
.vs_dispatch = nfsd_dispatch,
|
||||
.vs_xdrsize = NFS2_SVC_XDRSIZE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Map errnos to NFS errnos.
|
||||
*/
|
||||
__be32
|
||||
nfserrno (int errno)
|
||||
{
|
||||
static struct {
|
||||
__be32 nfserr;
|
||||
int syserr;
|
||||
} nfs_errtbl[] = {
|
||||
{ nfs_ok, 0 },
|
||||
{ nfserr_perm, -EPERM },
|
||||
{ nfserr_noent, -ENOENT },
|
||||
{ nfserr_io, -EIO },
|
||||
{ nfserr_nxio, -ENXIO },
|
||||
{ nfserr_fbig, -E2BIG },
|
||||
{ nfserr_acces, -EACCES },
|
||||
{ nfserr_exist, -EEXIST },
|
||||
{ nfserr_xdev, -EXDEV },
|
||||
{ nfserr_mlink, -EMLINK },
|
||||
{ nfserr_nodev, -ENODEV },
|
||||
{ nfserr_notdir, -ENOTDIR },
|
||||
{ nfserr_isdir, -EISDIR },
|
||||
{ nfserr_inval, -EINVAL },
|
||||
{ nfserr_fbig, -EFBIG },
|
||||
{ nfserr_nospc, -ENOSPC },
|
||||
{ nfserr_rofs, -EROFS },
|
||||
{ nfserr_mlink, -EMLINK },
|
||||
{ nfserr_nametoolong, -ENAMETOOLONG },
|
||||
{ nfserr_notempty, -ENOTEMPTY },
|
||||
#ifdef EDQUOT
|
||||
{ nfserr_dquot, -EDQUOT },
|
||||
#endif
|
||||
{ nfserr_stale, -ESTALE },
|
||||
{ nfserr_jukebox, -ETIMEDOUT },
|
||||
{ nfserr_jukebox, -ERESTARTSYS },
|
||||
{ nfserr_jukebox, -EAGAIN },
|
||||
{ nfserr_jukebox, -EWOULDBLOCK },
|
||||
{ nfserr_jukebox, -ENOMEM },
|
||||
{ nfserr_io, -ETXTBSY },
|
||||
{ nfserr_notsupp, -EOPNOTSUPP },
|
||||
{ nfserr_toosmall, -ETOOSMALL },
|
||||
{ nfserr_serverfault, -ESERVERFAULT },
|
||||
{ nfserr_serverfault, -ENFILE },
|
||||
{ nfserr_io, -EUCLEAN },
|
||||
{ nfserr_perm, -ENOKEY },
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
|
||||
if (nfs_errtbl[i].syserr == errno)
|
||||
return nfs_errtbl[i].nfserr;
|
||||
}
|
||||
WARN_ONCE(1, "nfsd: non-standard errno: %d\n", errno);
|
||||
return nfserr_io;
|
||||
}
|
||||
|
||||
|
352
fs/nfsd/nfssvc.c
352
fs/nfsd/nfssvc.c
@ -12,6 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/siphash.h>
|
||||
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/sunrpc/svcsock.h>
|
||||
@ -29,13 +30,9 @@
|
||||
#include "netns.h"
|
||||
#include "filecache.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_SVC
|
||||
#include "trace.h"
|
||||
|
||||
bool inter_copy_offload_enable;
|
||||
EXPORT_SYMBOL_GPL(inter_copy_offload_enable);
|
||||
module_param(inter_copy_offload_enable, bool, 0644);
|
||||
MODULE_PARM_DESC(inter_copy_offload_enable,
|
||||
"Enable inter server to server copy offload. Default: false");
|
||||
#define NFSDDBG_FACILITY NFSDDBG_SVC
|
||||
|
||||
extern struct svc_program nfsd_program;
|
||||
static int nfsd(void *vrqstp);
|
||||
@ -59,18 +56,17 @@ static __be32 nfsd_init_request(struct svc_rqst *,
|
||||
struct svc_process_info *);
|
||||
|
||||
/*
|
||||
* nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members
|
||||
* of the svc_serv struct. In particular, ->sv_nrthreads but also to some
|
||||
* extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt
|
||||
* nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
|
||||
* of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
|
||||
*
|
||||
* If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a
|
||||
* properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number
|
||||
* of nfsd threads must exist and each must listed in ->sp_all_threads in each
|
||||
* entry of ->sv_pools[].
|
||||
* properly initialised 'struct svc_serv' with ->sv_nrthreads > 0 (unless
|
||||
* nn->keep_active is set). That number of nfsd threads must
|
||||
* exist and each must be listed in ->sp_all_threads in some entry of
|
||||
* ->sv_pools[].
|
||||
*
|
||||
* Transitions of the thread count between zero and non-zero are of particular
|
||||
* interest since the svc_serv needs to be created and initialized at that
|
||||
* point, or freed.
|
||||
* Each active thread holds a counted reference on nn->nfsd_serv, as does
|
||||
* the nn->keep_active flag and various transient calls to svc_get().
|
||||
*
|
||||
* Finally, the nfsd_mutex also protects some of the global variables that are
|
||||
* accessed when nfsd starts and that are settable via the write_* routines in
|
||||
@ -88,15 +84,19 @@ DEFINE_MUTEX(nfsd_mutex);
|
||||
* version 4.1 DRC caches.
|
||||
* nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
|
||||
*/
|
||||
spinlock_t nfsd_drc_lock;
|
||||
DEFINE_SPINLOCK(nfsd_drc_lock);
|
||||
unsigned long nfsd_drc_max_mem;
|
||||
unsigned long nfsd_drc_mem_used;
|
||||
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
static struct svc_stat nfsd_acl_svcstats;
|
||||
static const struct svc_version *nfsd_acl_version[] = {
|
||||
# if defined(CONFIG_NFSD_V2_ACL)
|
||||
[2] = &nfsd_acl_version2,
|
||||
# endif
|
||||
# if defined(CONFIG_NFSD_V3_ACL)
|
||||
[3] = &nfsd_acl_version3,
|
||||
# endif
|
||||
};
|
||||
|
||||
#define NFSD_ACL_MINVERS 2
|
||||
@ -120,10 +120,10 @@ static struct svc_stat nfsd_acl_svcstats = {
|
||||
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
|
||||
|
||||
static const struct svc_version *nfsd_version[] = {
|
||||
#if defined(CONFIG_NFSD_V2)
|
||||
[2] = &nfsd_version2,
|
||||
#if defined(CONFIG_NFSD_V3)
|
||||
[3] = &nfsd_version3,
|
||||
#endif
|
||||
[3] = &nfsd_version3,
|
||||
#if defined(CONFIG_NFSD_V4)
|
||||
[4] = &nfsd_version4,
|
||||
#endif
|
||||
@ -297,12 +297,12 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
|
||||
if (!list_empty(&nn->nfsd_serv->sv_permsocks))
|
||||
return 0;
|
||||
|
||||
error = svc_create_xprt(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
|
||||
error = svc_xprt_create(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
|
||||
SVC_SOCK_DEFAULTS, cred);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = svc_create_xprt(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
|
||||
error = svc_xprt_create(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
|
||||
SVC_SOCK_DEFAULTS, cred);
|
||||
if (error < 0)
|
||||
return error;
|
||||
@ -312,7 +312,7 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
|
||||
|
||||
static int nfsd_users = 0;
|
||||
|
||||
static int nfsd_startup_generic(int nrservs)
|
||||
static int nfsd_startup_generic(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -349,36 +349,60 @@ static bool nfsd_needs_lockd(struct nfsd_net *nn)
|
||||
return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST);
|
||||
}
|
||||
|
||||
void nfsd_copy_boot_verifier(__be32 verf[2], struct nfsd_net *nn)
|
||||
/**
|
||||
* nfsd_copy_write_verifier - Atomically copy a write verifier
|
||||
* @verf: buffer in which to receive the verifier cookie
|
||||
* @nn: NFS net namespace
|
||||
*
|
||||
* This function provides a wait-free mechanism for copying the
|
||||
* namespace's write verifier without tearing it.
|
||||
*/
|
||||
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn)
|
||||
{
|
||||
int seq = 0;
|
||||
|
||||
do {
|
||||
read_seqbegin_or_lock(&nn->boot_lock, &seq);
|
||||
read_seqbegin_or_lock(&nn->writeverf_lock, &seq);
|
||||
memcpy(verf, nn->writeverf, sizeof(nn->writeverf));
|
||||
} while (need_seqretry(&nn->writeverf_lock, seq));
|
||||
done_seqretry(&nn->writeverf_lock, seq);
|
||||
}
|
||||
|
||||
static void nfsd_reset_write_verifier_locked(struct nfsd_net *nn)
|
||||
{
|
||||
struct timespec64 now;
|
||||
u64 verf;
|
||||
|
||||
/*
|
||||
* This is opaque to client, so no need to byte-swap. Use
|
||||
* __force to keep sparse happy. y2038 time_t overflow is
|
||||
* irrelevant in this usage
|
||||
* Because the time value is hashed, y2038 time_t overflow
|
||||
* is irrelevant in this usage.
|
||||
*/
|
||||
verf[0] = (__force __be32)nn->nfssvc_boot.tv_sec;
|
||||
verf[1] = (__force __be32)nn->nfssvc_boot.tv_nsec;
|
||||
} while (need_seqretry(&nn->boot_lock, seq));
|
||||
done_seqretry(&nn->boot_lock, seq);
|
||||
ktime_get_raw_ts64(&now);
|
||||
verf = siphash_2u64(now.tv_sec, now.tv_nsec, &nn->siphash_key);
|
||||
memcpy(nn->writeverf, &verf, sizeof(nn->writeverf));
|
||||
}
|
||||
|
||||
static void nfsd_reset_boot_verifier_locked(struct nfsd_net *nn)
|
||||
/**
|
||||
* nfsd_reset_write_verifier - Generate a new write verifier
|
||||
* @nn: NFS net namespace
|
||||
*
|
||||
* This function updates the ->writeverf field of @nn. This field
|
||||
* contains an opaque cookie that, according to Section 18.32.3 of
|
||||
* RFC 8881, "the client can use to determine whether a server has
|
||||
* changed instance state (e.g., server restart) between a call to
|
||||
* WRITE and a subsequent call to either WRITE or COMMIT. This
|
||||
* cookie MUST be unchanged during a single instance of the NFSv4.1
|
||||
* server and MUST be unique between instances of the NFSv4.1
|
||||
* server."
|
||||
*/
|
||||
void nfsd_reset_write_verifier(struct nfsd_net *nn)
|
||||
{
|
||||
ktime_get_real_ts64(&nn->nfssvc_boot);
|
||||
write_seqlock(&nn->writeverf_lock);
|
||||
nfsd_reset_write_verifier_locked(nn);
|
||||
write_sequnlock(&nn->writeverf_lock);
|
||||
}
|
||||
|
||||
void nfsd_reset_boot_verifier(struct nfsd_net *nn)
|
||||
{
|
||||
write_seqlock(&nn->boot_lock);
|
||||
nfsd_reset_boot_verifier_locked(nn);
|
||||
write_sequnlock(&nn->boot_lock);
|
||||
}
|
||||
|
||||
static int nfsd_startup_net(int nrservs, struct net *net, const struct cred *cred)
|
||||
static int nfsd_startup_net(struct net *net, const struct cred *cred)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
int ret;
|
||||
@ -386,7 +410,7 @@ static int nfsd_startup_net(int nrservs, struct net *net, const struct cred *cre
|
||||
if (nn->nfsd_net_up)
|
||||
return 0;
|
||||
|
||||
ret = nfsd_startup_generic(nrservs);
|
||||
ret = nfsd_startup_generic();
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = nfsd_init_socks(net, cred);
|
||||
@ -407,6 +431,9 @@ static int nfsd_startup_net(int nrservs, struct net *net, const struct cred *cre
|
||||
if (ret)
|
||||
goto out_filecache;
|
||||
|
||||
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||
nfsd4_ssc_init_umount_work(nn);
|
||||
#endif
|
||||
nn->nfsd_net_up = true;
|
||||
return 0;
|
||||
|
||||
@ -436,6 +463,7 @@ static void nfsd_shutdown_net(struct net *net)
|
||||
nfsd_shutdown_generic();
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(nfsd_notifier_lock);
|
||||
static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
@ -445,18 +473,17 @@ static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct sockaddr_in sin;
|
||||
|
||||
if ((event != NETDEV_DOWN) ||
|
||||
!atomic_inc_not_zero(&nn->ntf_refcnt))
|
||||
if (event != NETDEV_DOWN || !nn->nfsd_serv)
|
||||
goto out;
|
||||
|
||||
spin_lock(&nfsd_notifier_lock);
|
||||
if (nn->nfsd_serv) {
|
||||
dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = ifa->ifa_local;
|
||||
svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin);
|
||||
}
|
||||
atomic_dec(&nn->ntf_refcnt);
|
||||
wake_up(&nn->ntf_wq);
|
||||
spin_unlock(&nfsd_notifier_lock);
|
||||
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
@ -476,10 +503,10 @@ static int nfsd_inet6addr_event(struct notifier_block *this,
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct sockaddr_in6 sin6;
|
||||
|
||||
if ((event != NETDEV_DOWN) ||
|
||||
!atomic_inc_not_zero(&nn->ntf_refcnt))
|
||||
if (event != NETDEV_DOWN || !nn->nfsd_serv)
|
||||
goto out;
|
||||
|
||||
spin_lock(&nfsd_notifier_lock);
|
||||
if (nn->nfsd_serv) {
|
||||
dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr);
|
||||
sin6.sin6_family = AF_INET6;
|
||||
@ -488,8 +515,8 @@ static int nfsd_inet6addr_event(struct notifier_block *this,
|
||||
sin6.sin6_scope_id = ifa->idev->dev->ifindex;
|
||||
svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6);
|
||||
}
|
||||
atomic_dec(&nn->ntf_refcnt);
|
||||
wake_up(&nn->ntf_wq);
|
||||
spin_unlock(&nfsd_notifier_lock);
|
||||
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
@ -502,11 +529,15 @@ static struct notifier_block nfsd_inet6addr_notifier = {
|
||||
/* Only used under nfsd_mutex, so this atomic may be overkill: */
|
||||
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
|
||||
|
||||
static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
|
||||
void nfsd_last_thread(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv = nn->nfsd_serv;
|
||||
|
||||
spin_lock(&nfsd_notifier_lock);
|
||||
nn->nfsd_serv = NULL;
|
||||
spin_unlock(&nfsd_notifier_lock);
|
||||
|
||||
atomic_dec(&nn->ntf_refcnt);
|
||||
/* check if the notifier still has clients */
|
||||
if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
|
||||
unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
|
||||
@ -514,7 +545,8 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
|
||||
unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
|
||||
#endif
|
||||
}
|
||||
wait_event(nn->ntf_wq, atomic_read(&nn->ntf_refcnt) == 0);
|
||||
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
|
||||
/*
|
||||
* write_ports can create the server without actually starting
|
||||
@ -567,7 +599,6 @@ static void set_max_drc(void)
|
||||
nfsd_drc_max_mem = (nr_free_buffer_pages()
|
||||
>> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
|
||||
nfsd_drc_mem_used = 0;
|
||||
spin_lock_init(&nfsd_drc_lock);
|
||||
dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem);
|
||||
}
|
||||
|
||||
@ -592,24 +623,6 @@ static int nfsd_get_default_max_blksize(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct svc_serv_ops nfsd_thread_sv_ops = {
|
||||
.svo_shutdown = nfsd_last_thread,
|
||||
.svo_function = nfsd,
|
||||
.svo_enqueue_xprt = svc_xprt_do_enqueue,
|
||||
.svo_setup = svc_set_num_threads,
|
||||
.svo_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void nfsd_complete_shutdown(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
|
||||
WARN_ON(!mutex_is_locked(&nfsd_mutex));
|
||||
|
||||
nn->nfsd_serv = NULL;
|
||||
complete(&nn->nfsd_shutdown_complete);
|
||||
}
|
||||
|
||||
void nfsd_shutdown_threads(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
@ -624,11 +637,10 @@ void nfsd_shutdown_threads(struct net *net)
|
||||
|
||||
svc_get(serv);
|
||||
/* Kill outstanding nfsd threads */
|
||||
serv->sv_ops->svo_setup(serv, NULL, 0);
|
||||
nfsd_destroy(net);
|
||||
svc_set_num_threads(serv, NULL, 0);
|
||||
nfsd_last_thread(net);
|
||||
svc_put(serv);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
/* Wait for shutdown of nfsd_serv to complete */
|
||||
wait_for_completion(&nn->nfsd_shutdown_complete);
|
||||
}
|
||||
|
||||
bool i_am_nfsd(void)
|
||||
@ -640,6 +652,7 @@ int nfsd_create_serv(struct net *net)
|
||||
{
|
||||
int error;
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&nfsd_mutex));
|
||||
if (nn->nfsd_serv) {
|
||||
@ -649,19 +662,19 @@ int nfsd_create_serv(struct net *net)
|
||||
if (nfsd_max_blksize == 0)
|
||||
nfsd_max_blksize = nfsd_get_default_max_blksize();
|
||||
nfsd_reset_versions(nn);
|
||||
nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
|
||||
&nfsd_thread_sv_ops);
|
||||
if (nn->nfsd_serv == NULL)
|
||||
serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, nfsd);
|
||||
if (serv == NULL)
|
||||
return -ENOMEM;
|
||||
init_completion(&nn->nfsd_shutdown_complete);
|
||||
|
||||
nn->nfsd_serv->sv_maxconn = nn->max_connections;
|
||||
error = svc_bind(nn->nfsd_serv, net);
|
||||
serv->sv_maxconn = nn->max_connections;
|
||||
error = svc_bind(serv, net);
|
||||
if (error < 0) {
|
||||
svc_destroy(nn->nfsd_serv);
|
||||
nfsd_complete_shutdown(net);
|
||||
svc_put(serv);
|
||||
return error;
|
||||
}
|
||||
spin_lock(&nfsd_notifier_lock);
|
||||
nn->nfsd_serv = serv;
|
||||
spin_unlock(&nfsd_notifier_lock);
|
||||
|
||||
set_max_drc();
|
||||
/* check if the notifier is already set */
|
||||
@ -671,8 +684,7 @@ int nfsd_create_serv(struct net *net)
|
||||
register_inet6addr_notifier(&nfsd_inet6addr_notifier);
|
||||
#endif
|
||||
}
|
||||
atomic_inc(&nn->ntf_refcnt);
|
||||
nfsd_reset_boot_verifier(nn);
|
||||
nfsd_reset_write_verifier(nn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -699,18 +711,6 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nfsd_destroy(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
int destroy = (nn->nfsd_serv->sv_nrthreads == 1);
|
||||
|
||||
if (destroy)
|
||||
svc_shutdown_net(nn->nfsd_serv, net);
|
||||
svc_destroy(nn->nfsd_serv);
|
||||
if (destroy)
|
||||
nfsd_complete_shutdown(net);
|
||||
}
|
||||
|
||||
int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
|
||||
{
|
||||
int i = 0;
|
||||
@ -755,12 +755,13 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
|
||||
/* apply the new numbers */
|
||||
svc_get(nn->nfsd_serv);
|
||||
for (i = 0; i < n; i++) {
|
||||
err = nn->nfsd_serv->sv_ops->svo_setup(nn->nfsd_serv,
|
||||
&nn->nfsd_serv->sv_pools[i], nthreads[i]);
|
||||
err = svc_set_num_threads(nn->nfsd_serv,
|
||||
&nn->nfsd_serv->sv_pools[i],
|
||||
nthreads[i]);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
nfsd_destroy(net);
|
||||
svc_put(nn->nfsd_serv);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -775,6 +776,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
|
||||
int error;
|
||||
bool nfsd_up_before;
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
dprintk("nfsd: creating service\n");
|
||||
@ -786,7 +788,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
|
||||
if (nrservs == 0 && nn->nfsd_serv == NULL)
|
||||
goto out;
|
||||
|
||||
strlcpy(nn->nfsd_name, utsname()->nodename,
|
||||
strscpy(nn->nfsd_name, utsname()->nodename,
|
||||
sizeof(nn->nfsd_name));
|
||||
|
||||
error = nfsd_create_serv(net);
|
||||
@ -794,24 +796,25 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
|
||||
goto out;
|
||||
|
||||
nfsd_up_before = nn->nfsd_net_up;
|
||||
serv = nn->nfsd_serv;
|
||||
|
||||
error = nfsd_startup_net(nrservs, net, cred);
|
||||
error = nfsd_startup_net(net, cred);
|
||||
if (error)
|
||||
goto out_destroy;
|
||||
error = nn->nfsd_serv->sv_ops->svo_setup(nn->nfsd_serv,
|
||||
NULL, nrservs);
|
||||
goto out_put;
|
||||
error = svc_set_num_threads(serv, NULL, nrservs);
|
||||
if (error)
|
||||
goto out_shutdown;
|
||||
/* We are holding a reference to nn->nfsd_serv which
|
||||
* we don't want to count in the return value,
|
||||
* so subtract 1
|
||||
*/
|
||||
error = nn->nfsd_serv->sv_nrthreads - 1;
|
||||
error = serv->sv_nrthreads;
|
||||
if (error == 0)
|
||||
nfsd_last_thread(net);
|
||||
out_shutdown:
|
||||
if (error < 0 && !nfsd_up_before)
|
||||
nfsd_shutdown_net(net);
|
||||
out_destroy:
|
||||
nfsd_destroy(net); /* Release server */
|
||||
out_put:
|
||||
/* Threads now hold service active */
|
||||
if (xchg(&nn->keep_active, 0))
|
||||
svc_put(serv);
|
||||
svc_put(serv);
|
||||
out:
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return error;
|
||||
@ -925,9 +928,6 @@ nfsd(void *vrqstp)
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
int err;
|
||||
|
||||
/* Lock module and set up kernel thread */
|
||||
mutex_lock(&nfsd_mutex);
|
||||
|
||||
/* At this point, the thread shares current->fs
|
||||
* with the init process. We need to create files with the
|
||||
* umask as defined by the client instead of init's umask. */
|
||||
@ -938,17 +938,7 @@ nfsd(void *vrqstp)
|
||||
|
||||
current->fs->umask = 0;
|
||||
|
||||
/*
|
||||
* thread is spawned with all signals set to SIG_IGN, re-enable
|
||||
* the ones that will bring down the thread
|
||||
*/
|
||||
allow_signal(SIGKILL);
|
||||
allow_signal(SIGHUP);
|
||||
allow_signal(SIGINT);
|
||||
allow_signal(SIGQUIT);
|
||||
|
||||
nfsdstats.th_cnt++;
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
atomic_inc(&nfsdstats.th_cnt);
|
||||
|
||||
set_freezable();
|
||||
|
||||
@ -972,57 +962,14 @@ nfsd(void *vrqstp)
|
||||
validate_process_creds();
|
||||
}
|
||||
|
||||
/* Clear signals before calling svc_exit_thread() */
|
||||
flush_signals(current);
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
nfsdstats.th_cnt --;
|
||||
atomic_dec(&nfsdstats.th_cnt);
|
||||
|
||||
out:
|
||||
rqstp->rq_server = NULL;
|
||||
|
||||
/* Release the thread */
|
||||
svc_exit_thread(rqstp);
|
||||
|
||||
nfsd_destroy(net);
|
||||
|
||||
/* Release module */
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
module_put_and_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A write procedure can have a large argument, and a read procedure can
|
||||
* have a large reply, but no NFSv2 or NFSv3 procedure has argument and
|
||||
* reply that can both be larger than a page. The xdr code has taken
|
||||
* advantage of this assumption to be a sloppy about bounds checking in
|
||||
* some cases. Pending a rewrite of the NFSv2/v3 xdr code to fix that
|
||||
* problem, we enforce these assumptions here:
|
||||
*/
|
||||
static bool nfs_request_too_big(struct svc_rqst *rqstp,
|
||||
const struct svc_procedure *proc)
|
||||
{
|
||||
/*
|
||||
* The ACL code has more careful bounds-checking and is not
|
||||
* susceptible to this problem:
|
||||
*/
|
||||
if (rqstp->rq_prog != NFS_PROGRAM)
|
||||
return false;
|
||||
/*
|
||||
* Ditto NFSv4 (which can in theory have argument and reply both
|
||||
* more than a page):
|
||||
*/
|
||||
if (rqstp->rq_vers >= 4)
|
||||
return false;
|
||||
/* The reply will be small, we're OK: */
|
||||
if (proc->pc_xdrressize > 0 &&
|
||||
proc->pc_xdrressize < XDR_QUADLEN(PAGE_SIZE))
|
||||
return false;
|
||||
|
||||
return rqstp->rq_arg.len > PAGE_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_dispatch - Process an NFS or NFSACL Request
|
||||
* @rqstp: incoming request
|
||||
@ -1037,22 +984,15 @@ static bool nfs_request_too_big(struct svc_rqst *rqstp,
|
||||
int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
||||
{
|
||||
const struct svc_procedure *proc = rqstp->rq_procinfo;
|
||||
struct kvec *argv = &rqstp->rq_arg.head[0];
|
||||
struct kvec *resv = &rqstp->rq_res.head[0];
|
||||
__be32 *p;
|
||||
|
||||
dprintk("nfsd_dispatch: vers %d proc %d\n",
|
||||
rqstp->rq_vers, rqstp->rq_proc);
|
||||
|
||||
if (nfs_request_too_big(rqstp, proc))
|
||||
goto out_too_large;
|
||||
|
||||
/*
|
||||
* Give the xdr decoder a chance to change this if it wants
|
||||
* (necessary in the NFSv4.0 compound case)
|
||||
*/
|
||||
rqstp->rq_cachetype = proc->pc_cachetype;
|
||||
if (!proc->pc_decode(rqstp, argv->iov_base))
|
||||
|
||||
svcxdr_init_decode(rqstp);
|
||||
if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
|
||||
goto out_decode_err;
|
||||
|
||||
switch (nfsd_cache_lookup(rqstp)) {
|
||||
@ -1068,43 +1008,64 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
||||
* Need to grab the location to store the status, as
|
||||
* NFSv4 does some encoding while processing
|
||||
*/
|
||||
p = resv->iov_base + resv->iov_len;
|
||||
resv->iov_len += sizeof(__be32);
|
||||
svcxdr_init_encode(rqstp);
|
||||
|
||||
*statp = proc->pc_func(rqstp);
|
||||
if (*statp == rpc_drop_reply || test_bit(RQ_DROPME, &rqstp->rq_flags))
|
||||
if (test_bit(RQ_DROPME, &rqstp->rq_flags))
|
||||
goto out_update_drop;
|
||||
|
||||
if (!proc->pc_encode(rqstp, p))
|
||||
if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
|
||||
goto out_encode_err;
|
||||
|
||||
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");
|
||||
trace_nfsd_garbage_args_err(rqstp);
|
||||
*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");
|
||||
trace_nfsd_cant_encode_err(rqstp);
|
||||
nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
|
||||
*statp = rpc_system_err;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfssvc_decode_voidarg - Decode void arguments
|
||||
* @rqstp: Server RPC transaction context
|
||||
* @xdr: XDR stream positioned at arguments to decode
|
||||
*
|
||||
* Return values:
|
||||
* %false: Arguments were not valid
|
||||
* %true: Decoding was successful
|
||||
*/
|
||||
bool nfssvc_decode_voidarg(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfssvc_encode_voidres - Encode void results
|
||||
* @rqstp: Server RPC transaction context
|
||||
* @xdr: XDR stream into which to encode results
|
||||
*
|
||||
* Return values:
|
||||
* %false: Local error while encoding
|
||||
* %true: Encoding was successful
|
||||
*/
|
||||
bool nfssvc_encode_voidres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret;
|
||||
@ -1115,7 +1076,6 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
/* bump up the psudo refcount while traversing */
|
||||
svc_get(nn->nfsd_serv);
|
||||
ret = svc_pool_stats_open(nn->nfsd_serv, file);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
@ -1124,12 +1084,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
||||
|
||||
int nfsd_pool_stats_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct svc_serv *serv = seq->private;
|
||||
int ret = seq_release(inode, file);
|
||||
struct net *net = inode->i_sb->s_fs_info;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
/* this function really, really should have been called svc_put() */
|
||||
nfsd_destroy(net);
|
||||
svc_put(serv);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
834
fs/nfsd/nfsxdr.c
834
fs/nfsd/nfsxdr.c
File diff suppressed because it is too large
Load Diff
@ -57,11 +57,11 @@ typedef struct {
|
||||
} stateid_t;
|
||||
|
||||
typedef struct {
|
||||
stateid_t stid;
|
||||
stateid_t cs_stid;
|
||||
#define NFS4_COPY_STID 1
|
||||
#define NFS4_COPYNOTIFY_STID 2
|
||||
unsigned char sc_type;
|
||||
refcount_t sc_count;
|
||||
unsigned char cs_type;
|
||||
refcount_t cs_count;
|
||||
} copy_stateid_t;
|
||||
|
||||
struct nfsd4_callback {
|
||||
@ -149,6 +149,7 @@ struct nfs4_delegation {
|
||||
/* For recall: */
|
||||
int dl_retries;
|
||||
struct nfsd4_callback dl_recall;
|
||||
bool dl_recalled;
|
||||
};
|
||||
|
||||
#define cb_to_delegation(cb) \
|
||||
@ -174,7 +175,7 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
|
||||
/* Maximum number of slots per session. 160 is useful for long haul TCP */
|
||||
#define NFSD_MAX_SLOTS_PER_SESSION 160
|
||||
/* Maximum number of operations per session compound */
|
||||
#define NFSD_MAX_OPS_PER_COMPOUND 16
|
||||
#define NFSD_MAX_OPS_PER_COMPOUND 50
|
||||
/* Maximum session per slot cache size */
|
||||
#define NFSD_SLOT_CACHE_SIZE 2048
|
||||
/* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */
|
||||
@ -282,6 +283,28 @@ struct nfsd4_sessionid {
|
||||
|
||||
#define HEXDIR_LEN 33 /* hex version of 16 byte md5 of cl_name plus '\0' */
|
||||
|
||||
/*
|
||||
* State Meaning Where set
|
||||
* --------------------------------------------------------------------------
|
||||
* | NFSD4_ACTIVE | Confirmed, active | Default |
|
||||
* |------------------- ----------------------------------------------------|
|
||||
* | NFSD4_COURTESY | Courtesy state. | nfs4_get_client_reaplist |
|
||||
* | | Lease/lock/share | |
|
||||
* | | reservation conflict | |
|
||||
* | | can cause Courtesy | |
|
||||
* | | client to be expired | |
|
||||
* |------------------------------------------------------------------------|
|
||||
* | NFSD4_EXPIRABLE | Courtesy client to be| nfs4_laundromat |
|
||||
* | | expired by Laundromat| try_to_expire_client |
|
||||
* | | due to conflict | |
|
||||
* |------------------------------------------------------------------------|
|
||||
*/
|
||||
enum {
|
||||
NFSD4_ACTIVE = 0,
|
||||
NFSD4_COURTESY,
|
||||
NFSD4_EXPIRABLE,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct nfs4_client - one per client. Clientids live here.
|
||||
*
|
||||
@ -345,6 +368,7 @@ struct nfs4_client {
|
||||
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
|
||||
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
|
||||
1 << NFSD4_CLIENT_CB_KILL)
|
||||
#define NFSD4_CLIENT_CB_RECALL_ANY (6)
|
||||
unsigned long cl_flags;
|
||||
const struct cred *cl_cb_cred;
|
||||
struct rpc_clnt *cl_cb_client;
|
||||
@ -371,6 +395,10 @@ struct nfs4_client {
|
||||
|
||||
/* debugging info directory under nfsd/clients/ : */
|
||||
struct dentry *cl_nfsd_dentry;
|
||||
/* 'info' file within that directory. Ref is not counted,
|
||||
* but will remain valid iff cl_nfsd_dentry != NULL
|
||||
*/
|
||||
struct dentry *cl_nfsd_info_dentry;
|
||||
|
||||
/* for nfs41 callbacks */
|
||||
/* We currently support a single back channel with a single slot */
|
||||
@ -381,6 +409,13 @@ struct nfs4_client {
|
||||
struct list_head async_copies; /* list of async copies */
|
||||
spinlock_t async_lock; /* lock for async copies */
|
||||
atomic_t cl_cb_inflight; /* Outstanding callbacks */
|
||||
|
||||
unsigned int cl_state;
|
||||
atomic_t cl_delegs_in_recall;
|
||||
|
||||
struct nfsd4_cb_recall_any *cl_ra;
|
||||
time64_t cl_ra_time;
|
||||
struct list_head cl_ra_cblist;
|
||||
};
|
||||
|
||||
/* struct nfs4_client_reset
|
||||
@ -506,14 +541,13 @@ struct nfs4_clnt_odstate {
|
||||
* inode can have multiple filehandles associated with it, so there is
|
||||
* (potentially) a many to one relationship between this struct and struct
|
||||
* inode.
|
||||
*
|
||||
* These are hashed by filehandle in the file_hashtbl, which is protected by
|
||||
* the global state_lock spinlock.
|
||||
*/
|
||||
struct nfs4_file {
|
||||
refcount_t fi_ref;
|
||||
struct inode * fi_inode;
|
||||
bool fi_aliased;
|
||||
spinlock_t fi_lock;
|
||||
struct hlist_node fi_hash; /* hash on fi_fhandle */
|
||||
struct rhlist_head fi_rlist;
|
||||
struct list_head fi_stateids;
|
||||
union {
|
||||
struct list_head fi_delegations;
|
||||
@ -562,6 +596,10 @@ struct nfs4_ol_stateid {
|
||||
struct list_head st_locks;
|
||||
struct nfs4_stateowner *st_stateowner;
|
||||
struct nfs4_clnt_odstate *st_clnt_odstate;
|
||||
/*
|
||||
* These bitmasks use 3 separate bits for READ, ALLOW, and BOTH; see the
|
||||
* comment above bmap_to_share_mode() for explanation:
|
||||
*/
|
||||
unsigned char st_access_bmap;
|
||||
unsigned char st_deny_bmap;
|
||||
struct nfs4_ol_stateid *st_openstp;
|
||||
@ -603,6 +641,7 @@ enum nfsd4_cb_op {
|
||||
NFSPROC4_CLNT_CB_OFFLOAD,
|
||||
NFSPROC4_CLNT_CB_SEQUENCE,
|
||||
NFSPROC4_CLNT_CB_NOTIFY_LOCK,
|
||||
NFSPROC4_CLNT_CB_RECALL_ANY,
|
||||
};
|
||||
|
||||
/* Returns true iff a is later than b: */
|
||||
@ -623,6 +662,7 @@ struct nfsd4_blocked_lock {
|
||||
struct file_lock nbl_lock;
|
||||
struct knfsd_fh nbl_fh;
|
||||
struct nfsd4_callback nbl_cb;
|
||||
struct kref nbl_kref;
|
||||
};
|
||||
|
||||
struct nfsd4_compound_state;
|
||||
@ -649,26 +689,22 @@ void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *)
|
||||
extern void nfs4_release_reclaim(struct nfsd_net *);
|
||||
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct xdr_netobj name,
|
||||
struct nfsd_net *nn);
|
||||
extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
|
||||
struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
|
||||
extern __be32 nfs4_check_open_reclaim(struct nfs4_client *);
|
||||
extern void nfsd4_probe_callback(struct nfs4_client *clp);
|
||||
extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
|
||||
extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
|
||||
extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
|
||||
const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op);
|
||||
extern void nfsd4_run_cb(struct nfsd4_callback *cb);
|
||||
extern bool nfsd4_run_cb(struct nfsd4_callback *cb);
|
||||
extern int nfsd4_create_callback_queue(void);
|
||||
extern void nfsd4_destroy_callback_queue(void);
|
||||
extern void nfsd4_shutdown_callback(struct nfs4_client *);
|
||||
extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
|
||||
extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
|
||||
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
|
||||
struct xdr_netobj princhash, struct nfsd_net *nn);
|
||||
extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
|
||||
|
||||
struct nfs4_file *find_file(struct knfsd_fh *fh);
|
||||
void put_nfs4_file(struct nfs4_file *fi);
|
||||
extern void nfs4_put_copy(struct nfsd4_copy *copy);
|
||||
extern struct nfsd4_copy *
|
||||
find_async_copy(struct nfs4_client *clp, stateid_t *staetid);
|
||||
extern void nfs4_put_cpntf_state(struct nfsd_net *nn,
|
||||
@ -693,4 +729,9 @@ 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);
|
||||
|
||||
static inline bool try_to_expire_client(struct nfs4_client *clp)
|
||||
{
|
||||
cmpxchg(&clp->cl_state, NFSD4_COURTESY, NFSD4_EXPIRABLE);
|
||||
return clp->cl_state == NFSD4_EXPIRABLE;
|
||||
}
|
||||
#endif /* NFSD4_STATE_H */
|
||||
|
124
fs/nfsd/stats.c
124
fs/nfsd/stats.c
@ -7,16 +7,14 @@
|
||||
* Format:
|
||||
* rc <hits> <misses> <nocache>
|
||||
* Statistsics for the reply cache
|
||||
* fh <stale> <total-lookups> <anonlookups> <dir-not-in-dcache> <nondir-not-in-dcache>
|
||||
* fh <stale> <deprecated filehandle cache stats>
|
||||
* statistics for filehandle lookup
|
||||
* io <bytes-read> <bytes-written>
|
||||
* statistics for IO throughput
|
||||
* th <threads> <fullcnt> <10%-20%> <20%-30%> ... <90%-100%> <100%>
|
||||
* time (seconds) when nfsd thread usage above thresholds
|
||||
* and number of times that all threads were in use
|
||||
* ra cache-size <10% <20% <30% ... <100% not-found
|
||||
* number of times that read-ahead entry was found that deep in
|
||||
* the cache.
|
||||
* th <threads> <deprecated thread usage histogram stats>
|
||||
* number of threads
|
||||
* ra <deprecated ra-cache stats>
|
||||
*
|
||||
* plus generic RPC stats (see net/sunrpc/stats.c)
|
||||
*
|
||||
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
|
||||
@ -34,34 +32,27 @@ struct svc_stat nfsd_svcstats = {
|
||||
.program = &nfsd_program,
|
||||
};
|
||||
|
||||
static int nfsd_proc_show(struct seq_file *seq, void *v)
|
||||
static int nfsd_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
int i;
|
||||
|
||||
seq_printf(seq, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n",
|
||||
nfsdstats.rchits,
|
||||
nfsdstats.rcmisses,
|
||||
nfsdstats.rcnocache,
|
||||
nfsdstats.fh_stale,
|
||||
nfsdstats.fh_lookup,
|
||||
nfsdstats.fh_anon,
|
||||
nfsdstats.fh_nocache_dir,
|
||||
nfsdstats.fh_nocache_nondir,
|
||||
nfsdstats.io_read,
|
||||
nfsdstats.io_write);
|
||||
/* thread usage: */
|
||||
seq_printf(seq, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt);
|
||||
for (i=0; i<10; i++) {
|
||||
unsigned int jifs = nfsdstats.th_usage[i];
|
||||
unsigned int sec = jifs / HZ, msec = (jifs % HZ)*1000/HZ;
|
||||
seq_printf(seq, " %u.%03u", sec, msec);
|
||||
}
|
||||
seq_printf(seq, "rc %lld %lld %lld\nfh %lld 0 0 0 0\nio %lld %lld\n",
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_HITS]),
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_MISSES]),
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_RC_NOCACHE]),
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_FH_STALE]),
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_IO_READ]),
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_IO_WRITE]));
|
||||
|
||||
/* newline and ra-cache */
|
||||
seq_printf(seq, "\nra %u", nfsdstats.ra_size);
|
||||
for (i=0; i<11; i++)
|
||||
seq_printf(seq, " %u", nfsdstats.ra_depth[i]);
|
||||
seq_putc(seq, '\n');
|
||||
/* thread usage: */
|
||||
seq_printf(seq, "th %u 0", atomic_read(&nfsdstats.th_cnt));
|
||||
|
||||
/* deprecated thread usage histogram stats */
|
||||
for (i = 0; i < 10; i++)
|
||||
seq_puts(seq, " 0.000");
|
||||
|
||||
/* deprecated ra-cache stats */
|
||||
seq_puts(seq, "\nra 0 0 0 0 0 0 0 0 0 0 0 0\n");
|
||||
|
||||
/* show my rpc info */
|
||||
svc_seq_show(seq, &nfsd_svcstats);
|
||||
@ -70,8 +61,10 @@ static int nfsd_proc_show(struct seq_file *seq, void *v)
|
||||
/* Show count for individual nfsv4 operations */
|
||||
/* Writing operation numbers 0 1 2 also for maintaining uniformity */
|
||||
seq_printf(seq,"proc4ops %u", LAST_NFS4_OP + 1);
|
||||
for (i = 0; i <= LAST_NFS4_OP; i++)
|
||||
seq_printf(seq, " %u", nfsdstats.nfs4_opcount[i]);
|
||||
for (i = 0; i <= LAST_NFS4_OP; i++) {
|
||||
seq_printf(seq, " %lld",
|
||||
percpu_counter_sum_positive(&nfsdstats.counter[NFSD_STATS_NFS4_OP(i)]));
|
||||
}
|
||||
|
||||
seq_putc(seq, '\n');
|
||||
#endif
|
||||
@ -79,26 +72,65 @@ static int nfsd_proc_show(struct seq_file *seq, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfsd_proc_open(struct inode *inode, struct file *file)
|
||||
DEFINE_PROC_SHOW_ATTRIBUTE(nfsd);
|
||||
|
||||
int nfsd_percpu_counters_init(struct percpu_counter counters[], int num)
|
||||
{
|
||||
return single_open(file, nfsd_proc_show, NULL);
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; !err && i < num; i++)
|
||||
err = percpu_counter_init(&counters[i], 0, GFP_KERNEL);
|
||||
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
for (; i > 0; i--)
|
||||
percpu_counter_destroy(&counters[i-1]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct proc_ops nfsd_proc_ops = {
|
||||
.proc_open = nfsd_proc_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
|
||||
void
|
||||
nfsd_stat_init(void)
|
||||
void nfsd_percpu_counters_reset(struct percpu_counter counters[], int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
percpu_counter_set(&counters[i], 0);
|
||||
}
|
||||
|
||||
void nfsd_percpu_counters_destroy(struct percpu_counter counters[], int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
percpu_counter_destroy(&counters[i]);
|
||||
}
|
||||
|
||||
static int nfsd_stat_counters_init(void)
|
||||
{
|
||||
return nfsd_percpu_counters_init(nfsdstats.counter, NFSD_STATS_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
static void nfsd_stat_counters_destroy(void)
|
||||
{
|
||||
nfsd_percpu_counters_destroy(nfsdstats.counter, NFSD_STATS_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
int nfsd_stat_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = nfsd_stat_counters_init();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nfsd_stat_shutdown(void)
|
||||
void nfsd_stat_shutdown(void)
|
||||
{
|
||||
nfsd_stat_counters_destroy();
|
||||
svc_proc_unregister(&init_net, "nfsd");
|
||||
}
|
||||
|
@ -8,37 +8,89 @@
|
||||
#define _NFSD_STATS_H
|
||||
|
||||
#include <uapi/linux/nfsd/stats.h>
|
||||
#include <linux/percpu_counter.h>
|
||||
|
||||
|
||||
struct nfsd_stats {
|
||||
unsigned int rchits; /* repcache hits */
|
||||
unsigned int rcmisses; /* repcache hits */
|
||||
unsigned int rcnocache; /* uncached reqs */
|
||||
unsigned int fh_stale; /* FH stale error */
|
||||
unsigned int fh_lookup; /* dentry cached */
|
||||
unsigned int fh_anon; /* anon file dentry returned */
|
||||
unsigned int fh_nocache_dir; /* filehandle not found in dcache */
|
||||
unsigned int fh_nocache_nondir; /* filehandle not found in dcache */
|
||||
unsigned int io_read; /* bytes returned to read requests */
|
||||
unsigned int io_write; /* bytes passed in write requests */
|
||||
unsigned int th_cnt; /* number of available threads */
|
||||
unsigned int th_usage[10]; /* number of ticks during which n perdeciles
|
||||
* of available threads were in use */
|
||||
unsigned int th_fullcnt; /* number of times last free thread was used */
|
||||
unsigned int ra_size; /* size of ra cache */
|
||||
unsigned int ra_depth[11]; /* number of times ra entry was found that deep
|
||||
* in the cache (10percentiles). [10] = not found */
|
||||
enum {
|
||||
NFSD_STATS_RC_HITS, /* repcache hits */
|
||||
NFSD_STATS_RC_MISSES, /* repcache misses */
|
||||
NFSD_STATS_RC_NOCACHE, /* uncached reqs */
|
||||
NFSD_STATS_FH_STALE, /* FH stale error */
|
||||
NFSD_STATS_IO_READ, /* bytes returned to read requests */
|
||||
NFSD_STATS_IO_WRITE, /* bytes passed in write requests */
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
unsigned int nfs4_opcount[LAST_NFS4_OP + 1]; /* count of individual nfsv4 operations */
|
||||
NFSD_STATS_FIRST_NFS4_OP, /* count of individual nfsv4 operations */
|
||||
NFSD_STATS_LAST_NFS4_OP = NFSD_STATS_FIRST_NFS4_OP + LAST_NFS4_OP,
|
||||
#define NFSD_STATS_NFS4_OP(op) (NFSD_STATS_FIRST_NFS4_OP + (op))
|
||||
#endif
|
||||
|
||||
NFSD_STATS_COUNTERS_NUM
|
||||
};
|
||||
|
||||
struct nfsd_stats {
|
||||
struct percpu_counter counter[NFSD_STATS_COUNTERS_NUM];
|
||||
|
||||
atomic_t th_cnt; /* number of available threads */
|
||||
};
|
||||
|
||||
extern struct nfsd_stats nfsdstats;
|
||||
|
||||
extern struct svc_stat nfsd_svcstats;
|
||||
|
||||
void nfsd_stat_init(void);
|
||||
int nfsd_percpu_counters_init(struct percpu_counter counters[], int num);
|
||||
void nfsd_percpu_counters_reset(struct percpu_counter counters[], int num);
|
||||
void nfsd_percpu_counters_destroy(struct percpu_counter counters[], int num);
|
||||
int nfsd_stat_init(void);
|
||||
void nfsd_stat_shutdown(void);
|
||||
|
||||
static inline void nfsd_stats_rc_hits_inc(void)
|
||||
{
|
||||
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_RC_HITS]);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_rc_misses_inc(void)
|
||||
{
|
||||
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_RC_MISSES]);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_rc_nocache_inc(void)
|
||||
{
|
||||
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_RC_NOCACHE]);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_fh_stale_inc(struct svc_export *exp)
|
||||
{
|
||||
percpu_counter_inc(&nfsdstats.counter[NFSD_STATS_FH_STALE]);
|
||||
if (exp)
|
||||
percpu_counter_inc(&exp->ex_stats.counter[EXP_STATS_FH_STALE]);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_io_read_add(struct svc_export *exp, s64 amount)
|
||||
{
|
||||
percpu_counter_add(&nfsdstats.counter[NFSD_STATS_IO_READ], amount);
|
||||
if (exp)
|
||||
percpu_counter_add(&exp->ex_stats.counter[EXP_STATS_IO_READ], amount);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_io_write_add(struct svc_export *exp, s64 amount)
|
||||
{
|
||||
percpu_counter_add(&nfsdstats.counter[NFSD_STATS_IO_WRITE], amount);
|
||||
if (exp)
|
||||
percpu_counter_add(&exp->ex_stats.counter[EXP_STATS_IO_WRITE], amount);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_payload_misses_inc(struct nfsd_net *nn)
|
||||
{
|
||||
percpu_counter_inc(&nn->counter[NFSD_NET_PAYLOAD_MISSES]);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_drc_mem_usage_add(struct nfsd_net *nn, s64 amount)
|
||||
{
|
||||
percpu_counter_add(&nn->counter[NFSD_NET_DRC_MEM_USAGE], amount);
|
||||
}
|
||||
|
||||
static inline void nfsd_stats_drc_mem_usage_sub(struct nfsd_net *nn, s64 amount)
|
||||
{
|
||||
percpu_counter_sub(&nn->counter[NFSD_NET_DRC_MEM_USAGE], amount);
|
||||
}
|
||||
|
||||
#endif /* _NFSD_STATS_H */
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
|
890
fs/nfsd/trace.h
890
fs/nfsd/trace.h
File diff suppressed because it is too large
Load Diff
927
fs/nfsd/vfs.c
927
fs/nfsd/vfs.c
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,8 @@
|
||||
#ifndef LINUX_NFSD_VFS_H
|
||||
#define LINUX_NFSD_VFS_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include "nfsfh.h"
|
||||
#include "nfsd.h"
|
||||
|
||||
@ -42,6 +44,23 @@ struct nfsd_file;
|
||||
typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
|
||||
|
||||
/* nfsd/vfs.c */
|
||||
struct nfsd_attrs {
|
||||
struct iattr *na_iattr; /* input */
|
||||
struct xdr_netobj *na_seclabel; /* input */
|
||||
struct posix_acl *na_pacl; /* input */
|
||||
struct posix_acl *na_dpacl; /* input */
|
||||
|
||||
int na_labelerr; /* output */
|
||||
int na_aclerr; /* output */
|
||||
};
|
||||
|
||||
static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
|
||||
{
|
||||
posix_acl_release(attrs->na_pacl);
|
||||
posix_acl_release(attrs->na_dpacl);
|
||||
}
|
||||
|
||||
__be32 nfserrno (int errno);
|
||||
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
|
||||
struct svc_export **expp);
|
||||
__be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *,
|
||||
@ -50,32 +69,28 @@ __be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
|
||||
const char *, unsigned int,
|
||||
struct svc_export **, struct dentry **);
|
||||
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
|
||||
struct iattr *, int, time64_t);
|
||||
struct nfsd_attrs *, int, time64_t);
|
||||
int nfsd_mountpoint(struct dentry *, struct svc_export *);
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
|
||||
struct xdr_netobj *);
|
||||
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
|
||||
struct file *, loff_t, loff_t, int);
|
||||
__be32 nfsd4_clone_file_range(struct nfsd_file *nf_src, u64 src_pos,
|
||||
__be32 nfsd4_clone_file_range(struct svc_rqst *rqstp,
|
||||
struct nfsd_file *nf_src, u64 src_pos,
|
||||
struct nfsd_file *nf_dst, u64 dst_pos,
|
||||
u64 count, bool sync);
|
||||
#endif /* CONFIG_NFSD_V4 */
|
||||
__be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *,
|
||||
char *name, int len, struct iattr *attrs,
|
||||
int type, dev_t rdev, struct svc_fh *res);
|
||||
struct nfsd_attrs *attrs, int type, dev_t rdev,
|
||||
struct svc_fh *res);
|
||||
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
|
||||
char *name, int len, struct iattr *attrs,
|
||||
char *name, int len, struct nfsd_attrs *attrs,
|
||||
int type, dev_t rdev, struct svc_fh *res);
|
||||
#ifdef CONFIG_NFSD_V3
|
||||
__be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *);
|
||||
__be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *,
|
||||
char *name, int len, struct iattr *attrs,
|
||||
struct svc_fh *res, int createmode,
|
||||
u32 *verifier, bool *truncp, bool *created);
|
||||
__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
|
||||
loff_t, unsigned long, __be32 *verf);
|
||||
#endif /* CONFIG_NFSD_V3 */
|
||||
__be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct svc_fh *resfhp, struct nfsd_attrs *iap);
|
||||
__be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp,
|
||||
struct nfsd_file *nf, u64 offset, u32 count,
|
||||
__be32 *verf);
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
__be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
char *name, void **bufp, int *lenp);
|
||||
@ -89,7 +104,7 @@ __be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
int nfsd_open_break_lease(struct inode *, int);
|
||||
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
|
||||
int, struct file **);
|
||||
__be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *, umode_t,
|
||||
__be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *,
|
||||
int, struct file **);
|
||||
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct file *file, loff_t offset,
|
||||
@ -114,6 +129,7 @@ __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
|
||||
char *, int *);
|
||||
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
|
||||
char *name, int len, char *path,
|
||||
struct nfsd_attrs *attrs,
|
||||
struct svc_fh *res);
|
||||
__be32 nfsd_link(struct svc_rqst *, struct svc_fh *,
|
||||
char *, int, struct svc_fh *);
|
||||
@ -152,7 +168,7 @@ static inline void fh_drop_write(struct svc_fh *fh)
|
||||
}
|
||||
}
|
||||
|
||||
static inline __be32 fh_getattr(struct svc_fh *fh, struct kstat *stat)
|
||||
static inline __be32 fh_getattr(const struct svc_fh *fh, struct kstat *stat)
|
||||
{
|
||||
struct path p = {.mnt = fh->fh_export->ex_path.mnt,
|
||||
.dentry = fh->fh_dentry};
|
||||
@ -160,10 +176,4 @@ static inline __be32 fh_getattr(struct svc_fh *fh, struct kstat *stat)
|
||||
AT_STATX_SYNC_AS_STAT));
|
||||
}
|
||||
|
||||
static inline int nfsd_create_is_exclusive(int createmode)
|
||||
{
|
||||
return createmode == NFS3_CREATE_EXCLUSIVE
|
||||
|| createmode == NFS4_CREATE_EXCLUSIVE4_1;
|
||||
}
|
||||
|
||||
#endif /* LINUX_NFSD_VFS_H */
|
||||
|
@ -27,14 +27,13 @@ struct nfsd_readargs {
|
||||
struct svc_fh fh;
|
||||
__u32 offset;
|
||||
__u32 count;
|
||||
int vlen;
|
||||
};
|
||||
|
||||
struct nfsd_writeargs {
|
||||
svc_fh fh;
|
||||
__u32 offset;
|
||||
__u32 len;
|
||||
struct kvec first;
|
||||
struct xdr_buf payload;
|
||||
};
|
||||
|
||||
struct nfsd_createargs {
|
||||
@ -53,11 +52,6 @@ struct nfsd_renameargs {
|
||||
unsigned int tlen;
|
||||
};
|
||||
|
||||
struct nfsd_readlinkargs {
|
||||
struct svc_fh fh;
|
||||
char * buffer;
|
||||
};
|
||||
|
||||
struct nfsd_linkargs {
|
||||
struct svc_fh ffh;
|
||||
struct svc_fh tfh;
|
||||
@ -79,7 +73,6 @@ struct nfsd_readdirargs {
|
||||
struct svc_fh fh;
|
||||
__u32 cookie;
|
||||
__u32 count;
|
||||
__be32 * buffer;
|
||||
};
|
||||
|
||||
struct nfsd_stat {
|
||||
@ -101,6 +94,7 @@ struct nfsd_diropres {
|
||||
struct nfsd_readlinkres {
|
||||
__be32 status;
|
||||
int len;
|
||||
struct page *page;
|
||||
};
|
||||
|
||||
struct nfsd_readres {
|
||||
@ -108,17 +102,20 @@ struct nfsd_readres {
|
||||
struct svc_fh fh;
|
||||
unsigned long count;
|
||||
struct kstat stat;
|
||||
struct page **pages;
|
||||
};
|
||||
|
||||
struct nfsd_readdirres {
|
||||
/* Components of the reply */
|
||||
__be32 status;
|
||||
|
||||
int count;
|
||||
|
||||
/* Used to encode the reply's entry list */
|
||||
struct xdr_stream xdr;
|
||||
struct xdr_buf dirlist;
|
||||
struct readdir_cd common;
|
||||
__be32 * buffer;
|
||||
int buflen;
|
||||
__be32 * offset;
|
||||
unsigned int cookie_offset;
|
||||
};
|
||||
|
||||
struct nfsd_statfsres {
|
||||
@ -144,36 +141,37 @@ union nfsd_xdrstore {
|
||||
#define NFS2_SVC_XDRSIZE sizeof(union nfsd_xdrstore)
|
||||
|
||||
|
||||
int nfssvc_decode_void(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_fhandle(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_sattrargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_diropargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_readargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_writeargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_createargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_renameargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_readlinkargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_linkargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_symlinkargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_decode_readdirargs(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_void(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_stat(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_attrstat(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_diropres(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_readlinkres(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_readres(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_statfsres(struct svc_rqst *, __be32 *);
|
||||
int nfssvc_encode_readdirres(struct svc_rqst *, __be32 *);
|
||||
bool nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
|
||||
int nfssvc_encode_entry(void *, const char *name,
|
||||
int namlen, loff_t offset, u64 ino, unsigned int);
|
||||
bool nfssvc_encode_statres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfssvc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
|
||||
void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset);
|
||||
int nfssvc_encode_entry(void *data, const char *name, int namlen,
|
||||
loff_t offset, u64 ino, unsigned int d_type);
|
||||
|
||||
void nfssvc_release_attrstat(struct svc_rqst *rqstp);
|
||||
void nfssvc_release_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);
|
||||
__be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp);
|
||||
bool svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp);
|
||||
bool svcxdr_encode_stat(struct xdr_stream *xdr, __be32 status);
|
||||
bool svcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
const struct svc_fh *fhp, const struct kstat *stat);
|
||||
|
||||
#endif /* LINUX_NFSD_H */
|
||||
|
116
fs/nfsd/xdr3.h
116
fs/nfsd/xdr3.h
@ -25,14 +25,13 @@ struct nfsd3_diropargs {
|
||||
|
||||
struct nfsd3_accessargs {
|
||||
struct svc_fh fh;
|
||||
unsigned int access;
|
||||
__u32 access;
|
||||
};
|
||||
|
||||
struct nfsd3_readargs {
|
||||
struct svc_fh fh;
|
||||
__u64 offset;
|
||||
__u32 count;
|
||||
int vlen;
|
||||
};
|
||||
|
||||
struct nfsd3_writeargs {
|
||||
@ -41,7 +40,7 @@ struct nfsd3_writeargs {
|
||||
__u32 count;
|
||||
int stable;
|
||||
__u32 len;
|
||||
struct kvec first;
|
||||
struct xdr_buf payload;
|
||||
};
|
||||
|
||||
struct nfsd3_createargs {
|
||||
@ -71,11 +70,6 @@ struct nfsd3_renameargs {
|
||||
unsigned int tlen;
|
||||
};
|
||||
|
||||
struct nfsd3_readlinkargs {
|
||||
struct svc_fh fh;
|
||||
char * buffer;
|
||||
};
|
||||
|
||||
struct nfsd3_linkargs {
|
||||
struct svc_fh ffh;
|
||||
struct svc_fh tfh;
|
||||
@ -96,10 +90,8 @@ struct nfsd3_symlinkargs {
|
||||
struct nfsd3_readdirargs {
|
||||
struct svc_fh fh;
|
||||
__u64 cookie;
|
||||
__u32 dircount;
|
||||
__u32 count;
|
||||
__be32 * verf;
|
||||
__be32 * buffer;
|
||||
};
|
||||
|
||||
struct nfsd3_commitargs {
|
||||
@ -110,13 +102,13 @@ struct nfsd3_commitargs {
|
||||
|
||||
struct nfsd3_getaclargs {
|
||||
struct svc_fh fh;
|
||||
int mask;
|
||||
__u32 mask;
|
||||
};
|
||||
|
||||
struct posix_acl;
|
||||
struct nfsd3_setaclargs {
|
||||
struct svc_fh fh;
|
||||
int mask;
|
||||
__u32 mask;
|
||||
struct posix_acl *acl_access;
|
||||
struct posix_acl *acl_default;
|
||||
};
|
||||
@ -145,6 +137,7 @@ struct nfsd3_readlinkres {
|
||||
__be32 status;
|
||||
struct svc_fh fh;
|
||||
__u32 len;
|
||||
struct page **pages;
|
||||
};
|
||||
|
||||
struct nfsd3_readres {
|
||||
@ -152,6 +145,7 @@ struct nfsd3_readres {
|
||||
struct svc_fh fh;
|
||||
unsigned long count;
|
||||
__u32 eof;
|
||||
struct page **pages;
|
||||
};
|
||||
|
||||
struct nfsd3_writeres {
|
||||
@ -175,19 +169,17 @@ struct nfsd3_linkres {
|
||||
};
|
||||
|
||||
struct nfsd3_readdirres {
|
||||
/* Components of the reply */
|
||||
__be32 status;
|
||||
struct svc_fh fh;
|
||||
/* Just to save kmalloc on every readdirplus entry (svc_fh is a
|
||||
* little large for the stack): */
|
||||
struct svc_fh scratch;
|
||||
int count;
|
||||
__be32 verf[2];
|
||||
|
||||
/* Used to encode the reply's entry list */
|
||||
struct xdr_stream xdr;
|
||||
struct xdr_buf dirlist;
|
||||
struct svc_fh scratch;
|
||||
struct readdir_cd common;
|
||||
__be32 * buffer;
|
||||
int buflen;
|
||||
__be32 * offset;
|
||||
__be32 * offset1;
|
||||
unsigned int cookie_offset;
|
||||
struct svc_rqst * rqstp;
|
||||
|
||||
};
|
||||
@ -273,52 +265,50 @@ 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 *);
|
||||
int nfs3svc_decode_accessargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_readargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_writeargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_createargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_mkdirargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_mknodargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_renameargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_readlinkargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_linkargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_symlinkargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_readdirargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_readdirplusargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_decode_commitargs(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_voidres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_attrstat(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_wccstat(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_diropres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_accessres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_readlinkres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_readres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_writeres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_createres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_renameres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_linkres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_readdirres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_fsstatres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_fsinfores(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_pathconfres(struct svc_rqst *, __be32 *);
|
||||
int nfs3svc_encode_commitres(struct svc_rqst *, __be32 *);
|
||||
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
|
||||
bool nfs3svc_encode_getattrres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs3svc_encode_commitres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
|
||||
void nfs3svc_release_fhandle(struct svc_rqst *);
|
||||
void nfs3svc_release_fhandle2(struct svc_rqst *);
|
||||
int nfs3svc_encode_entry(void *, const char *name,
|
||||
int namlen, loff_t offset, u64 ino,
|
||||
unsigned int);
|
||||
int nfs3svc_encode_entry_plus(void *, const char *name,
|
||||
int namlen, loff_t offset, u64 ino,
|
||||
unsigned int);
|
||||
/* Helper functions for NFSv3 ACL code */
|
||||
__be32 *nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p,
|
||||
struct svc_fh *fhp);
|
||||
__be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp);
|
||||
|
||||
void nfs3svc_encode_cookie3(struct nfsd3_readdirres *resp, u64 offset);
|
||||
int nfs3svc_encode_entry3(void *data, const char *name, int namlen,
|
||||
loff_t offset, u64 ino, unsigned int d_type);
|
||||
int nfs3svc_encode_entryplus3(void *data, const char *name, int namlen,
|
||||
loff_t offset, u64 ino, unsigned int d_type);
|
||||
/* Helper functions for NFSv3 ACL code */
|
||||
bool svcxdr_decode_nfs_fh3(struct xdr_stream *xdr, struct svc_fh *fhp);
|
||||
bool svcxdr_encode_nfsstat3(struct xdr_stream *xdr, __be32 status);
|
||||
bool svcxdr_encode_post_op_attr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
const struct svc_fh *fhp);
|
||||
|
||||
#endif /* _LINUX_NFSD_XDR3_H */
|
||||
|
123
fs/nfsd/xdr4.h
123
fs/nfsd/xdr4.h
@ -76,12 +76,7 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
|
||||
|
||||
struct nfsd4_change_info {
|
||||
u32 atomic;
|
||||
bool change_supported;
|
||||
u32 before_ctime_sec;
|
||||
u32 before_ctime_nsec;
|
||||
u64 before_change;
|
||||
u32 after_ctime_sec;
|
||||
u32 after_ctime_nsec;
|
||||
u64 after_change;
|
||||
};
|
||||
|
||||
@ -252,7 +247,8 @@ struct nfsd4_listxattrs {
|
||||
|
||||
struct nfsd4_open {
|
||||
u32 op_claim_type; /* request */
|
||||
struct xdr_netobj op_fname; /* request - everything but CLAIM_PREV */
|
||||
u32 op_fnamelen;
|
||||
char * op_fname; /* request - everything but CLAIM_PREV */
|
||||
u32 op_delegate_type; /* request - CLAIM_PREV only */
|
||||
stateid_t op_delegate_stateid; /* request - response */
|
||||
u32 op_why_no_deleg; /* response - DELEG_NONE_EXT only */
|
||||
@ -277,11 +273,13 @@ struct nfsd4_open {
|
||||
bool op_truncate; /* used during processing */
|
||||
bool op_created; /* used during processing */
|
||||
struct nfs4_openowner *op_openowner; /* used during processing */
|
||||
struct file *op_filp; /* used during processing */
|
||||
struct nfs4_file *op_file; /* used during processing */
|
||||
struct nfs4_ol_stateid *op_stp; /* used during processing */
|
||||
struct nfs4_clnt_odstate *op_odstate; /* used during processing */
|
||||
struct nfs4_acl *op_acl;
|
||||
struct xdr_netobj op_label;
|
||||
struct svc_rqst *op_rqstp;
|
||||
};
|
||||
|
||||
struct nfsd4_open_confirm {
|
||||
@ -308,6 +306,7 @@ struct nfsd4_read {
|
||||
|
||||
struct svc_rqst *rd_rqstp; /* response */
|
||||
struct svc_fh *rd_fhp; /* response */
|
||||
u32 rd_eof; /* response */
|
||||
};
|
||||
|
||||
struct nfsd4_readdir {
|
||||
@ -385,13 +384,6 @@ struct nfsd4_setclientid_confirm {
|
||||
nfs4_verifier sc_confirm;
|
||||
};
|
||||
|
||||
struct nfsd4_saved_compoundargs {
|
||||
__be32 *p;
|
||||
__be32 *end;
|
||||
int pagelen;
|
||||
struct page **pagelist;
|
||||
};
|
||||
|
||||
struct nfsd4_test_stateid_id {
|
||||
__be32 ts_id_status;
|
||||
stateid_t ts_id_stateid;
|
||||
@ -419,8 +411,7 @@ struct nfsd4_write {
|
||||
u64 wr_offset; /* request */
|
||||
u32 wr_stable_how; /* request */
|
||||
u32 wr_buflen; /* request */
|
||||
struct kvec wr_head;
|
||||
struct page ** wr_pagelist; /* request */
|
||||
struct xdr_buf wr_payload; /* request */
|
||||
|
||||
u32 wr_bytes_written; /* response */
|
||||
u32 wr_how_written; /* response */
|
||||
@ -433,7 +424,7 @@ struct nfsd4_exchange_id {
|
||||
u32 flags;
|
||||
clientid_t clientid;
|
||||
u32 seqid;
|
||||
int spa_how;
|
||||
u32 spa_how;
|
||||
u32 spo_must_enforce[3];
|
||||
u32 spo_must_allow[3];
|
||||
struct xdr_netobj nii_domain;
|
||||
@ -543,6 +534,13 @@ struct nfsd42_write_res {
|
||||
stateid_t cb_stateid;
|
||||
};
|
||||
|
||||
struct nfsd4_cb_offload {
|
||||
struct nfsd4_callback co_cb;
|
||||
struct nfsd42_write_res co_res;
|
||||
__be32 co_nfserr;
|
||||
struct knfsd_fh co_fh;
|
||||
};
|
||||
|
||||
struct nfsd4_copy {
|
||||
/* request */
|
||||
stateid_t cp_src_stateid;
|
||||
@ -550,18 +548,16 @@ struct nfsd4_copy {
|
||||
u64 cp_src_pos;
|
||||
u64 cp_dst_pos;
|
||||
u64 cp_count;
|
||||
struct nl4_server cp_src;
|
||||
bool cp_intra;
|
||||
struct nl4_server *cp_src;
|
||||
|
||||
/* both */
|
||||
bool cp_synchronous;
|
||||
unsigned long cp_flags;
|
||||
#define NFSD4_COPY_F_STOPPED (0)
|
||||
#define NFSD4_COPY_F_INTRA (1)
|
||||
#define NFSD4_COPY_F_SYNCHRONOUS (2)
|
||||
#define NFSD4_COPY_F_COMMITTED (3)
|
||||
|
||||
/* response */
|
||||
struct nfsd42_write_res cp_res;
|
||||
|
||||
/* for cb_offload */
|
||||
struct nfsd4_callback cp_cb;
|
||||
__be32 nfserr;
|
||||
struct knfsd_fh fh;
|
||||
|
||||
struct nfs4_client *cp_clp;
|
||||
@ -574,13 +570,34 @@ struct nfsd4_copy {
|
||||
struct list_head copies;
|
||||
struct task_struct *copy_task;
|
||||
refcount_t refcount;
|
||||
bool stopped;
|
||||
|
||||
struct vfsmount *ss_mnt;
|
||||
struct nfsd4_ssc_umount_item *ss_nsui;
|
||||
struct nfs_fh c_fh;
|
||||
nfs4_stateid stateid;
|
||||
};
|
||||
extern bool inter_copy_offload_enable;
|
||||
|
||||
static inline void nfsd4_copy_set_sync(struct nfsd4_copy *copy, bool sync)
|
||||
{
|
||||
if (sync)
|
||||
set_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags);
|
||||
else
|
||||
clear_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags);
|
||||
}
|
||||
|
||||
static inline bool nfsd4_copy_is_sync(const struct nfsd4_copy *copy)
|
||||
{
|
||||
return test_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags);
|
||||
}
|
||||
|
||||
static inline bool nfsd4_copy_is_async(const struct nfsd4_copy *copy)
|
||||
{
|
||||
return !test_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags);
|
||||
}
|
||||
|
||||
static inline bool nfsd4_ssc_is_inter(const struct nfsd4_copy *copy)
|
||||
{
|
||||
return !test_bit(NFSD4_COPY_F_INTRA, ©->cp_flags);
|
||||
}
|
||||
|
||||
struct nfsd4_seek {
|
||||
/* request */
|
||||
@ -605,19 +622,20 @@ struct nfsd4_offload_status {
|
||||
struct nfsd4_copy_notify {
|
||||
/* request */
|
||||
stateid_t cpn_src_stateid;
|
||||
struct nl4_server cpn_dst;
|
||||
struct nl4_server *cpn_dst;
|
||||
|
||||
/* response */
|
||||
stateid_t cpn_cnr_stateid;
|
||||
u64 cpn_sec;
|
||||
u32 cpn_nsec;
|
||||
struct nl4_server cpn_src;
|
||||
struct nl4_server *cpn_src;
|
||||
};
|
||||
|
||||
struct nfsd4_op {
|
||||
int opnum;
|
||||
const struct nfsd4_operation * opdesc;
|
||||
u32 opnum;
|
||||
__be32 status;
|
||||
const struct nfsd4_operation *opdesc;
|
||||
struct nfs4_replay *replay;
|
||||
union nfsd4_op_u {
|
||||
struct nfsd4_access access;
|
||||
struct nfsd4_close close;
|
||||
@ -681,7 +699,6 @@ struct nfsd4_op {
|
||||
struct nfsd4_listxattrs listxattrs;
|
||||
struct nfsd4_removexattr removexattr;
|
||||
} u;
|
||||
struct nfs4_replay * replay;
|
||||
};
|
||||
|
||||
bool nfsd4_cache_this_op(struct nfsd4_op *);
|
||||
@ -696,35 +713,29 @@ struct svcxdr_tmpbuf {
|
||||
|
||||
struct nfsd4_compoundargs {
|
||||
/* scratch variables for XDR decode */
|
||||
__be32 * p;
|
||||
__be32 * end;
|
||||
struct page ** pagelist;
|
||||
int pagelen;
|
||||
bool tail;
|
||||
__be32 tmp[8];
|
||||
__be32 * tmpp;
|
||||
struct xdr_stream *xdr;
|
||||
struct svcxdr_tmpbuf *to_free;
|
||||
|
||||
struct svc_rqst *rqstp;
|
||||
|
||||
u32 taglen;
|
||||
char * tag;
|
||||
u32 taglen;
|
||||
u32 minorversion;
|
||||
u32 client_opcnt;
|
||||
u32 opcnt;
|
||||
struct nfsd4_op *ops;
|
||||
struct nfsd4_op iops[8];
|
||||
int cachetype;
|
||||
};
|
||||
|
||||
struct nfsd4_compoundres {
|
||||
/* scratch variables for XDR encode */
|
||||
struct xdr_stream xdr;
|
||||
struct xdr_stream *xdr;
|
||||
struct svc_rqst * rqstp;
|
||||
|
||||
u32 taglen;
|
||||
__be32 *statusp;
|
||||
char * tag;
|
||||
u32 taglen;
|
||||
u32 opcnt;
|
||||
__be32 * tagp; /* tag, opcount encode location */
|
||||
|
||||
struct nfsd4_compound_state cstate;
|
||||
};
|
||||
|
||||
@ -767,24 +778,16 @@ static inline void
|
||||
set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp)
|
||||
{
|
||||
BUG_ON(!fhp->fh_pre_saved);
|
||||
cinfo->atomic = (u32)fhp->fh_post_saved;
|
||||
cinfo->change_supported = IS_I_VERSION(d_inode(fhp->fh_dentry));
|
||||
cinfo->atomic = (u32)(fhp->fh_post_saved && !fhp->fh_no_atomic_attr);
|
||||
|
||||
cinfo->before_change = fhp->fh_pre_change;
|
||||
cinfo->after_change = fhp->fh_post_change;
|
||||
cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec;
|
||||
cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec;
|
||||
cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec;
|
||||
cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec;
|
||||
|
||||
}
|
||||
|
||||
|
||||
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 *);
|
||||
bool nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
bool nfs4svc_encode_compoundres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
|
||||
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
|
||||
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
|
||||
void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op);
|
||||
@ -885,13 +888,19 @@ struct nfsd4_operation {
|
||||
u32 op_flags;
|
||||
char *op_name;
|
||||
/* Try to get response size before operation */
|
||||
u32 (*op_rsize_bop)(struct svc_rqst *, struct nfsd4_op *);
|
||||
u32 (*op_rsize_bop)(const struct svc_rqst *rqstp,
|
||||
const struct nfsd4_op *op);
|
||||
void (*op_get_currentstateid)(struct nfsd4_compound_state *,
|
||||
union nfsd4_op_u *);
|
||||
void (*op_set_currentstateid)(struct nfsd4_compound_state *,
|
||||
union nfsd4_op_u *);
|
||||
};
|
||||
|
||||
struct nfsd4_cb_recall_any {
|
||||
struct nfsd4_callback ra_cb;
|
||||
u32 ra_keep;
|
||||
u32 ra_bmval[1];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -48,3 +48,9 @@
|
||||
#define NFS4_dec_cb_offload_sz (cb_compound_dec_hdr_sz + \
|
||||
cb_sequence_dec_sz + \
|
||||
op_dec_sz)
|
||||
#define NFS4_enc_cb_recall_any_sz (cb_compound_enc_hdr_sz + \
|
||||
cb_sequence_enc_sz + \
|
||||
1 + 1 + 1)
|
||||
#define NFS4_dec_cb_recall_any_sz (cb_compound_dec_hdr_sz + \
|
||||
cb_sequence_dec_sz + \
|
||||
op_dec_sz)
|
||||
|
@ -150,7 +150,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
|
||||
return;
|
||||
dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
|
||||
|
||||
mutex_lock(&dnotify_group->mark_mutex);
|
||||
fsnotify_group_lock(dnotify_group);
|
||||
|
||||
spin_lock(&fsn_mark->lock);
|
||||
prev = &dn_mark->dn;
|
||||
@ -173,7 +173,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
|
||||
free = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&dnotify_group->mark_mutex);
|
||||
fsnotify_group_unlock(dnotify_group);
|
||||
|
||||
if (free)
|
||||
fsnotify_free_mark(fsn_mark);
|
||||
@ -196,7 +196,7 @@ static __u32 convert_arg(unsigned long arg)
|
||||
if (arg & DN_ATTRIB)
|
||||
new_mask |= FS_ATTRIB;
|
||||
if (arg & DN_RENAME)
|
||||
new_mask |= FS_DN_RENAME;
|
||||
new_mask |= FS_RENAME;
|
||||
if (arg & DN_CREATE)
|
||||
new_mask |= (FS_CREATE | FS_MOVED_TO);
|
||||
|
||||
@ -306,7 +306,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
|
||||
new_dn_mark->dn = NULL;
|
||||
|
||||
/* this is needed to prevent the fcntl/close race described below */
|
||||
mutex_lock(&dnotify_group->mark_mutex);
|
||||
fsnotify_group_lock(dnotify_group);
|
||||
|
||||
/* add the new_fsn_mark or find an old one. */
|
||||
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group);
|
||||
@ -316,7 +316,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
|
||||
} else {
|
||||
error = fsnotify_add_inode_mark_locked(new_fsn_mark, inode, 0);
|
||||
if (error) {
|
||||
mutex_unlock(&dnotify_group->mark_mutex);
|
||||
fsnotify_group_unlock(dnotify_group);
|
||||
goto out_err;
|
||||
}
|
||||
spin_lock(&new_fsn_mark->lock);
|
||||
@ -327,7 +327,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
f = fcheck(fd);
|
||||
f = lookup_fd_rcu(fd);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* if (f != filp) means that we lost a race and another task/thread
|
||||
@ -365,7 +365,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
|
||||
|
||||
if (destroy)
|
||||
fsnotify_detach_mark(fsn_mark);
|
||||
mutex_unlock(&dnotify_group->mark_mutex);
|
||||
fsnotify_group_unlock(dnotify_group);
|
||||
if (destroy)
|
||||
fsnotify_free_mark(fsn_mark);
|
||||
fsnotify_put_mark(fsn_mark);
|
||||
@ -383,7 +383,8 @@ static int __init dnotify_init(void)
|
||||
SLAB_PANIC|SLAB_ACCOUNT);
|
||||
dnotify_mark_cache = KMEM_CACHE(dnotify_mark, SLAB_PANIC|SLAB_ACCOUNT);
|
||||
|
||||
dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops);
|
||||
dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops,
|
||||
FSNOTIFY_GROUP_NOFS);
|
||||
if (IS_ERR(dnotify_group))
|
||||
panic("unable to allocate fsnotify group for dnotify\n");
|
||||
return 0;
|
||||
|
@ -14,20 +14,33 @@
|
||||
#include <linux/audit.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/stringhash.h>
|
||||
|
||||
#include "fanotify.h"
|
||||
|
||||
static bool fanotify_path_equal(struct path *p1, struct path *p2)
|
||||
static bool fanotify_path_equal(const struct path *p1, const struct path *p2)
|
||||
{
|
||||
return p1->mnt == p2->mnt && p1->dentry == p2->dentry;
|
||||
}
|
||||
|
||||
static unsigned int fanotify_hash_path(const struct path *path)
|
||||
{
|
||||
return hash_ptr(path->dentry, FANOTIFY_EVENT_HASH_BITS) ^
|
||||
hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
|
||||
}
|
||||
|
||||
static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
|
||||
__kernel_fsid_t *fsid2)
|
||||
{
|
||||
return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
|
||||
}
|
||||
|
||||
static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
|
||||
{
|
||||
return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^
|
||||
hash_32(fsid->val[1], FANOTIFY_EVENT_HASH_BITS);
|
||||
}
|
||||
|
||||
static bool fanotify_fh_equal(struct fanotify_fh *fh1,
|
||||
struct fanotify_fh *fh2)
|
||||
{
|
||||
@ -38,6 +51,16 @@ static bool fanotify_fh_equal(struct fanotify_fh *fh1,
|
||||
!memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len);
|
||||
}
|
||||
|
||||
static unsigned int fanotify_hash_fh(struct fanotify_fh *fh)
|
||||
{
|
||||
long salt = (long)fh->type | (long)fh->len << 8;
|
||||
|
||||
/*
|
||||
* full_name_hash() works long by long, so it handles fh buf optimally.
|
||||
*/
|
||||
return full_name_hash((void *)salt, fanotify_fh_buf(fh), fh->len);
|
||||
}
|
||||
|
||||
static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1,
|
||||
struct fanotify_fid_event *ffe2)
|
||||
{
|
||||
@ -53,8 +76,10 @@ static bool fanotify_info_equal(struct fanotify_info *info1,
|
||||
struct fanotify_info *info2)
|
||||
{
|
||||
if (info1->dir_fh_totlen != info2->dir_fh_totlen ||
|
||||
info1->dir2_fh_totlen != info2->dir2_fh_totlen ||
|
||||
info1->file_fh_totlen != info2->file_fh_totlen ||
|
||||
info1->name_len != info2->name_len)
|
||||
info1->name_len != info2->name_len ||
|
||||
info1->name2_len != info2->name2_len)
|
||||
return false;
|
||||
|
||||
if (info1->dir_fh_totlen &&
|
||||
@ -62,14 +87,24 @@ static bool fanotify_info_equal(struct fanotify_info *info1,
|
||||
fanotify_info_dir_fh(info2)))
|
||||
return false;
|
||||
|
||||
if (info1->dir2_fh_totlen &&
|
||||
!fanotify_fh_equal(fanotify_info_dir2_fh(info1),
|
||||
fanotify_info_dir2_fh(info2)))
|
||||
return false;
|
||||
|
||||
if (info1->file_fh_totlen &&
|
||||
!fanotify_fh_equal(fanotify_info_file_fh(info1),
|
||||
fanotify_info_file_fh(info2)))
|
||||
return false;
|
||||
|
||||
return !info1->name_len ||
|
||||
!memcmp(fanotify_info_name(info1), fanotify_info_name(info2),
|
||||
info1->name_len);
|
||||
if (info1->name_len &&
|
||||
memcmp(fanotify_info_name(info1), fanotify_info_name(info2),
|
||||
info1->name_len))
|
||||
return false;
|
||||
|
||||
return !info1->name2_len ||
|
||||
!memcmp(fanotify_info_name2(info1), fanotify_info_name2(info2),
|
||||
info1->name2_len);
|
||||
}
|
||||
|
||||
static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
|
||||
@ -88,16 +123,22 @@ static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
|
||||
return fanotify_info_equal(info1, info2);
|
||||
}
|
||||
|
||||
static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
|
||||
struct fsnotify_event *new_fsn)
|
||||
static bool fanotify_error_event_equal(struct fanotify_error_event *fee1,
|
||||
struct fanotify_error_event *fee2)
|
||||
{
|
||||
struct fanotify_event *old, *new;
|
||||
/* Error events against the same file system are always merged. */
|
||||
if (!fanotify_fsid_equal(&fee1->fsid, &fee2->fsid))
|
||||
return false;
|
||||
|
||||
pr_debug("%s: old=%p new=%p\n", __func__, old_fsn, new_fsn);
|
||||
old = FANOTIFY_E(old_fsn);
|
||||
new = FANOTIFY_E(new_fsn);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (old_fsn->objectid != new_fsn->objectid ||
|
||||
static bool fanotify_should_merge(struct fanotify_event *old,
|
||||
struct fanotify_event *new)
|
||||
{
|
||||
pr_debug("%s: old=%p new=%p\n", __func__, old, new);
|
||||
|
||||
if (old->hash != new->hash ||
|
||||
old->type != new->type || old->pid != new->pid)
|
||||
return false;
|
||||
|
||||
@ -112,6 +153,13 @@ static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
|
||||
if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* FAN_RENAME event is reported with special info record types,
|
||||
* so we cannot merge it with other events.
|
||||
*/
|
||||
if ((old->mask & FAN_RENAME) != (new->mask & FAN_RENAME))
|
||||
return false;
|
||||
|
||||
switch (old->type) {
|
||||
case FANOTIFY_EVENT_TYPE_PATH:
|
||||
return fanotify_path_equal(fanotify_event_path(old),
|
||||
@ -122,6 +170,9 @@ static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
|
||||
case FANOTIFY_EVENT_TYPE_FID_NAME:
|
||||
return fanotify_name_event_equal(FANOTIFY_NE(old),
|
||||
FANOTIFY_NE(new));
|
||||
case FANOTIFY_EVENT_TYPE_FS_ERROR:
|
||||
return fanotify_error_event_equal(FANOTIFY_EE(old),
|
||||
FANOTIFY_EE(new));
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
@ -133,14 +184,16 @@ static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
|
||||
#define FANOTIFY_MAX_MERGE_EVENTS 128
|
||||
|
||||
/* and the list better be locked by something too! */
|
||||
static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
|
||||
static int fanotify_merge(struct fsnotify_group *group,
|
||||
struct fsnotify_event *event)
|
||||
{
|
||||
struct fsnotify_event *test_event;
|
||||
struct fanotify_event *new;
|
||||
struct fanotify_event *old, *new = FANOTIFY_E(event);
|
||||
unsigned int bucket = fanotify_event_hash_bucket(group, new);
|
||||
struct hlist_head *hlist = &group->fanotify_data.merge_hash[bucket];
|
||||
int i = 0;
|
||||
|
||||
pr_debug("%s: list=%p event=%p\n", __func__, list, event);
|
||||
new = FANOTIFY_E(event);
|
||||
pr_debug("%s: group=%p event=%p bucket=%u\n", __func__,
|
||||
group, event, bucket);
|
||||
|
||||
/*
|
||||
* Don't merge a permission event with any other event so that we know
|
||||
@ -150,11 +203,15 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
|
||||
if (fanotify_is_perm_event(new->mask))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry_reverse(test_event, list, list) {
|
||||
hlist_for_each_entry(old, hlist, merge_list) {
|
||||
if (++i > FANOTIFY_MAX_MERGE_EVENTS)
|
||||
break;
|
||||
if (fanotify_should_merge(test_event, event)) {
|
||||
FANOTIFY_E(test_event)->mask |= new->mask;
|
||||
if (fanotify_should_merge(old, new)) {
|
||||
old->mask |= new->mask;
|
||||
|
||||
if (fanotify_is_error_event(old->mask))
|
||||
FANOTIFY_EE(old)->err_count++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -190,8 +247,11 @@ static int fanotify_get_response(struct fsnotify_group *group,
|
||||
return ret;
|
||||
}
|
||||
/* Event not yet reported? Just remove it. */
|
||||
if (event->state == FAN_EVENT_INIT)
|
||||
if (event->state == FAN_EVENT_INIT) {
|
||||
fsnotify_remove_queued_event(group, &event->fae.fse);
|
||||
/* Permission events are not supposed to be hashed */
|
||||
WARN_ON_ONCE(!hlist_unhashed(&event->fae.merge_list));
|
||||
}
|
||||
/*
|
||||
* Event may be also answered in case signal delivery raced
|
||||
* with wakeup. In that case we have nothing to do besides
|
||||
@ -231,15 +291,17 @@ static int fanotify_get_response(struct fsnotify_group *group,
|
||||
*/
|
||||
static u32 fanotify_group_event_mask(struct fsnotify_group *group,
|
||||
struct fsnotify_iter_info *iter_info,
|
||||
u32 event_mask, const void *data,
|
||||
int data_type, struct inode *dir)
|
||||
u32 *match_mask, u32 event_mask,
|
||||
const void *data, int data_type,
|
||||
struct inode *dir)
|
||||
{
|
||||
__u32 marks_mask = 0, marks_ignored_mask = 0;
|
||||
__u32 marks_mask = 0, marks_ignore_mask = 0;
|
||||
__u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS |
|
||||
FANOTIFY_EVENT_FLAGS;
|
||||
const struct path *path = fsnotify_data_path(data, data_type);
|
||||
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
|
||||
struct fsnotify_mark *mark;
|
||||
bool ondir = event_mask & FAN_ONDIR;
|
||||
int type;
|
||||
|
||||
pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n",
|
||||
@ -254,37 +316,30 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
|
||||
return 0;
|
||||
} else if (!(fid_mode & FAN_REPORT_FID)) {
|
||||
/* Do we have a directory inode to report? */
|
||||
if (!dir && !(event_mask & FS_ISDIR))
|
||||
if (!dir && !ondir)
|
||||
return 0;
|
||||
}
|
||||
|
||||
fsnotify_foreach_obj_type(type) {
|
||||
if (!fsnotify_iter_should_report_type(iter_info, type))
|
||||
continue;
|
||||
mark = iter_info->marks[type];
|
||||
|
||||
/* Apply ignore mask regardless of ISDIR and ON_CHILD flags */
|
||||
marks_ignored_mask |= mark->ignored_mask;
|
||||
fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
|
||||
/*
|
||||
* Apply ignore mask depending on event flags in ignore mask.
|
||||
*/
|
||||
marks_ignore_mask |=
|
||||
fsnotify_effective_ignore_mask(mark, ondir, type);
|
||||
|
||||
/*
|
||||
* If the event is on dir and this mark doesn't care about
|
||||
* events on dir, don't send it!
|
||||
* Send the event depending on event flags in mark mask.
|
||||
*/
|
||||
if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the event is on a child and this mark is on a parent not
|
||||
* watching children, don't send it!
|
||||
*/
|
||||
if (type == FSNOTIFY_OBJ_TYPE_PARENT &&
|
||||
!(mark->mask & FS_EVENT_ON_CHILD))
|
||||
if (!fsnotify_mask_applicable(mark->mask, ondir, type))
|
||||
continue;
|
||||
|
||||
marks_mask |= mark->mask;
|
||||
|
||||
/* Record the mark types of this group that matched the event */
|
||||
*match_mask |= 1U << type;
|
||||
}
|
||||
|
||||
test_mask = event_mask & marks_mask & ~marks_ignored_mask;
|
||||
test_mask = event_mask & marks_mask & ~marks_ignore_mask;
|
||||
|
||||
/*
|
||||
* For dirent modification events (create/delete/move) that do not carry
|
||||
@ -319,13 +374,23 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
|
||||
static int fanotify_encode_fh_len(struct inode *inode)
|
||||
{
|
||||
int dwords = 0;
|
||||
int fh_len;
|
||||
|
||||
if (!inode)
|
||||
return 0;
|
||||
|
||||
exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
|
||||
fh_len = dwords << 2;
|
||||
|
||||
return dwords << 2;
|
||||
/*
|
||||
* struct fanotify_error_event might be preallocated and is
|
||||
* limited to MAX_HANDLE_SZ. This should never happen, but
|
||||
* safeguard by forcing an invalid file handle.
|
||||
*/
|
||||
if (WARN_ON_ONCE(fh_len > MAX_HANDLE_SZ))
|
||||
return 0;
|
||||
|
||||
return fh_len;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -335,7 +400,8 @@ static int fanotify_encode_fh_len(struct inode *inode)
|
||||
* Return 0 on failure to encode.
|
||||
*/
|
||||
static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
|
||||
unsigned int fh_len, gfp_t gfp)
|
||||
unsigned int fh_len, unsigned int *hash,
|
||||
gfp_t gfp)
|
||||
{
|
||||
int dwords, type = 0;
|
||||
char *ext_buf = NULL;
|
||||
@ -345,15 +411,21 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
|
||||
fh->type = FILEID_ROOT;
|
||||
fh->len = 0;
|
||||
fh->flags = 0;
|
||||
|
||||
/*
|
||||
* Invalid FHs are used by FAN_FS_ERROR for errors not
|
||||
* linked to any inode. The f_handle won't be reported
|
||||
* back to userspace.
|
||||
*/
|
||||
if (!inode)
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* !gpf means preallocated variable size fh, but fh_len could
|
||||
* be zero in that case if encoding fh len failed.
|
||||
*/
|
||||
err = -ENOENT;
|
||||
if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4))
|
||||
if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4) || fh_len > MAX_HANDLE_SZ)
|
||||
goto out_err;
|
||||
|
||||
/* No external buffer in a variable size allocated fh */
|
||||
@ -378,6 +450,14 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
|
||||
fh->type = type;
|
||||
fh->len = fh_len;
|
||||
|
||||
out:
|
||||
/*
|
||||
* Mix fh into event merge key. Hash might be NULL in case of
|
||||
* unhashed FID events (i.e. FAN_FS_ERROR).
|
||||
*/
|
||||
if (hash)
|
||||
*hash ^= fanotify_hash_fh(fh);
|
||||
|
||||
return FANOTIFY_FH_HDR_LEN + fh_len;
|
||||
|
||||
out_err:
|
||||
@ -392,17 +472,41 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
|
||||
}
|
||||
|
||||
/*
|
||||
* The inode to use as identifier when reporting fid depends on the event.
|
||||
* Report the modified directory inode on dirent modification events.
|
||||
* Report the "victim" inode otherwise.
|
||||
* FAN_REPORT_FID is ambiguous in that it reports the fid of the child for
|
||||
* some events and the fid of the parent for create/delete/move events.
|
||||
*
|
||||
* With the FAN_REPORT_TARGET_FID flag, the fid of the child is reported
|
||||
* also in create/delete/move events in addition to the fid of the parent
|
||||
* and the name of the child.
|
||||
*/
|
||||
static inline bool fanotify_report_child_fid(unsigned int fid_mode, u32 mask)
|
||||
{
|
||||
if (mask & ALL_FSNOTIFY_DIRENT_EVENTS)
|
||||
return (fid_mode & FAN_REPORT_TARGET_FID);
|
||||
|
||||
return (fid_mode & FAN_REPORT_FID) && !(mask & FAN_ONDIR);
|
||||
}
|
||||
|
||||
/*
|
||||
* The inode to use as identifier when reporting fid depends on the event
|
||||
* and the group flags.
|
||||
*
|
||||
* With the group flag FAN_REPORT_TARGET_FID, always report the child fid.
|
||||
*
|
||||
* Without the group flag FAN_REPORT_TARGET_FID, report the modified directory
|
||||
* fid on dirent events and the child fid otherwise.
|
||||
*
|
||||
* For example:
|
||||
* FS_ATTRIB reports the child inode even if reported on a watched parent.
|
||||
* FS_CREATE reports the modified dir inode and not the created inode.
|
||||
* FS_ATTRIB reports the child fid even if reported on a watched parent.
|
||||
* FS_CREATE reports the modified dir fid without FAN_REPORT_TARGET_FID.
|
||||
* and reports the created child fid with FAN_REPORT_TARGET_FID.
|
||||
*/
|
||||
static struct inode *fanotify_fid_inode(u32 event_mask, const void *data,
|
||||
int data_type, struct inode *dir)
|
||||
int data_type, struct inode *dir,
|
||||
unsigned int fid_mode)
|
||||
{
|
||||
if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
|
||||
if ((event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) &&
|
||||
!(fid_mode & FAN_REPORT_TARGET_FID))
|
||||
return dir;
|
||||
|
||||
return fsnotify_data_inode(data, data_type);
|
||||
@ -424,13 +528,14 @@ static struct inode *fanotify_dfid_inode(u32 event_mask, const void *data,
|
||||
if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
|
||||
return dir;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
if (inode && S_ISDIR(inode->i_mode))
|
||||
return inode;
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
|
||||
unsigned int *hash,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct fanotify_path_event *pevent;
|
||||
@ -441,6 +546,7 @@ static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
|
||||
|
||||
pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH;
|
||||
pevent->path = *path;
|
||||
*hash ^= fanotify_hash_path(path);
|
||||
path_get(path);
|
||||
|
||||
return &pevent->fae;
|
||||
@ -466,6 +572,7 @@ static struct fanotify_event *fanotify_alloc_perm_event(const struct path *path,
|
||||
|
||||
static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
|
||||
__kernel_fsid_t *fsid,
|
||||
unsigned int *hash,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct fanotify_fid_event *ffe;
|
||||
@ -476,78 +583,153 @@ static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
|
||||
|
||||
ffe->fae.type = FANOTIFY_EVENT_TYPE_FID;
|
||||
ffe->fsid = *fsid;
|
||||
*hash ^= fanotify_hash_fsid(fsid);
|
||||
fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id),
|
||||
gfp);
|
||||
hash, gfp);
|
||||
|
||||
return &ffe->fae;
|
||||
}
|
||||
|
||||
static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
|
||||
static struct fanotify_event *fanotify_alloc_name_event(struct inode *dir,
|
||||
__kernel_fsid_t *fsid,
|
||||
const struct qstr *file_name,
|
||||
const struct qstr *name,
|
||||
struct inode *child,
|
||||
struct dentry *moved,
|
||||
unsigned int *hash,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct fanotify_name_event *fne;
|
||||
struct fanotify_info *info;
|
||||
struct fanotify_fh *dfh, *ffh;
|
||||
unsigned int dir_fh_len = fanotify_encode_fh_len(id);
|
||||
struct inode *dir2 = moved ? d_inode(moved->d_parent) : NULL;
|
||||
const struct qstr *name2 = moved ? &moved->d_name : NULL;
|
||||
unsigned int dir_fh_len = fanotify_encode_fh_len(dir);
|
||||
unsigned int dir2_fh_len = fanotify_encode_fh_len(dir2);
|
||||
unsigned int child_fh_len = fanotify_encode_fh_len(child);
|
||||
unsigned int size;
|
||||
unsigned long name_len = name ? name->len : 0;
|
||||
unsigned long name2_len = name2 ? name2->len : 0;
|
||||
unsigned int len, size;
|
||||
|
||||
size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len;
|
||||
/* Reserve terminating null byte even for empty name */
|
||||
size = sizeof(*fne) + name_len + name2_len + 2;
|
||||
if (dir_fh_len)
|
||||
size += FANOTIFY_FH_HDR_LEN + dir_fh_len;
|
||||
if (dir2_fh_len)
|
||||
size += FANOTIFY_FH_HDR_LEN + dir2_fh_len;
|
||||
if (child_fh_len)
|
||||
size += FANOTIFY_FH_HDR_LEN + child_fh_len;
|
||||
if (file_name)
|
||||
size += file_name->len + 1;
|
||||
fne = kmalloc(size, gfp);
|
||||
if (!fne)
|
||||
return NULL;
|
||||
|
||||
fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME;
|
||||
fne->fsid = *fsid;
|
||||
*hash ^= fanotify_hash_fsid(fsid);
|
||||
info = &fne->info;
|
||||
fanotify_info_init(info);
|
||||
if (dir_fh_len) {
|
||||
dfh = fanotify_info_dir_fh(info);
|
||||
info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, 0);
|
||||
len = fanotify_encode_fh(dfh, dir, dir_fh_len, hash, 0);
|
||||
fanotify_info_set_dir_fh(info, len);
|
||||
}
|
||||
if (dir2_fh_len) {
|
||||
dfh = fanotify_info_dir2_fh(info);
|
||||
len = fanotify_encode_fh(dfh, dir2, dir2_fh_len, hash, 0);
|
||||
fanotify_info_set_dir2_fh(info, len);
|
||||
}
|
||||
if (child_fh_len) {
|
||||
ffh = fanotify_info_file_fh(info);
|
||||
info->file_fh_totlen = fanotify_encode_fh(ffh, child, child_fh_len, 0);
|
||||
len = fanotify_encode_fh(ffh, child, child_fh_len, hash, 0);
|
||||
fanotify_info_set_file_fh(info, len);
|
||||
}
|
||||
if (name_len) {
|
||||
fanotify_info_copy_name(info, name);
|
||||
*hash ^= full_name_hash((void *)name_len, name->name, name_len);
|
||||
}
|
||||
if (name2_len) {
|
||||
fanotify_info_copy_name2(info, name2);
|
||||
*hash ^= full_name_hash((void *)name2_len, name2->name,
|
||||
name2_len);
|
||||
}
|
||||
if (file_name)
|
||||
fanotify_info_copy_name(info, file_name);
|
||||
|
||||
pr_debug("%s: ino=%lu size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n",
|
||||
__func__, id->i_ino, size, dir_fh_len, child_fh_len,
|
||||
pr_debug("%s: size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n",
|
||||
__func__, size, dir_fh_len, child_fh_len,
|
||||
info->name_len, info->name_len, fanotify_info_name(info));
|
||||
|
||||
if (dir2_fh_len) {
|
||||
pr_debug("%s: dir2_fh_len=%u name2_len=%u name2='%.*s'\n",
|
||||
__func__, dir2_fh_len, info->name2_len,
|
||||
info->name2_len, fanotify_info_name2(info));
|
||||
}
|
||||
|
||||
return &fne->fae;
|
||||
}
|
||||
|
||||
static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
||||
u32 mask, const void *data,
|
||||
int data_type, struct inode *dir,
|
||||
const struct qstr *file_name,
|
||||
__kernel_fsid_t *fsid)
|
||||
static struct fanotify_event *fanotify_alloc_error_event(
|
||||
struct fsnotify_group *group,
|
||||
__kernel_fsid_t *fsid,
|
||||
const void *data, int data_type,
|
||||
unsigned int *hash)
|
||||
{
|
||||
struct fs_error_report *report =
|
||||
fsnotify_data_error_report(data, data_type);
|
||||
struct inode *inode;
|
||||
struct fanotify_error_event *fee;
|
||||
int fh_len;
|
||||
|
||||
if (WARN_ON_ONCE(!report))
|
||||
return NULL;
|
||||
|
||||
fee = mempool_alloc(&group->fanotify_data.error_events_pool, GFP_NOFS);
|
||||
if (!fee)
|
||||
return NULL;
|
||||
|
||||
fee->fae.type = FANOTIFY_EVENT_TYPE_FS_ERROR;
|
||||
fee->error = report->error;
|
||||
fee->err_count = 1;
|
||||
fee->fsid = *fsid;
|
||||
|
||||
inode = report->inode;
|
||||
fh_len = fanotify_encode_fh_len(inode);
|
||||
|
||||
/* Bad fh_len. Fallback to using an invalid fh. Should never happen. */
|
||||
if (!fh_len && inode)
|
||||
inode = NULL;
|
||||
|
||||
fanotify_encode_fh(&fee->object_fh, inode, fh_len, NULL, 0);
|
||||
|
||||
*hash ^= fanotify_hash_fsid(fsid);
|
||||
|
||||
return &fee->fae;
|
||||
}
|
||||
|
||||
static struct fanotify_event *fanotify_alloc_event(
|
||||
struct fsnotify_group *group,
|
||||
u32 mask, const void *data, int data_type,
|
||||
struct inode *dir, const struct qstr *file_name,
|
||||
__kernel_fsid_t *fsid, u32 match_mask)
|
||||
{
|
||||
struct fanotify_event *event = NULL;
|
||||
gfp_t gfp = GFP_KERNEL_ACCOUNT;
|
||||
struct inode *id = fanotify_fid_inode(mask, data, data_type, dir);
|
||||
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
|
||||
struct inode *id = fanotify_fid_inode(mask, data, data_type, dir,
|
||||
fid_mode);
|
||||
struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
|
||||
const struct path *path = fsnotify_data_path(data, data_type);
|
||||
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
|
||||
struct mem_cgroup *old_memcg;
|
||||
struct dentry *moved = NULL;
|
||||
struct inode *child = NULL;
|
||||
bool name_event = false;
|
||||
unsigned int hash = 0;
|
||||
bool ondir = mask & FAN_ONDIR;
|
||||
struct pid *pid;
|
||||
|
||||
if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) {
|
||||
/*
|
||||
* With both flags FAN_REPORT_DIR_FID and FAN_REPORT_FID, we
|
||||
* report the child fid for events reported on a non-dir child
|
||||
* For certain events and group flags, report the child fid
|
||||
* in addition to reporting the parent fid and maybe child name.
|
||||
*/
|
||||
if ((fid_mode & FAN_REPORT_FID) &&
|
||||
id != dirid && !(mask & FAN_ONDIR))
|
||||
if (fanotify_report_child_fid(fid_mode, mask) && id != dirid)
|
||||
child = id;
|
||||
|
||||
id = dirid;
|
||||
@ -568,10 +750,41 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
||||
if (!(fid_mode & FAN_REPORT_NAME)) {
|
||||
name_event = !!child;
|
||||
file_name = NULL;
|
||||
} else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) ||
|
||||
!(mask & FAN_ONDIR)) {
|
||||
} else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || !ondir) {
|
||||
name_event = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* In the special case of FAN_RENAME event, use the match_mask
|
||||
* to determine if we need to report only the old parent+name,
|
||||
* only the new parent+name or both.
|
||||
* 'dirid' and 'file_name' are the old parent+name and
|
||||
* 'moved' has the new parent+name.
|
||||
*/
|
||||
if (mask & FAN_RENAME) {
|
||||
bool report_old, report_new;
|
||||
|
||||
if (WARN_ON_ONCE(!match_mask))
|
||||
return NULL;
|
||||
|
||||
/* Report both old and new parent+name if sb watching */
|
||||
report_old = report_new =
|
||||
match_mask & (1U << FSNOTIFY_ITER_TYPE_SB);
|
||||
report_old |=
|
||||
match_mask & (1U << FSNOTIFY_ITER_TYPE_INODE);
|
||||
report_new |=
|
||||
match_mask & (1U << FSNOTIFY_ITER_TYPE_INODE2);
|
||||
|
||||
if (!report_old) {
|
||||
/* Do not report old parent+name */
|
||||
dirid = NULL;
|
||||
file_name = NULL;
|
||||
}
|
||||
if (report_new) {
|
||||
/* Report new parent+name */
|
||||
moved = fsnotify_data_dentry(data, data_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -590,28 +803,30 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
||||
|
||||
if (fanotify_is_perm_event(mask)) {
|
||||
event = fanotify_alloc_perm_event(path, gfp);
|
||||
} else if (name_event && (file_name || child)) {
|
||||
event = fanotify_alloc_name_event(id, fsid, file_name, child,
|
||||
gfp);
|
||||
} else if (fanotify_is_error_event(mask)) {
|
||||
event = fanotify_alloc_error_event(group, fsid, data,
|
||||
data_type, &hash);
|
||||
} else if (name_event && (file_name || moved || child)) {
|
||||
event = fanotify_alloc_name_event(dirid, fsid, file_name, child,
|
||||
moved, &hash, gfp);
|
||||
} else if (fid_mode) {
|
||||
event = fanotify_alloc_fid_event(id, fsid, gfp);
|
||||
event = fanotify_alloc_fid_event(id, fsid, &hash, gfp);
|
||||
} else {
|
||||
event = fanotify_alloc_path_event(path, gfp);
|
||||
event = fanotify_alloc_path_event(path, &hash, gfp);
|
||||
}
|
||||
|
||||
if (!event)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Use the victim inode instead of the watching inode as the id for
|
||||
* event queue, so event reported on parent is merged with event
|
||||
* reported on child when both directory and child watches exist.
|
||||
*/
|
||||
fanotify_init_event(event, (unsigned long)id, mask);
|
||||
if (FAN_GROUP_FLAG(group, FAN_REPORT_TID))
|
||||
event->pid = get_pid(task_pid(current));
|
||||
pid = get_pid(task_pid(current));
|
||||
else
|
||||
event->pid = get_pid(task_tgid(current));
|
||||
pid = get_pid(task_tgid(current));
|
||||
|
||||
/* Mix event info, FAN_ONDIR flag and pid into event merge key */
|
||||
hash ^= hash_long((unsigned long)pid | ondir, FANOTIFY_EVENT_HASH_BITS);
|
||||
fanotify_init_event(event, hash, mask);
|
||||
event->pid = pid;
|
||||
|
||||
out:
|
||||
set_active_memcg(old_memcg);
|
||||
@ -625,16 +840,14 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
||||
*/
|
||||
static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
|
||||
{
|
||||
struct fsnotify_mark *mark;
|
||||
int type;
|
||||
__kernel_fsid_t fsid = {};
|
||||
|
||||
fsnotify_foreach_obj_type(type) {
|
||||
fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
|
||||
struct fsnotify_mark_connector *conn;
|
||||
|
||||
if (!fsnotify_iter_should_report_type(iter_info, type))
|
||||
continue;
|
||||
|
||||
conn = READ_ONCE(iter_info->marks[type]->connector);
|
||||
conn = READ_ONCE(mark->connector);
|
||||
/* Mark is just getting destroyed or created? */
|
||||
if (!conn)
|
||||
continue;
|
||||
@ -651,6 +864,27 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
|
||||
return fsid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an event to hash table for faster merge.
|
||||
*/
|
||||
static void fanotify_insert_event(struct fsnotify_group *group,
|
||||
struct fsnotify_event *fsn_event)
|
||||
{
|
||||
struct fanotify_event *event = FANOTIFY_E(fsn_event);
|
||||
unsigned int bucket = fanotify_event_hash_bucket(group, event);
|
||||
struct hlist_head *hlist = &group->fanotify_data.merge_hash[bucket];
|
||||
|
||||
assert_spin_locked(&group->notification_lock);
|
||||
|
||||
if (!fanotify_is_hashed_event(event->mask))
|
||||
return;
|
||||
|
||||
pr_debug("%s: group=%p event=%p bucket=%u\n", __func__,
|
||||
group, event, bucket);
|
||||
|
||||
hlist_add_head(&event->merge_list, hlist);
|
||||
}
|
||||
|
||||
static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
||||
const void *data, int data_type,
|
||||
struct inode *dir,
|
||||
@ -661,6 +895,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
||||
struct fanotify_event *event;
|
||||
struct fsnotify_event *fsn_event;
|
||||
__kernel_fsid_t fsid = {};
|
||||
u32 match_mask = 0;
|
||||
|
||||
BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
|
||||
BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
|
||||
@ -681,15 +916,18 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
||||
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);
|
||||
BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
|
||||
BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
|
||||
BUILD_BUG_ON(FAN_FS_ERROR != FS_ERROR);
|
||||
BUILD_BUG_ON(FAN_RENAME != FS_RENAME);
|
||||
|
||||
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
|
||||
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 21);
|
||||
|
||||
mask = fanotify_group_event_mask(group, iter_info, mask, data,
|
||||
data_type, dir);
|
||||
mask = fanotify_group_event_mask(group, iter_info, &match_mask,
|
||||
mask, data, data_type, dir);
|
||||
if (!mask)
|
||||
return 0;
|
||||
|
||||
pr_debug("%s: group=%p mask=%x\n", __func__, group, mask);
|
||||
pr_debug("%s: group=%p mask=%x report_mask=%x\n", __func__,
|
||||
group, mask, match_mask);
|
||||
|
||||
if (fanotify_is_perm_event(mask)) {
|
||||
/*
|
||||
@ -708,7 +946,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
||||
}
|
||||
|
||||
event = fanotify_alloc_event(group, mask, data, data_type, dir,
|
||||
file_name, &fsid);
|
||||
file_name, &fsid, match_mask);
|
||||
ret = -ENOMEM;
|
||||
if (unlikely(!event)) {
|
||||
/*
|
||||
@ -721,7 +959,8 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
||||
}
|
||||
|
||||
fsn_event = &event->fse;
|
||||
ret = fsnotify_add_event(group, fsn_event, fanotify_merge);
|
||||
ret = fsnotify_insert_event(group, fsn_event, fanotify_merge,
|
||||
fanotify_insert_event);
|
||||
if (ret) {
|
||||
/* Permission events shouldn't be merged */
|
||||
BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS);
|
||||
@ -742,11 +981,13 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
||||
|
||||
static void fanotify_free_group_priv(struct fsnotify_group *group)
|
||||
{
|
||||
struct user_struct *user;
|
||||
kfree(group->fanotify_data.merge_hash);
|
||||
if (group->fanotify_data.ucounts)
|
||||
dec_ucount(group->fanotify_data.ucounts,
|
||||
UCOUNT_FANOTIFY_GROUPS);
|
||||
|
||||
user = group->fanotify_data.user;
|
||||
atomic_dec(&user->fanotify_listeners);
|
||||
free_uid(user);
|
||||
if (mempool_initialized(&group->fanotify_data.error_events_pool))
|
||||
mempool_exit(&group->fanotify_data.error_events_pool);
|
||||
}
|
||||
|
||||
static void fanotify_free_path_event(struct fanotify_event *event)
|
||||
@ -775,7 +1016,16 @@ static void fanotify_free_name_event(struct fanotify_event *event)
|
||||
kfree(FANOTIFY_NE(event));
|
||||
}
|
||||
|
||||
static void fanotify_free_event(struct fsnotify_event *fsn_event)
|
||||
static void fanotify_free_error_event(struct fsnotify_group *group,
|
||||
struct fanotify_event *event)
|
||||
{
|
||||
struct fanotify_error_event *fee = FANOTIFY_EE(event);
|
||||
|
||||
mempool_free(fee, &group->fanotify_data.error_events_pool);
|
||||
}
|
||||
|
||||
static void fanotify_free_event(struct fsnotify_group *group,
|
||||
struct fsnotify_event *fsn_event)
|
||||
{
|
||||
struct fanotify_event *event;
|
||||
|
||||
@ -797,11 +1047,21 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
|
||||
case FANOTIFY_EVENT_TYPE_OVERFLOW:
|
||||
kfree(event);
|
||||
break;
|
||||
case FANOTIFY_EVENT_TYPE_FS_ERROR:
|
||||
fanotify_free_error_event(group, event);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void fanotify_freeing_mark(struct fsnotify_mark *mark,
|
||||
struct fsnotify_group *group)
|
||||
{
|
||||
if (!FAN_GROUP_FLAG(group, FAN_UNLIMITED_MARKS))
|
||||
dec_ucount(group->fanotify_data.ucounts, UCOUNT_FANOTIFY_MARKS);
|
||||
}
|
||||
|
||||
static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
|
||||
{
|
||||
kmem_cache_free(fanotify_mark_cache, fsn_mark);
|
||||
@ -811,5 +1071,6 @@ const struct fsnotify_ops fanotify_fsnotify_ops = {
|
||||
.handle_event = fanotify_handle_event,
|
||||
.free_group_priv = fanotify_free_group_priv,
|
||||
.free_event = fanotify_free_event,
|
||||
.freeing_mark = fanotify_freeing_mark,
|
||||
.free_mark = fanotify_free_mark,
|
||||
};
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <linux/path.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/exportfs.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
extern struct kmem_cache *fanotify_mark_cache;
|
||||
extern struct kmem_cache *fanotify_fid_event_cachep;
|
||||
@ -39,15 +40,45 @@ struct fanotify_fh {
|
||||
struct fanotify_info {
|
||||
/* size of dir_fh/file_fh including fanotify_fh hdr size */
|
||||
u8 dir_fh_totlen;
|
||||
u8 dir2_fh_totlen;
|
||||
u8 file_fh_totlen;
|
||||
u8 name_len;
|
||||
u8 pad;
|
||||
u8 name2_len;
|
||||
u8 pad[3];
|
||||
unsigned char buf[];
|
||||
/*
|
||||
* (struct fanotify_fh) dir_fh starts at buf[0]
|
||||
* (optional) file_fh starts at buf[dir_fh_totlen]
|
||||
* name starts at buf[dir_fh_totlen + file_fh_totlen]
|
||||
* (optional) dir2_fh starts at buf[dir_fh_totlen]
|
||||
* (optional) file_fh starts at buf[dir_fh_totlen + dir2_fh_totlen]
|
||||
* name starts at buf[dir_fh_totlen + dir2_fh_totlen + file_fh_totlen]
|
||||
* ...
|
||||
*/
|
||||
#define FANOTIFY_DIR_FH_SIZE(info) ((info)->dir_fh_totlen)
|
||||
#define FANOTIFY_DIR2_FH_SIZE(info) ((info)->dir2_fh_totlen)
|
||||
#define FANOTIFY_FILE_FH_SIZE(info) ((info)->file_fh_totlen)
|
||||
#define FANOTIFY_NAME_SIZE(info) ((info)->name_len + 1)
|
||||
#define FANOTIFY_NAME2_SIZE(info) ((info)->name2_len + 1)
|
||||
|
||||
#define FANOTIFY_DIR_FH_OFFSET(info) 0
|
||||
#define FANOTIFY_DIR2_FH_OFFSET(info) \
|
||||
(FANOTIFY_DIR_FH_OFFSET(info) + FANOTIFY_DIR_FH_SIZE(info))
|
||||
#define FANOTIFY_FILE_FH_OFFSET(info) \
|
||||
(FANOTIFY_DIR2_FH_OFFSET(info) + FANOTIFY_DIR2_FH_SIZE(info))
|
||||
#define FANOTIFY_NAME_OFFSET(info) \
|
||||
(FANOTIFY_FILE_FH_OFFSET(info) + FANOTIFY_FILE_FH_SIZE(info))
|
||||
#define FANOTIFY_NAME2_OFFSET(info) \
|
||||
(FANOTIFY_NAME_OFFSET(info) + FANOTIFY_NAME_SIZE(info))
|
||||
|
||||
#define FANOTIFY_DIR_FH_BUF(info) \
|
||||
((info)->buf + FANOTIFY_DIR_FH_OFFSET(info))
|
||||
#define FANOTIFY_DIR2_FH_BUF(info) \
|
||||
((info)->buf + FANOTIFY_DIR2_FH_OFFSET(info))
|
||||
#define FANOTIFY_FILE_FH_BUF(info) \
|
||||
((info)->buf + FANOTIFY_FILE_FH_OFFSET(info))
|
||||
#define FANOTIFY_NAME_BUF(info) \
|
||||
((info)->buf + FANOTIFY_NAME_OFFSET(info))
|
||||
#define FANOTIFY_NAME2_BUF(info) \
|
||||
((info)->buf + FANOTIFY_NAME2_OFFSET(info))
|
||||
} __aligned(4);
|
||||
|
||||
static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh)
|
||||
@ -86,7 +117,21 @@ static inline struct fanotify_fh *fanotify_info_dir_fh(struct fanotify_info *inf
|
||||
{
|
||||
BUILD_BUG_ON(offsetof(struct fanotify_info, buf) % 4);
|
||||
|
||||
return (struct fanotify_fh *)info->buf;
|
||||
return (struct fanotify_fh *)FANOTIFY_DIR_FH_BUF(info);
|
||||
}
|
||||
|
||||
static inline int fanotify_info_dir2_fh_len(struct fanotify_info *info)
|
||||
{
|
||||
if (!info->dir2_fh_totlen ||
|
||||
WARN_ON_ONCE(info->dir2_fh_totlen < FANOTIFY_FH_HDR_LEN))
|
||||
return 0;
|
||||
|
||||
return info->dir2_fh_totlen - FANOTIFY_FH_HDR_LEN;
|
||||
}
|
||||
|
||||
static inline struct fanotify_fh *fanotify_info_dir2_fh(struct fanotify_info *info)
|
||||
{
|
||||
return (struct fanotify_fh *)FANOTIFY_DIR2_FH_BUF(info);
|
||||
}
|
||||
|
||||
static inline int fanotify_info_file_fh_len(struct fanotify_info *info)
|
||||
@ -100,27 +145,90 @@ static inline int fanotify_info_file_fh_len(struct fanotify_info *info)
|
||||
|
||||
static inline struct fanotify_fh *fanotify_info_file_fh(struct fanotify_info *info)
|
||||
{
|
||||
return (struct fanotify_fh *)(info->buf + info->dir_fh_totlen);
|
||||
return (struct fanotify_fh *)FANOTIFY_FILE_FH_BUF(info);
|
||||
}
|
||||
|
||||
static inline const char *fanotify_info_name(struct fanotify_info *info)
|
||||
static inline char *fanotify_info_name(struct fanotify_info *info)
|
||||
{
|
||||
return info->buf + info->dir_fh_totlen + info->file_fh_totlen;
|
||||
if (!info->name_len)
|
||||
return NULL;
|
||||
|
||||
return FANOTIFY_NAME_BUF(info);
|
||||
}
|
||||
|
||||
static inline char *fanotify_info_name2(struct fanotify_info *info)
|
||||
{
|
||||
if (!info->name2_len)
|
||||
return NULL;
|
||||
|
||||
return FANOTIFY_NAME2_BUF(info);
|
||||
}
|
||||
|
||||
static inline void fanotify_info_init(struct fanotify_info *info)
|
||||
{
|
||||
BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN + MAX_HANDLE_SZ > U8_MAX);
|
||||
BUILD_BUG_ON(NAME_MAX > U8_MAX);
|
||||
|
||||
info->dir_fh_totlen = 0;
|
||||
info->dir2_fh_totlen = 0;
|
||||
info->file_fh_totlen = 0;
|
||||
info->name_len = 0;
|
||||
info->name2_len = 0;
|
||||
}
|
||||
|
||||
/* These set/copy helpers MUST be called by order */
|
||||
static inline void fanotify_info_set_dir_fh(struct fanotify_info *info,
|
||||
unsigned int totlen)
|
||||
{
|
||||
if (WARN_ON_ONCE(info->dir2_fh_totlen > 0) ||
|
||||
WARN_ON_ONCE(info->file_fh_totlen > 0) ||
|
||||
WARN_ON_ONCE(info->name_len > 0) ||
|
||||
WARN_ON_ONCE(info->name2_len > 0))
|
||||
return;
|
||||
|
||||
info->dir_fh_totlen = totlen;
|
||||
}
|
||||
|
||||
static inline void fanotify_info_set_dir2_fh(struct fanotify_info *info,
|
||||
unsigned int totlen)
|
||||
{
|
||||
if (WARN_ON_ONCE(info->file_fh_totlen > 0) ||
|
||||
WARN_ON_ONCE(info->name_len > 0) ||
|
||||
WARN_ON_ONCE(info->name2_len > 0))
|
||||
return;
|
||||
|
||||
info->dir2_fh_totlen = totlen;
|
||||
}
|
||||
|
||||
static inline void fanotify_info_set_file_fh(struct fanotify_info *info,
|
||||
unsigned int totlen)
|
||||
{
|
||||
if (WARN_ON_ONCE(info->name_len > 0) ||
|
||||
WARN_ON_ONCE(info->name2_len > 0))
|
||||
return;
|
||||
|
||||
info->file_fh_totlen = totlen;
|
||||
}
|
||||
|
||||
static inline void fanotify_info_copy_name(struct fanotify_info *info,
|
||||
const struct qstr *name)
|
||||
{
|
||||
if (WARN_ON_ONCE(name->len > NAME_MAX) ||
|
||||
WARN_ON_ONCE(info->name2_len > 0))
|
||||
return;
|
||||
|
||||
info->name_len = name->len;
|
||||
strcpy(info->buf + info->dir_fh_totlen + info->file_fh_totlen,
|
||||
name->name);
|
||||
strcpy(fanotify_info_name(info), name->name);
|
||||
}
|
||||
|
||||
static inline void fanotify_info_copy_name2(struct fanotify_info *info,
|
||||
const struct qstr *name)
|
||||
{
|
||||
if (WARN_ON_ONCE(name->len > NAME_MAX))
|
||||
return;
|
||||
|
||||
info->name2_len = name->len;
|
||||
strcpy(fanotify_info_name2(info), name->name);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -135,29 +243,48 @@ enum fanotify_event_type {
|
||||
FANOTIFY_EVENT_TYPE_PATH,
|
||||
FANOTIFY_EVENT_TYPE_PATH_PERM,
|
||||
FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
|
||||
FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */
|
||||
__FANOTIFY_EVENT_TYPE_NUM
|
||||
};
|
||||
|
||||
#define FANOTIFY_EVENT_TYPE_BITS \
|
||||
(ilog2(__FANOTIFY_EVENT_TYPE_NUM - 1) + 1)
|
||||
#define FANOTIFY_EVENT_HASH_BITS \
|
||||
(32 - FANOTIFY_EVENT_TYPE_BITS)
|
||||
|
||||
struct fanotify_event {
|
||||
struct fsnotify_event fse;
|
||||
struct hlist_node merge_list; /* List for hashed merge */
|
||||
u32 mask;
|
||||
enum fanotify_event_type type;
|
||||
struct {
|
||||
unsigned int type : FANOTIFY_EVENT_TYPE_BITS;
|
||||
unsigned int hash : FANOTIFY_EVENT_HASH_BITS;
|
||||
};
|
||||
struct pid *pid;
|
||||
};
|
||||
|
||||
static inline void fanotify_init_event(struct fanotify_event *event,
|
||||
unsigned long id, u32 mask)
|
||||
unsigned int hash, u32 mask)
|
||||
{
|
||||
fsnotify_init_event(&event->fse, id);
|
||||
fsnotify_init_event(&event->fse);
|
||||
INIT_HLIST_NODE(&event->merge_list);
|
||||
event->hash = hash;
|
||||
event->mask = mask;
|
||||
event->pid = NULL;
|
||||
}
|
||||
|
||||
#define FANOTIFY_INLINE_FH(name, size) \
|
||||
struct { \
|
||||
struct fanotify_fh (name); \
|
||||
/* Space for object_fh.buf[] - access with fanotify_fh_buf() */ \
|
||||
unsigned char _inline_fh_buf[(size)]; \
|
||||
}
|
||||
|
||||
struct fanotify_fid_event {
|
||||
struct fanotify_event fae;
|
||||
__kernel_fsid_t fsid;
|
||||
struct fanotify_fh object_fh;
|
||||
/* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */
|
||||
unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN];
|
||||
|
||||
FANOTIFY_INLINE_FH(object_fh, FANOTIFY_INLINE_FH_LEN);
|
||||
};
|
||||
|
||||
static inline struct fanotify_fid_event *
|
||||
@ -178,12 +305,30 @@ FANOTIFY_NE(struct fanotify_event *event)
|
||||
return container_of(event, struct fanotify_name_event, fae);
|
||||
}
|
||||
|
||||
struct fanotify_error_event {
|
||||
struct fanotify_event fae;
|
||||
s32 error; /* Error reported by the Filesystem. */
|
||||
u32 err_count; /* Suppressed errors count */
|
||||
|
||||
__kernel_fsid_t fsid; /* FSID this error refers to. */
|
||||
|
||||
FANOTIFY_INLINE_FH(object_fh, MAX_HANDLE_SZ);
|
||||
};
|
||||
|
||||
static inline struct fanotify_error_event *
|
||||
FANOTIFY_EE(struct fanotify_event *event)
|
||||
{
|
||||
return container_of(event, struct fanotify_error_event, fae);
|
||||
}
|
||||
|
||||
static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
|
||||
{
|
||||
if (event->type == FANOTIFY_EVENT_TYPE_FID)
|
||||
return &FANOTIFY_FE(event)->fsid;
|
||||
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
|
||||
return &FANOTIFY_NE(event)->fsid;
|
||||
else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
|
||||
return &FANOTIFY_EE(event)->fsid;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@ -195,6 +340,8 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
|
||||
return &FANOTIFY_FE(event)->object_fh;
|
||||
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
|
||||
return fanotify_info_file_fh(&FANOTIFY_NE(event)->info);
|
||||
else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
|
||||
return &FANOTIFY_EE(event)->object_fh;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@ -226,6 +373,37 @@ static inline int fanotify_event_dir_fh_len(struct fanotify_event *event)
|
||||
return info ? fanotify_info_dir_fh_len(info) : 0;
|
||||
}
|
||||
|
||||
static inline int fanotify_event_dir2_fh_len(struct fanotify_event *event)
|
||||
{
|
||||
struct fanotify_info *info = fanotify_event_info(event);
|
||||
|
||||
return info ? fanotify_info_dir2_fh_len(info) : 0;
|
||||
}
|
||||
|
||||
static inline bool fanotify_event_has_object_fh(struct fanotify_event *event)
|
||||
{
|
||||
/* For error events, even zeroed fh are reported. */
|
||||
if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
|
||||
return true;
|
||||
return fanotify_event_object_fh_len(event) > 0;
|
||||
}
|
||||
|
||||
static inline bool fanotify_event_has_dir_fh(struct fanotify_event *event)
|
||||
{
|
||||
return fanotify_event_dir_fh_len(event) > 0;
|
||||
}
|
||||
|
||||
static inline bool fanotify_event_has_dir2_fh(struct fanotify_event *event)
|
||||
{
|
||||
return fanotify_event_dir2_fh_len(event) > 0;
|
||||
}
|
||||
|
||||
static inline bool fanotify_event_has_any_dir_fh(struct fanotify_event *event)
|
||||
{
|
||||
return fanotify_event_has_dir_fh(event) ||
|
||||
fanotify_event_has_dir2_fh(event);
|
||||
}
|
||||
|
||||
struct fanotify_path_event {
|
||||
struct fanotify_event fae;
|
||||
struct path path;
|
||||
@ -269,13 +447,12 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
|
||||
return container_of(fse, struct fanotify_event, fse);
|
||||
}
|
||||
|
||||
static inline bool fanotify_event_has_path(struct fanotify_event *event)
|
||||
static inline bool fanotify_is_error_event(u32 mask)
|
||||
{
|
||||
return event->type == FANOTIFY_EVENT_TYPE_PATH ||
|
||||
event->type == FANOTIFY_EVENT_TYPE_PATH_PERM;
|
||||
return mask & FAN_FS_ERROR;
|
||||
}
|
||||
|
||||
static inline struct path *fanotify_event_path(struct fanotify_event *event)
|
||||
static inline const struct path *fanotify_event_path(struct fanotify_event *event)
|
||||
{
|
||||
if (event->type == FANOTIFY_EVENT_TYPE_PATH)
|
||||
return &FANOTIFY_PE(event)->path;
|
||||
@ -284,3 +461,40 @@ static inline struct path *fanotify_event_path(struct fanotify_event *event)
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use 128 size hash table to speed up events merge.
|
||||
*/
|
||||
#define FANOTIFY_HTABLE_BITS (7)
|
||||
#define FANOTIFY_HTABLE_SIZE (1 << FANOTIFY_HTABLE_BITS)
|
||||
#define FANOTIFY_HTABLE_MASK (FANOTIFY_HTABLE_SIZE - 1)
|
||||
|
||||
/*
|
||||
* Permission events and overflow event do not get merged - don't hash them.
|
||||
*/
|
||||
static inline bool fanotify_is_hashed_event(u32 mask)
|
||||
{
|
||||
return !(fanotify_is_perm_event(mask) ||
|
||||
fsnotify_is_overflow_event(mask));
|
||||
}
|
||||
|
||||
static inline unsigned int fanotify_event_hash_bucket(
|
||||
struct fsnotify_group *group,
|
||||
struct fanotify_event *event)
|
||||
{
|
||||
return event->hash & FANOTIFY_HTABLE_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
|
||||
{
|
||||
unsigned int mflags = 0;
|
||||
|
||||
if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
|
||||
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
|
||||
if (mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)
|
||||
mflags |= FAN_MARK_EVICTABLE;
|
||||
if (mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
|
||||
mflags |= FAN_MARK_IGNORE;
|
||||
|
||||
return mflags;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
||||
#include <linux/exportfs.h>
|
||||
|
||||
#include "inotify/inotify.h"
|
||||
#include "fanotify/fanotify.h"
|
||||
#include "fdinfo.h"
|
||||
#include "fsnotify.h"
|
||||
|
||||
@ -28,13 +29,13 @@ static void show_fdinfo(struct seq_file *m, struct file *f,
|
||||
struct fsnotify_group *group = f->private_data;
|
||||
struct fsnotify_mark *mark;
|
||||
|
||||
mutex_lock(&group->mark_mutex);
|
||||
fsnotify_group_lock(group);
|
||||
list_for_each_entry(mark, &group->marks_list, g_list) {
|
||||
show(m, mark);
|
||||
if (seq_has_overflowed(m))
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&group->mark_mutex);
|
||||
fsnotify_group_unlock(group);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_EXPORTFS)
|
||||
@ -103,19 +104,16 @@ void inotify_show_fdinfo(struct seq_file *m, struct file *f)
|
||||
|
||||
static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
|
||||
{
|
||||
unsigned int mflags = 0;
|
||||
unsigned int mflags = fanotify_mark_user_flags(mark);
|
||||
struct inode *inode;
|
||||
|
||||
if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
|
||||
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
|
||||
|
||||
if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) {
|
||||
inode = igrab(fsnotify_conn_inode(mark->connector));
|
||||
if (!inode)
|
||||
return;
|
||||
seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ",
|
||||
inode->i_ino, inode->i_sb->s_dev,
|
||||
mflags, mark->mask, mark->ignored_mask);
|
||||
mflags, mark->mask, mark->ignore_mask);
|
||||
show_mark_fhandle(m, inode);
|
||||
seq_putc(m, '\n');
|
||||
iput(inode);
|
||||
@ -123,12 +121,12 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
|
||||
struct mount *mnt = fsnotify_conn_mount(mark->connector);
|
||||
|
||||
seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
|
||||
mnt->mnt_id, mflags, mark->mask, mark->ignored_mask);
|
||||
mnt->mnt_id, mflags, mark->mask, mark->ignore_mask);
|
||||
} else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) {
|
||||
struct super_block *sb = fsnotify_conn_sb(mark->connector);
|
||||
|
||||
seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n",
|
||||
sb->s_dev, mflags, mark->mask, mark->ignored_mask);
|
||||
sb->s_dev, mflags, mark->mask, mark->ignore_mask);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +135,8 @@ void fanotify_show_fdinfo(struct seq_file *m, struct file *f)
|
||||
struct fsnotify_group *group = f->private_data;
|
||||
|
||||
seq_printf(m, "fanotify flags:%x event-flags:%x\n",
|
||||
group->fanotify_data.flags, group->fanotify_data.f_flags);
|
||||
group->fanotify_data.flags & FANOTIFY_INIT_FLAGS,
|
||||
group->fanotify_data.f_flags);
|
||||
|
||||
show_fdinfo(m, f, fanotify_fdinfo);
|
||||
}
|
||||
|
@ -70,7 +70,6 @@ static void fsnotify_unmount_inodes(struct super_block *sb)
|
||||
spin_unlock(&inode->i_lock);
|
||||
spin_unlock(&sb->s_inode_list_lock);
|
||||
|
||||
if (iput_inode)
|
||||
iput(iput_inode);
|
||||
|
||||
/* for each watch, send FS_UNMOUNT and then remove it */
|
||||
@ -85,24 +84,23 @@ static void fsnotify_unmount_inodes(struct super_block *sb)
|
||||
}
|
||||
spin_unlock(&sb->s_inode_list_lock);
|
||||
|
||||
if (iput_inode)
|
||||
iput(iput_inode);
|
||||
/* Wait for outstanding inode references from connectors */
|
||||
wait_var_event(&sb->s_fsnotify_inode_refs,
|
||||
!atomic_long_read(&sb->s_fsnotify_inode_refs));
|
||||
}
|
||||
|
||||
void fsnotify_sb_delete(struct super_block *sb)
|
||||
{
|
||||
fsnotify_unmount_inodes(sb);
|
||||
fsnotify_clear_marks_by_sb(sb);
|
||||
/* Wait for outstanding object references from connectors */
|
||||
wait_var_event(&sb->s_fsnotify_connectors,
|
||||
!atomic_long_read(&sb->s_fsnotify_connectors));
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an inode, first check if we care what happens to our children. Inotify
|
||||
* and dnotify both tell their parents about events. If we care about any event
|
||||
* on a child we run all of our children and set a dentry flag saying that the
|
||||
* parent cares. Thus when an event happens on a child it can quickly tell if
|
||||
* parent cares. Thus when an event happens on a child it can quickly tell
|
||||
* if there is a need to find a parent and send the event to the parent.
|
||||
*/
|
||||
void __fsnotify_update_child_dentry_flags(struct inode *inode)
|
||||
@ -252,7 +250,10 @@ static int fsnotify_handle_inode_event(struct fsnotify_group *group,
|
||||
if (WARN_ON_ONCE(!ops->handle_inode_event))
|
||||
return 0;
|
||||
|
||||
if ((inode_mark->mask & FS_EXCL_UNLINK) &&
|
||||
if (WARN_ON_ONCE(!inode && !dir))
|
||||
return 0;
|
||||
|
||||
if ((inode_mark->flags & FSNOTIFY_MARK_FLAG_EXCL_UNLINK) &&
|
||||
path && d_unlinked(path->dentry))
|
||||
return 0;
|
||||
|
||||
@ -276,22 +277,27 @@ static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
|
||||
WARN_ON_ONCE(fsnotify_iter_vfsmount_mark(iter_info)))
|
||||
return 0;
|
||||
|
||||
if (parent_mark) {
|
||||
/*
|
||||
* parent_mark indicates that the parent inode is watching
|
||||
* children and interested in this event, which is an event
|
||||
* possible on child. But is *this mark* watching children and
|
||||
* interested in this event?
|
||||
* For FS_RENAME, 'dir' is old dir and 'data' is new dentry.
|
||||
* The only ->handle_inode_event() backend that supports FS_RENAME is
|
||||
* dnotify, where it means file was renamed within same parent.
|
||||
*/
|
||||
if (parent_mark->mask & FS_EVENT_ON_CHILD) {
|
||||
if (mask & FS_RENAME) {
|
||||
struct dentry *moved = fsnotify_data_dentry(data, data_type);
|
||||
|
||||
if (dir != moved->d_parent->d_inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parent_mark) {
|
||||
ret = fsnotify_handle_inode_event(group, parent_mark, mask,
|
||||
data, data_type, dir, name, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!inode_mark)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mask & FS_EVENT_ON_CHILD) {
|
||||
/*
|
||||
@ -318,42 +324,36 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
|
||||
struct fsnotify_group *group = NULL;
|
||||
__u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS);
|
||||
__u32 marks_mask = 0;
|
||||
__u32 marks_ignored_mask = 0;
|
||||
__u32 marks_ignore_mask = 0;
|
||||
bool is_dir = mask & FS_ISDIR;
|
||||
struct fsnotify_mark *mark;
|
||||
int type;
|
||||
|
||||
if (WARN_ON(!iter_info->report_mask))
|
||||
if (!iter_info->report_mask)
|
||||
return 0;
|
||||
|
||||
/* clear ignored on inode modification */
|
||||
if (mask & FS_MODIFY) {
|
||||
fsnotify_foreach_obj_type(type) {
|
||||
if (!fsnotify_iter_should_report_type(iter_info, type))
|
||||
continue;
|
||||
mark = iter_info->marks[type];
|
||||
if (mark &&
|
||||
!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
|
||||
mark->ignored_mask = 0;
|
||||
fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
|
||||
if (!(mark->flags &
|
||||
FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
|
||||
mark->ignore_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fsnotify_foreach_obj_type(type) {
|
||||
if (!fsnotify_iter_should_report_type(iter_info, type))
|
||||
continue;
|
||||
mark = iter_info->marks[type];
|
||||
/* does the object mark tell us to do something? */
|
||||
if (mark) {
|
||||
/* Are any of the group marks interested in this event? */
|
||||
fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
|
||||
group = mark->group;
|
||||
marks_mask |= mark->mask;
|
||||
marks_ignored_mask |= mark->ignored_mask;
|
||||
}
|
||||
marks_ignore_mask |=
|
||||
fsnotify_effective_ignore_mask(mark, is_dir, type);
|
||||
}
|
||||
|
||||
pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignored_mask=%x data=%p data_type=%d dir=%p cookie=%d\n",
|
||||
__func__, group, mask, marks_mask, marks_ignored_mask,
|
||||
pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignore_mask=%x data=%p data_type=%d dir=%p cookie=%d\n",
|
||||
__func__, group, mask, marks_mask, marks_ignore_mask,
|
||||
data, data_type, dir, cookie);
|
||||
|
||||
if (!(test_mask & marks_mask & ~marks_ignored_mask))
|
||||
if (!(test_mask & marks_mask & ~marks_ignore_mask))
|
||||
return 0;
|
||||
|
||||
if (group->ops->handle_event) {
|
||||
@ -390,11 +390,11 @@ static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark)
|
||||
|
||||
/*
|
||||
* iter_info is a multi head priority queue of marks.
|
||||
* Pick a subset of marks from queue heads, all with the
|
||||
* same group and set the report_mask for selected subset.
|
||||
* Returns the report_mask of the selected subset.
|
||||
* Pick a subset of marks from queue heads, all with the same group
|
||||
* and set the report_mask to a subset of the selected marks.
|
||||
* Returns false if there are no more groups to iterate.
|
||||
*/
|
||||
static unsigned int fsnotify_iter_select_report_types(
|
||||
static bool fsnotify_iter_select_report_types(
|
||||
struct fsnotify_iter_info *iter_info)
|
||||
{
|
||||
struct fsnotify_group *max_prio_group = NULL;
|
||||
@ -402,7 +402,7 @@ static unsigned int fsnotify_iter_select_report_types(
|
||||
int type;
|
||||
|
||||
/* Choose max prio group among groups of all queue heads */
|
||||
fsnotify_foreach_obj_type(type) {
|
||||
fsnotify_foreach_iter_type(type) {
|
||||
mark = iter_info->marks[type];
|
||||
if (mark &&
|
||||
fsnotify_compare_groups(max_prio_group, mark->group) > 0)
|
||||
@ -410,30 +410,49 @@ static unsigned int fsnotify_iter_select_report_types(
|
||||
}
|
||||
|
||||
if (!max_prio_group)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
/* Set the report mask for marks from same group as max prio group */
|
||||
iter_info->current_group = max_prio_group;
|
||||
iter_info->report_mask = 0;
|
||||
fsnotify_foreach_obj_type(type) {
|
||||
fsnotify_foreach_iter_type(type) {
|
||||
mark = iter_info->marks[type];
|
||||
if (mark &&
|
||||
fsnotify_compare_groups(max_prio_group, mark->group) == 0)
|
||||
if (mark && mark->group == iter_info->current_group) {
|
||||
/*
|
||||
* FSNOTIFY_ITER_TYPE_PARENT indicates that this inode
|
||||
* is watching children and interested in this event,
|
||||
* which is an event possible on child.
|
||||
* But is *this mark* watching children?
|
||||
*/
|
||||
if (type == FSNOTIFY_ITER_TYPE_PARENT &&
|
||||
!(mark->mask & FS_EVENT_ON_CHILD) &&
|
||||
!(fsnotify_ignore_mask(mark) & FS_EVENT_ON_CHILD))
|
||||
continue;
|
||||
|
||||
fsnotify_iter_set_report_type(iter_info, type);
|
||||
}
|
||||
}
|
||||
|
||||
return iter_info->report_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pop from iter_info multi head queue, the marks that were iterated in the
|
||||
* Pop from iter_info multi head queue, the marks that belong to the group of
|
||||
* current iteration step.
|
||||
*/
|
||||
static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
|
||||
{
|
||||
struct fsnotify_mark *mark;
|
||||
int type;
|
||||
|
||||
fsnotify_foreach_obj_type(type) {
|
||||
if (fsnotify_iter_should_report_type(iter_info, type))
|
||||
/*
|
||||
* We cannot use fsnotify_foreach_iter_mark_type() here because we
|
||||
* may need to advance a mark of type X that belongs to current_group
|
||||
* but was not selected for reporting.
|
||||
*/
|
||||
fsnotify_foreach_iter_type(type) {
|
||||
mark = iter_info->marks[type];
|
||||
if (mark && mark->group == iter_info->current_group)
|
||||
iter_info->marks[type] =
|
||||
fsnotify_next_mark(iter_info->marks[type]);
|
||||
}
|
||||
@ -455,18 +474,20 @@ static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
|
||||
* @file_name is relative to
|
||||
* @file_name: optional file name associated with event
|
||||
* @inode: optional inode associated with event -
|
||||
* either @dir or @inode must be non-NULL.
|
||||
* if both are non-NULL event may be reported to both.
|
||||
* If @dir and @inode are both non-NULL, event may be
|
||||
* reported to both.
|
||||
* @cookie: inotify rename cookie
|
||||
*/
|
||||
int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
|
||||
const struct qstr *file_name, struct inode *inode, u32 cookie)
|
||||
{
|
||||
const struct path *path = fsnotify_data_path(data, data_type);
|
||||
struct super_block *sb = fsnotify_data_sb(data, data_type);
|
||||
struct fsnotify_iter_info iter_info = {};
|
||||
struct super_block *sb;
|
||||
struct mount *mnt = NULL;
|
||||
struct inode *parent = NULL;
|
||||
struct inode *inode2 = NULL;
|
||||
struct dentry *moved;
|
||||
int inode2_type;
|
||||
int ret = 0;
|
||||
__u32 test_mask, marks_mask;
|
||||
|
||||
@ -476,14 +497,20 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
|
||||
if (!inode) {
|
||||
/* Dirent event - report on TYPE_INODE to dir */
|
||||
inode = dir;
|
||||
/* For FS_RENAME, inode is old_dir and inode2 is new_dir */
|
||||
if (mask & FS_RENAME) {
|
||||
moved = fsnotify_data_dentry(data, data_type);
|
||||
inode2 = moved->d_parent->d_inode;
|
||||
inode2_type = FSNOTIFY_ITER_TYPE_INODE2;
|
||||
}
|
||||
} else if (mask & FS_EVENT_ON_CHILD) {
|
||||
/*
|
||||
* Event on child - report on TYPE_PARENT to dir if it is
|
||||
* watching children and on TYPE_INODE to child.
|
||||
*/
|
||||
parent = dir;
|
||||
inode2 = dir;
|
||||
inode2_type = FSNOTIFY_ITER_TYPE_PARENT;
|
||||
}
|
||||
sb = inode->i_sb;
|
||||
|
||||
/*
|
||||
* Optimization: srcu_read_lock() has a memory barrier which can
|
||||
@ -495,7 +522,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
|
||||
if (!sb->s_fsnotify_marks &&
|
||||
(!mnt || !mnt->mnt_fsnotify_marks) &&
|
||||
(!inode || !inode->i_fsnotify_marks) &&
|
||||
(!parent || !parent->i_fsnotify_marks))
|
||||
(!inode2 || !inode2->i_fsnotify_marks))
|
||||
return 0;
|
||||
|
||||
marks_mask = sb->s_fsnotify_mask;
|
||||
@ -503,33 +530,35 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
|
||||
marks_mask |= mnt->mnt_fsnotify_mask;
|
||||
if (inode)
|
||||
marks_mask |= inode->i_fsnotify_mask;
|
||||
if (parent)
|
||||
marks_mask |= parent->i_fsnotify_mask;
|
||||
if (inode2)
|
||||
marks_mask |= inode2->i_fsnotify_mask;
|
||||
|
||||
|
||||
/*
|
||||
* if this is a modify event we may need to clear the ignored masks
|
||||
* otherwise return if none of the marks care about this type of event.
|
||||
* If this is a modify event we may need to clear some ignore masks.
|
||||
* In that case, the object with ignore masks will have the FS_MODIFY
|
||||
* event in its mask.
|
||||
* Otherwise, return if none of the marks care about this type of event.
|
||||
*/
|
||||
test_mask = (mask & ALL_FSNOTIFY_EVENTS);
|
||||
if (!(mask & FS_MODIFY) && !(test_mask & marks_mask))
|
||||
if (!(test_mask & marks_mask))
|
||||
return 0;
|
||||
|
||||
iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
|
||||
|
||||
iter_info.marks[FSNOTIFY_OBJ_TYPE_SB] =
|
||||
iter_info.marks[FSNOTIFY_ITER_TYPE_SB] =
|
||||
fsnotify_first_mark(&sb->s_fsnotify_marks);
|
||||
if (mnt) {
|
||||
iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
|
||||
iter_info.marks[FSNOTIFY_ITER_TYPE_VFSMOUNT] =
|
||||
fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
|
||||
}
|
||||
if (inode) {
|
||||
iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
|
||||
iter_info.marks[FSNOTIFY_ITER_TYPE_INODE] =
|
||||
fsnotify_first_mark(&inode->i_fsnotify_marks);
|
||||
}
|
||||
if (parent) {
|
||||
iter_info.marks[FSNOTIFY_OBJ_TYPE_PARENT] =
|
||||
fsnotify_first_mark(&parent->i_fsnotify_marks);
|
||||
if (inode2) {
|
||||
iter_info.marks[inode2_type] =
|
||||
fsnotify_first_mark(&inode2->i_fsnotify_marks);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -558,7 +587,7 @@ static __init int fsnotify_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 25);
|
||||
BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 23);
|
||||
|
||||
ret = init_srcu_struct(&fsnotify_mark_srcu);
|
||||
if (ret)
|
||||
|
@ -27,6 +27,21 @@ static inline struct super_block *fsnotify_conn_sb(
|
||||
return container_of(conn->obj, struct super_block, s_fsnotify_marks);
|
||||
}
|
||||
|
||||
static inline struct super_block *fsnotify_connector_sb(
|
||||
struct fsnotify_mark_connector *conn)
|
||||
{
|
||||
switch (conn->type) {
|
||||
case FSNOTIFY_OBJ_TYPE_INODE:
|
||||
return fsnotify_conn_inode(conn)->i_sb;
|
||||
case FSNOTIFY_OBJ_TYPE_VFSMOUNT:
|
||||
return fsnotify_conn_mount(conn)->mnt.mnt_sb;
|
||||
case FSNOTIFY_OBJ_TYPE_SB:
|
||||
return fsnotify_conn_sb(conn);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* destroy all events sitting in this groups notification queue */
|
||||
extern void fsnotify_flush_notify(struct fsnotify_group *group);
|
||||
|
||||
@ -61,10 +76,6 @@ static inline void fsnotify_clear_marks_by_sb(struct super_block *sb)
|
||||
*/
|
||||
extern void __fsnotify_update_child_dentry_flags(struct inode *inode);
|
||||
|
||||
/* allocate and destroy and event holder to attach events to notification/access queues */
|
||||
extern struct fsnotify_event_holder *fsnotify_alloc_event_holder(void);
|
||||
extern void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder);
|
||||
|
||||
extern struct kmem_cache *fsnotify_mark_connector_cachep;
|
||||
|
||||
#endif /* __FS_NOTIFY_FSNOTIFY_H_ */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user