summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2008-03-12 14:02:11 +0100
committerStefan Metzmacher <metze@samba.org>2008-03-12 17:35:07 +0100
commit50243cdbbda8a1f14e56c684281a93614aab0103 (patch)
tree475d6b410581ce07a2f4112e64570764221502f4
parent454e9bed04011bcb184f20c2dd82f37255403227 (diff)
downloadsamba-50243cdbbda8a1f14e56c684281a93614aab0103.tar.gz
samba-50243cdbbda8a1f14e56c684281a93614aab0103.tar.bz2
samba-50243cdbbda8a1f14e56c684281a93614aab0103.zip
pvfs_open: retry pvfs_open() after an EGAIN or EWOULDBLOCK from open()
In case a unix application as an oplock or share mode on a file we need to retry periodicly as there's no way to get a notification from the kernel when the oplock is released. metze (This used to be commit 4d40f3a02643b4cdacee31f0b7bc9fc77cc9869a)
-rw-r--r--source4/ntvfs/posix/pvfs_open.c58
1 files changed, 54 insertions, 4 deletions
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
index 8a949daa87..1399f120a7 100644
--- a/source4/ntvfs/posix/pvfs_open.c
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -858,7 +858,13 @@ NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
/* setup a pending lock */
status = odb_open_file_pending(lck, r);
- if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
+ /*
+ * maybe only a unix application
+ * has the file open
+ */
+ data_blob_free(&r->odb_locking_key);
+ } else if (!NT_STATUS_IS_OK(status)) {
return status;
}
@@ -891,8 +897,14 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
enum pvfs_wait_notice reason)
{
union smb_open *io = talloc_get_type(_io, union smb_open);
+ struct timeval *final_timeout = NULL;
NTSTATUS status;
+ if (private_data) {
+ final_timeout = talloc_get_type(private_data,
+ struct timeval);
+ }
+
/* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
just a bug in their server, but we better do the same */
if (reason == PVFS_WAIT_CANCEL) {
@@ -900,6 +912,16 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
}
if (reason == PVFS_WAIT_TIMEOUT) {
+ if (final_timeout &&
+ !timeval_expired(final_timeout)) {
+ /*
+ * we need to retry periodictly
+ * after an EAGAIN as there's
+ * no way the kernel tell us
+ * an oplock is released.
+ */
+ goto retry;
+ }
/* if it timed out, then give the failure
immediately */
talloc_free(r);
@@ -908,6 +930,7 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
return;
}
+retry:
talloc_free(r);
/* try the open again, which could trigger another retry setup
@@ -1027,6 +1050,7 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
struct pvfs_state *pvfs = ntvfs->private_data;
NTSTATUS status;
struct timeval end_time;
+ struct timeval *final_timeout = NULL;
if (io->generic.in.create_options &
(NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
@@ -1047,12 +1071,28 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
} else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
end_time = timeval_add(&req->statistics.request_time,
pvfs->oplock_break_timeout, 0);
+ } else if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
+ /*
+ * we got EAGAIN which means a unix application
+ * has an oplock or share mode
+ *
+ * we retry every 4/5 of the sharing violation delay
+ * to see if the unix application
+ * has released the oplock or share mode.
+ */
+ final_timeout = talloc(req, struct timeval);
+ NT_STATUS_HAVE_NO_MEMORY(final_timeout);
+ *final_timeout = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout,
+ 0);
+ end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5);
+ end_time = timeval_min(final_timeout, &end_time);
} else {
return NT_STATUS_INTERNAL_ERROR;
}
- return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
- pvfs_retry_open_sharing);
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
+ final_timeout, pvfs_retry_open_sharing);
}
/*
@@ -1305,8 +1345,18 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
/* do the actual open */
fd = open(f->handle->name->full_name, flags | O_NONBLOCK);
if (fd == -1) {
+ status = pvfs_map_errno(f->pvfs, errno);
+
+ /*
+ * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
+ */
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
+ }
+
talloc_free(lck);
- return pvfs_map_errno(f->pvfs, errno);
+ return status;
}
f->handle->fd = fd;