diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/ntvfs/posix/pvfs_rename.c | 105 |
1 files changed, 103 insertions, 2 deletions
diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c index 5693e79314..e3192370cc 100644 --- a/source4/ntvfs/posix/pvfs_rename.c +++ b/source4/ntvfs/posix/pvfs_rename.c @@ -181,6 +181,84 @@ static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx, } /* + retry an rename after a sharing violation +*/ +static void pvfs_retry_rename(struct pvfs_odb_retry *r, + struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + void *_io, + void *private_data, + enum pvfs_wait_notice reason) +{ + union smb_rename *io = talloc_get_type(_io, union smb_rename); + NTSTATUS status = NT_STATUS_INTERNAL_ERROR; + + talloc_free(r); + + switch (reason) { + case PVFS_WAIT_CANCEL: +/*TODO*/ + status = NT_STATUS_CANCELLED; + break; + case PVFS_WAIT_TIMEOUT: + /* if it timed out, then give the failure + immediately */ +/*TODO*/ + status = NT_STATUS_SHARING_VIOLATION; + break; + case PVFS_WAIT_EVENT: + + /* try the open again, which could trigger another retry setup + if it wants to, so we have to unmark the async flag so we + will know if it does a second async reply */ + req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC; + + status = pvfs_rename(ntvfs, req, io); + if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { + /* the 2nd try also replied async, so we don't send + the reply yet */ + return; + } + + /* re-mark it async, just in case someone up the chain does + paranoid checking */ + req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; + break; + } + + /* send the reply up the chain */ + req->async_states->status = status; + req->async_states->send_fn(req); +} + +/* + setup for a rename retry after a sharing violation + or a non granted oplock +*/ +static NTSTATUS pvfs_rename_setup_retry(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + union smb_rename *io, + struct odb_lock *lck, + NTSTATUS status) +{ + struct pvfs_state *pvfs = ntvfs->private_data; + struct timeval end_time; + + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + end_time = timeval_add(&req->statistics.request_time, + 0, pvfs->sharing_violation_delay); + } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) { + end_time = timeval_add(&req->statistics.request_time, + pvfs->oplock_break_timeout, 0); + } else { + return NT_STATUS_INTERNAL_ERROR; + } + + return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL, + pvfs_retry_rename); +} + +/* rename one file from a wildcard set */ static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, @@ -354,8 +432,19 @@ static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs, } status = pvfs_can_rename(pvfs, req, name1, &lck); + /* + * on a sharing violation we need to retry when the file is closed by + * the other user, or after 1 second + * on a non granted oplock we need to retry when the file is closed by + * the other user, or after 30 seconds + */ + if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) || + NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) && + (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { + return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status); + } + if (!NT_STATUS_IS_OK(status)) { - talloc_free(lck); return status; } @@ -377,6 +466,7 @@ static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs, struct pvfs_state *pvfs = ntvfs->private_data; NTSTATUS status; struct pvfs_filename *name1, *name2; + struct odb_lock *lck = NULL; switch (ren->ntrename.in.flags) { case RENAME_FLAG_RENAME: @@ -422,7 +512,18 @@ static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs, return status; } - status = pvfs_can_rename(pvfs, req, name1, NULL); + status = pvfs_can_rename(pvfs, req, name1, &lck); + /* + * on a sharing violation we need to retry when the file is closed by + * the other user, or after 1 second + * on a non granted oplock we need to retry when the file is closed by + * the other user, or after 30 seconds + */ + if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) || + NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) && + (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { + return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status); + } if (!NT_STATUS_IS_OK(status)) { return status; } |