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:
parent
222c1bf020
commit
0a309c4350
8 changed files with 46 additions and 17 deletions
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -2986,7 +2986,8 @@ static NTSTATUS map_close_errors(DWORD status)
|
||||||
switch (status) {
|
switch (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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue