recovery: use normal OPEN/LOCK on ERR_NO_GRACE

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>
This commit is contained in:
Casey Bodley 2010-11-30 13:10:30 -05:00 committed by unknown
parent 222c1bf020
commit 0a309c4350
8 changed files with 46 additions and 17 deletions

View file

@ -163,7 +163,7 @@ static int handle_lock(nfs41_upcall *upcall)
nfs41_lock_stateid_arg(state, &stateid); nfs41_lock_stateid_arg(state, &stateid);
status = nfs41_lock(state->session, &state->file, &state->owner, status = nfs41_lock(state->session, &state->file, &state->owner,
type, args->offset, args->length, 0, &stateid); type, args->offset, args->length, FALSE, TRUE, &stateid);
if (status) { if (status) {
dprintf(LKLVL, "nfs41_lock failed with %s\n", dprintf(LKLVL, "nfs41_lock failed with %s\n",
nfs_error_string(status)); nfs_error_string(status));

View file

@ -135,13 +135,24 @@ static int recover_open(
/* reclaim the open stateid */ /* reclaim the open stateid */
status = nfs41_open_reclaim(session, &open->parent, &open->file, status = nfs41_open_reclaim(session, &open->parent, &open->file,
&open->owner, open->share_access, open->share_deny, &stateid.stateid); &open->owner, open->share_access, open->share_deny, &stateid.stateid);
if (status == NFS4_OK) {
/* update the open stateid on success */
memcpy(&open->stateid, &stateid.stateid, sizeof(stateid4));
} else if (status == NFS4ERR_NO_GRACE) {
dprintf(1, "not in grace period, retrying a normal open\n");
status = nfs41_open(session, open->share_access,
open->share_deny, OPEN4_NOCREATE, 0, FALSE, open, NULL);
/* update the stateid arg with the new open->stateid */
memcpy(&stateid.stateid, &open->stateid, sizeof(stateid4));
}
if (status) if (status)
goto out; goto out;
AcquireSRWLockExclusive(&open->lock); AcquireSRWLockExclusive(&open->lock);
/* update the open stateid */
memcpy(&open->stateid, &stateid.stateid, sizeof(stateid4));
stateid.type = STATEID_OPEN; stateid.type = STATEID_OPEN;
stateid.open = open; stateid.open = open;
@ -149,7 +160,12 @@ static int recover_open(
list_for_each(entry, &open->locks.list) { list_for_each(entry, &open->locks.list) {
lock = list_container(entry, nfs41_lock_state, open_entry); lock = list_container(entry, nfs41_lock_state, open_entry);
status = nfs41_lock(session, &open->file, &open->owner, status = nfs41_lock(session, &open->file, &open->owner,
lock->type, lock->offset, lock->length, TRUE, &stateid); lock->type, lock->offset, lock->length, TRUE, FALSE, &stateid);
if (status == NFS4ERR_NO_GRACE) {
dprintf(1, "not in grace period, retrying a normal lock\n");
status = nfs41_lock(session, &open->file, &open->owner,
lock->type, lock->offset, lock->length, FALSE, FALSE, &stateid);
}
if (status == NFS4ERR_BADSESSION) if (status == NFS4ERR_BADSESSION)
break; break;
} }
@ -306,12 +322,15 @@ restart_recovery:
recovery_finish(session->client); recovery_finish(session->client);
goto do_retry; goto do_retry;
case NFS4ERR_STALE_STATEID: case NFS4ERR_EXPIRED: /* revoked by lease expiration */
case NFS4ERR_STALE_STATEID: /* server reboot */
if (compound->args.argarray[0].op == OP_SEQUENCE) { if (compound->args.argarray[0].op == OP_SEQUENCE) {
nfs41_sequence_args *seq = (nfs41_sequence_args*) nfs41_sequence_args *seq = (nfs41_sequence_args*)
compound->args.argarray[0].arg; compound->args.argarray[0].arg;
nfs41_session_free_slot(session, seq->sa_slotid); nfs41_session_free_slot(session, seq->sa_slotid);
} }
if (!try_recovery)
goto out;
{ {
nfs_argop4 *argop = &compound->args.argarray[ nfs_argop4 *argop = &compound->args.argarray[
compound->res.resarray_count-1]; compound->res.resarray_count-1];

View file

@ -295,8 +295,9 @@ int nfs41_open(
IN uint32_t deny, IN uint32_t deny,
IN uint32_t create, IN uint32_t create,
IN uint32_t mode, IN uint32_t mode,
IN bool_t try_recovery,
IN OUT nfs41_open_state *state, IN OUT nfs41_open_state *state,
OUT nfs41_file_info *info) OUT OPTIONAL nfs41_file_info *info)
{ {
int status; int status;
nfs41_compound compound; nfs41_compound compound;
@ -314,7 +315,10 @@ int nfs41_open(
nfs41_getattr_res getattr_res, pgetattr_res; nfs41_getattr_res getattr_res, pgetattr_res;
nfs41_savefh_res savefh_res; nfs41_savefh_res savefh_res;
nfs41_restorefh_res restorefh_res; nfs41_restorefh_res restorefh_res;
nfs41_file_info dir_info; nfs41_file_info tmp_info, dir_info;
if (info == NULL)
info = &tmp_info;
init_getattr_request(&attr_request); init_getattr_request(&attr_request);
attr_request.arr[0] |= FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID; attr_request.arr[0] |= FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID;
@ -367,7 +371,7 @@ int nfs41_open(
pgetattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT; pgetattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
pgetattr_res.info = &dir_info; pgetattr_res.info = &dir_info;
status = compound_encode_send_decode(session, &compound, TRUE); status = compound_encode_send_decode(session, &compound, try_recovery);
if (status) if (status)
goto out; goto out;
@ -867,6 +871,7 @@ int nfs41_lock(
IN uint64_t offset, IN uint64_t offset,
IN uint64_t length, IN uint64_t length,
IN bool_t reclaim, IN bool_t reclaim,
IN bool_t try_recovery,
IN OUT stateid_arg *stateid) IN OUT stateid_arg *stateid)
{ {
int status; int status;
@ -910,7 +915,7 @@ int nfs41_lock(
lock_res.u.resok4.lock_stateid = &stateid->stateid; lock_res.u.resok4.lock_stateid = &stateid->stateid;
lock_res.u.denied.owner.owner_len = NFS4_OPAQUE_LIMIT; lock_res.u.denied.owner.owner_len = NFS4_OPAQUE_LIMIT;
status = compound_encode_send_decode(session, &compound, !reclaim); status = compound_encode_send_decode(session, &compound, try_recovery);
if (status) if (status)
goto out; goto out;

View file

@ -915,8 +915,9 @@ int nfs41_open(
IN uint32_t deny, IN uint32_t deny,
IN uint32_t create, IN uint32_t create,
IN uint32_t mode, IN uint32_t mode,
IN bool_t try_recovery,
IN OUT nfs41_open_state *state, IN OUT nfs41_open_state *state,
OUT nfs41_file_info *info); OUT OPTIONAL nfs41_file_info *info);
int nfs41_open_reclaim( int nfs41_open_reclaim(
IN nfs41_session *session, IN nfs41_session *session,
@ -976,6 +977,7 @@ int nfs41_lock(
IN uint64_t offset, IN uint64_t offset,
IN uint64_t length, IN uint64_t length,
IN bool_t reclaim, IN bool_t reclaim,
IN bool_t try_recovery,
IN OUT stateid_arg *stateid); IN OUT stateid_arg *stateid);
int nfs41_unlock( int nfs41_unlock(

View file

@ -440,7 +440,7 @@ static int handle_open(nfs41_upcall *upcall)
args->created = status == NFS4_OK ? TRUE : FALSE; args->created = status == NFS4_OK ? TRUE : FALSE;
} else { } else {
status = nfs41_open(state->session, state->share_access, status = nfs41_open(state->session, state->share_access,
state->share_deny, create, args->mode, state, &info); state->share_deny, create, args->mode, TRUE, state, &info);
if (status == NFS4_OK) { if (status == NFS4_OK) {
/* add to the client's list of state for recovery */ /* add to the client's list of state for recovery */

View file

@ -440,14 +440,14 @@ static int handle_setattr(nfs41_upcall *upcall)
case FileEndOfFileInformation: case FileEndOfFileInformation:
if (!state->do_close) { if (!state->do_close) {
// get a stateid // get a stateid
uint32_t allow = 0, deny = 0;
StringCchPrintfA((LPSTR)state->owner.owner, NFS4_OPAQUE_LIMIT, StringCchPrintfA((LPSTR)state->owner.owner, NFS4_OPAQUE_LIMIT,
"%u", args->open_owner_id); "%u", args->open_owner_id);
state->owner.owner_len = (uint32_t)strlen( state->owner.owner_len = (uint32_t)strlen(
(const char*)state->owner.owner); (const char*)state->owner.owner);
map_access_2_allowdeny(args->access_mask, args->access_mode, &allow, &deny); map_access_2_allowdeny(args->access_mask, args->access_mode,
status = nfs41_open(state->session, allow, deny, &state->share_access, &state->share_deny);
OPEN4_NOCREATE, 0, state, NULL); status = nfs41_open(state->session, state->share_access,
state->share_deny, OPEN4_NOCREATE, 0, TRUE, state, NULL);
if (status) { if (status) {
dprintf(1, "nfs41_open() failed with %s\n", nfs_error_string(status)); dprintf(1, "nfs41_open() failed with %s\n", nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND); status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND);

View file

@ -316,6 +316,7 @@ int nfs_to_windows_error(int status, int default_error)
case NFS4ERR_SYMLINK: case NFS4ERR_SYMLINK:
case NFS4ERR_WRONG_TYPE: return ERROR_INVALID_PARAMETER; case NFS4ERR_WRONG_TYPE: return ERROR_INVALID_PARAMETER;
case NFS4ERR_EXPIRED:
case NFS4ERR_NOFILEHANDLE: case NFS4ERR_NOFILEHANDLE:
case NFS4ERR_OLD_STATEID: case NFS4ERR_OLD_STATEID:
case NFS4ERR_BAD_STATEID: case NFS4ERR_BAD_STATEID:

View file

@ -2987,6 +2987,7 @@ static NTSTATUS map_close_errors(DWORD status)
case NO_ERROR: return STATUS_SUCCESS; case NO_ERROR: return STATUS_SUCCESS;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED; case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_NOT_EMPTY: return STATUS_DIRECTORY_NOT_EMPTY; case ERROR_NOT_EMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
default: default:
print_error("failed to map windows error %d to NTSTATUS; " print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_INTERNAL_ERROR\n", status); "defaulting to STATUS_INTERNAL_ERROR\n", status);
@ -4021,7 +4022,7 @@ static NTSTATUS map_readwrite_errors(DWORD status)
switch (status) { switch (status) {
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED; case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_HANDLE_EOF: return STATUS_END_OF_FILE; case ERROR_HANDLE_EOF: return STATUS_END_OF_FILE;
case ERROR_FILE_INVALID: return STATUS_FILE_CLOSED; case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER; case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
case ERROR_LOCK_VIOLATION: return STATUS_FILE_LOCK_CONFLICT; case ERROR_LOCK_VIOLATION: return STATUS_FILE_LOCK_CONFLICT;
case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED; case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED;
@ -4194,6 +4195,7 @@ static NTSTATUS map_lock_errors(DWORD status)
case ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: return STATUS_UNSUCCESSFUL; case ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: return STATUS_UNSUCCESSFUL;
case ERROR_OUTOFMEMORY: return STATUS_INSUFFICIENT_RESOURCES; case ERROR_OUTOFMEMORY: return STATUS_INSUFFICIENT_RESOURCES;
case ERROR_SHARING_VIOLATION: return STATUS_SHARING_VIOLATION; case ERROR_SHARING_VIOLATION: return STATUS_SHARING_VIOLATION;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
/* if we return ERROR_INVALID_PARAMETER, Windows translates that to /* if we return ERROR_INVALID_PARAMETER, Windows translates that to
* success!! */ * success!! */
case ERROR_INVALID_PARAMETER: return STATUS_LOCK_NOT_GRANTED; case ERROR_INVALID_PARAMETER: return STATUS_LOCK_NOT_GRANTED;