if we see NFS4ERR_NO_GRACE from recovery operations, it means we lost our state due to a lease expiration rather than a server reboot. in this case, it's possible that conflicting locks were granted to other clients, so we have to try normal OPEN/LOCK operations to recover our state. because they're sent during recovery, nfs41_open() and nfs41_lock() take a new 'bool_t try_recovery' argument so we can avoid recursion
if these operations fail due to conflicting locks, we have no choice but to return errors to the application. using a stateid that was revoked due to lease expiration results in NFS4ERR_EXPIRED, and we map this error to ERROR_FILE_INVALID: The volume for a file has been externally altered so that the opened file is no longer valid.
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
nfs41_open_state maintains a list of outstanding byte-range locks by calling open_lock_add() and open_lock_remove() in lock.c
during client state recovery, after reclaiming each OPEN stateid, send LOCK requests with reclaim=TRUE for each lock it owns, and update the open's lock stateid with the result
added 'bool_t reclaim' argument to nfs41_lock(); when set, compound_encode_send_decode() is called with try_recovery=FALSE to avoid recursive recovery
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
avoid the recursive case where state recovery operations (OPEN for reclaim and RECLAIM_COMPLETE) return BADSESSION, which kicks off another round of recovery
added a 'bool_t try_recovery' argument to compound_encode_send_decode() in place of its unused 'bufsize_in' and 'bufsize_out'. when try_recovery=FALSE, return BADSESSION/STALE_CLIENTID errors instead of attempting recovery. nfs41_open_reclaim(), nfs41_reclaim_complete(), and nfs41_destroy_session() now pass try_recovery=FALSE
during state recovery, we can now check the return values of nfs41_open_reclaim() and nfs41_reclaim_complete() for BADSESSION, and use a goto to restart session recovery
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
moved recovery-related fields into struct nfs41_client.recovery. now uses a com
bination of CRITICAL_SECTION and CONDITION_VARIABLE for use with SleepConditionV
ariableCS()
renamed check_renew_in_progress() to recovery_start_or_wait(), and fixed the loc
king so that we atomically check/set in_recovery
when recovery is finished (including error conditions), call recovery_finish() t
o reset the recovery status and wake any waiting threads
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
consider an operation that takes a stateid, and results in a BADSESSION error due to server reboot. we'll recover the client and session, and send OPENs to reclaim all of the client's state. but after recovery, we'll resend the original operation with the original stateid, and this will result in a STALE_STATEID error
we handle this by making use of the information in stateid_arg. if we determine that stateid_arg.stateid is different from the nfs41_open_state's stateid, we copy the new stateid into stateid_arg.stateid and retry
note that if another thread is in recovery, it hasn't finished reclaiming its open state yet; so we wait on recovery to finish before comparing the stateids
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
operations that require a stateid now take stateid_arg for recovery information. these operations include close, setattr, lock/unlock, layoutget, and read/write (including pnfs)
nfs41_open_stateid_arg() locks nfs41_open_state and copies its stateid into a stateid_arg
nfs41_lock_stateid_arg() locks nfs41_open_state.last_lock and copies its stateid into a stateid_arg; if there is no lock state, it falls back to nfs41_open_stateid_arg()
pnfs_read/write() now take nfs41_open_state so they can generate stateid_args
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
after the client and session have been recovered, loop through the client's list of open state, calling nfs41_open_reclaim() and updating the stateid on success
nfs41_open_state saves the share_access and share_deny fields from the initial open, for use with nfs41_open_reclaim()
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
open state is added to the client's list on a successful call to nfs41_open(), and removed from the list on nfs41_close() regardless of success
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
to determine that the daemon has restarted -- rather that daemon is receiving upcalls from the kernel that were processed by the old instance of the daemon -- add a version to the upcall mechanism.
when daemon starts up it generates a version number (just a timestamp). it passes this value to the driver on start up via "start_ioctl" downcall. the driver saves that value in its device extensions. it uses that value in the mount and shtudown upcalls.
when daemon replies to the mount command it again sends its version as a part of the reply. this reply is stored in driver;s netroot extensions. the driver uses netroot's value in each upcall to the daemon.
if the daemon receives an upcall for an operation where the included version does not equal to the its current version, it fails the upcall (error_code=116).
a restart of the daemon would change driver's device extension value which the driver will then use in the new mount upcalls that would establish new sessions. then the correct daemon version would be returned as a part of the mount downcalled and saved in the netroot.
struct idmap_context contains configuration data (struct idmap_config), a cache for users, and a cache for groups. idmap_context is declared in idmap.c, and only available as an opaque pointer (nfs41_idmapper) elsewhere. similarly, Winldap.h is only included by idmap.c, and not needed elsewhere
nfs41_idmap_create() allocates the idmap_context, loads the configuration from file, and calls ldap_init(). it does not call ldap_connect(); we'll still be able to start the daemon if ldap isn't configured, or the ldap server is down. calling ldap_connect() is optional, as any ldap operation that requires a connection will establish it internally. this behavior, along with the LDAP_OPT_AUTO_RECONNECT option (defaults to on), means that we shouldn't have to maintain a separate connection for each thread
nfs41_idmap_*() functions return windows errors codes. LDAP_RETCODEs are mapped to windows errors with LdapMapErrorToWin32()
the user and group caches share a common generic interface in struct idmap_cache, which uses a linked list for storage, and protects access with a SRWLOCK. expiration of cache entries can be adjusted by the config option 'cache_ttl'
struct config_option g_options[] is a table of available config options and their default values. this patch adds a 'ms-nfs41-idmap.conf' file with all possible options set to default values, and commented out. the daemon expects to find this file under c:\etc\, and won't start if it can't be opened or parsed
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
very similar to the issue with nfs41_open_state, an abandoned upcall could outlive its mount. to prevent their nfs41_root from being freed, upcalls need to hold a reference until they're finished. this also keeps all of its clients/sessions/rpc connections alive
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
added call to upcall_cleanup() after both upcall_marshall() and upcall_cancel()
individual upcall operations define their nfs41_upcall_op structs locally, instead of putting tons of function prototypes in upcall.c
made the upcall_marshall() function optional; most marshall functions are noops
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
fixes a memory leak that occurs when a readdir loop doesn't complete, since the cookie was only freed on the last readdir upcall. by storing the cookie with nfs41_open_state, we can avoid passing the cookie to the driver and back, and not worry about having to free it separately
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
while following symlinks on open, after we break from the loop we need to respect that nfs41_lookup could have returned some kind of error value. thus only return error_reparse if nfs41_lookup returned success or file_not_found error.
similarly in after calling nfs41_symlink_follow() only set the reparse_error if function was successful.
fixed a few cases of warning 4242: possible loss of data
wincrypt.h appears to come with windows.h in later versions of the ddk, but nfs41_client.c fails to compile in WDK 6001 without #include <wincrypt.h>
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
avoid calling map_disposition_2_nfsopen() for this, because FILE_CREATE->ERROR_FILE_EXISTS is the only case we care about
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
/* msdn: There is a maximum of 31 reparse points (and
* therefore symbolic links) allowed in a particular path. */
#define NFS41_MAX_SYMLINK_DEPTH 31
also added checks for the return value of nfs41_symlink_target() on open/link/rename
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>