summaryrefslogtreecommitdiff
path: root/source4/ntvfs/posix
diff options
context:
space:
mode:
Diffstat (limited to 'source4/ntvfs/posix')
-rw-r--r--source4/ntvfs/posix/config.mk1
-rw-r--r--source4/ntvfs/posix/pvfs_lock.c14
-rw-r--r--source4/ntvfs/posix/pvfs_open.c326
-rw-r--r--source4/ntvfs/posix/pvfs_oplock.c239
-rw-r--r--source4/ntvfs/posix/pvfs_setfileinfo.c116
-rw-r--r--source4/ntvfs/posix/pvfs_unlink.c94
-rw-r--r--source4/ntvfs/posix/pvfs_write.c5
-rw-r--r--source4/ntvfs/posix/vfs_posix.c4
-rw-r--r--source4/ntvfs/posix/vfs_posix.h22
9 files changed, 729 insertions, 92 deletions
diff --git a/source4/ntvfs/posix/config.mk b/source4/ntvfs/posix/config.mk
index 6879940337..88048c2af7 100644
--- a/source4/ntvfs/posix/config.mk
+++ b/source4/ntvfs/posix/config.mk
@@ -53,6 +53,7 @@ OBJ_FILES = \
pvfs_resolve.o \
pvfs_shortname.o \
pvfs_lock.o \
+ pvfs_oplock.o \
pvfs_wait.o \
pvfs_seek.o \
pvfs_ioctl.o \
diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c
index df85b2b775..baa92880f1 100644
--- a/source4/ntvfs/posix/pvfs_lock.c
+++ b/source4/ntvfs/posix/pvfs_lock.c
@@ -294,6 +294,10 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
return ntvfs_map_lock(ntvfs, req, lck);
}
+ if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
+ return pvfs_oplock_release(ntvfs, req, lck);
+ }
+
f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
@@ -303,6 +307,9 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
if (lck->lockx.in.timeout != 0 &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
pending = talloc(f, struct pvfs_pending_lock);
@@ -338,13 +345,6 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
}
- if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
- DEBUG(0,("received unexpected oplock break\n"));
- talloc_free(pending);
- return NT_STATUS_NOT_IMPLEMENTED;
- }
-
-
/* the unlocks happen first */
locks = lck->lockx.in.locks;
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
index 3a8b17ab16..12b70c00fd 100644
--- a/source4/ntvfs/posix/pvfs_open.c
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -268,6 +268,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
+ f->handle->oplock = NULL;
f->handle->sticky_write_time = false;
f->handle->open_completed = false;
@@ -296,9 +297,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
}
/* see if we are allowed to open at the same time as existing opens */
- status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
+ status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
- name->full_name, OPLOCK_NONE, NULL);
+ io->generic.in.open_disposition,
+ false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
@@ -349,9 +351,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
+ status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
- name->full_name, OPLOCK_NONE, NULL);
+ io->generic.in.open_disposition,
+ false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup_delete;
@@ -669,9 +672,10 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
oplock_level = OPLOCK_EXCLUSIVE;
}
- status = odb_open_file(lck, f->handle, name->stream_id,
+ status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
- name->full_name, oplock_level, &oplock_granted);
+ io->generic.in.open_disposition,
+ false, oplock_level, &oplock_granted);
talloc_free(lck);
if (!NT_STATUS_IS_OK(status)) {
/* bad news, we must have hit a race - we don't delete the file
@@ -681,9 +685,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
return status;
}
- if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
- oplock_granted = OPLOCK_BATCH;
- }
f->ntvfs = h;
f->pvfs = pvfs;
@@ -702,12 +703,23 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
+ f->handle->oplock = NULL;
f->handle->have_opendb_entry = true;
f->handle->sticky_write_time = false;
f->handle->open_completed = false;
DLIST_ADD(pvfs->files.list, f);
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_granted = OPLOCK_BATCH;
+ } else if (oplock_granted != OPLOCK_NONE) {
+ status = pvfs_setup_oplock(f, oplock_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ }
+
/* setup a destructor to avoid file descriptor leaks on
abnormal termination */
talloc_set_destructor(f, pvfs_fnum_destructor);
@@ -748,20 +760,25 @@ cleanup_delete:
return status;
}
-
/*
- state of a pending open retry
+ state of a pending retry
*/
-struct pvfs_open_retry {
+struct pvfs_odb_retry {
struct ntvfs_module_context *ntvfs;
struct ntvfs_request *req;
- union smb_open *io;
- struct pvfs_wait *wait_handle;
DATA_BLOB odb_locking_key;
+ void *io;
+ void *private_data;
+ void (*callback)(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io,
+ void *private_data,
+ enum pvfs_wait_notice reason);
};
-/* destroy a pending open request */
-static int pvfs_retry_destructor(struct pvfs_open_retry *r)
+/* destroy a pending request */
+static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
{
struct pvfs_state *pvfs = r->ntvfs->private_data;
if (r->odb_locking_key.data) {
@@ -775,15 +792,92 @@ static int pvfs_retry_destructor(struct pvfs_open_retry *r)
return 0;
}
+static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
+{
+ struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
+
+ if (reason == PVFS_WAIT_EVENT) {
+ /*
+ * The pending odb entry is already removed.
+ * We use a null locking key to indicate this
+ * to the destructor.
+ */
+ data_blob_free(&r->odb_locking_key);
+ }
+
+ r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
+}
+
+/*
+ setup for a retry of a request that was rejected
+ by odb_open_file() or odb_can_open()
+*/
+NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct odb_lock *lck,
+ struct timeval end_time,
+ void *io,
+ void *private_data,
+ void (*callback)(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io,
+ void *private_data,
+ enum pvfs_wait_notice reason))
+{
+ struct pvfs_state *pvfs = ntvfs->private_data;
+ struct pvfs_odb_retry *r;
+ struct pvfs_wait *wait_handle;
+ NTSTATUS status;
+
+ r = talloc(req, struct pvfs_odb_retry);
+ NT_STATUS_HAVE_NO_MEMORY(r);
+
+ r->ntvfs = ntvfs;
+ r->req = req;
+ r->io = io;
+ r->private_data = private_data;
+ r->callback = callback;
+ r->odb_locking_key = odb_get_key(r, lck);
+ if (r->odb_locking_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* setup a pending lock */
+ status = odb_open_file_pending(lck, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ talloc_free(lck);
+
+ talloc_set_destructor(r, pvfs_odb_retry_destructor);
+
+ wait_handle = pvfs_wait_message(pvfs, req,
+ MSG_PVFS_RETRY_OPEN, end_time,
+ pvfs_odb_retry_callback, r);
+ if (wait_handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_steal(r, wait_handle);
+
+ talloc_steal(pvfs, r);
+
+ return NT_STATUS_OK;
+}
+
/*
- retry an open
+ retry an open after a sharing violation
*/
-static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
+static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *_io,
+ void *private_data,
+ enum pvfs_wait_notice reason)
{
- struct pvfs_open_retry *r = private;
- struct ntvfs_module_context *ntvfs = r->ntvfs;
- struct ntvfs_request *req = r->req;
- union smb_open *io = r->io;
+ union smb_open *io = talloc_get_type(_io, union smb_open);
NTSTATUS status;
/* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
@@ -792,8 +886,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
return;
}
- talloc_free(r->wait_handle);
-
if (reason == PVFS_WAIT_TIMEOUT) {
/* if it timed out, then give the failure
immediately */
@@ -803,9 +895,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
return;
}
- /* the pending odb entry is already removed. We use a null locking
- key to indicate this */
- data_blob_free(&r->odb_locking_key);
talloc_free(r);
/* try the open again, which could trigger another retry setup
@@ -919,10 +1008,10 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_open *io,
struct pvfs_file *f,
- struct odb_lock *lck)
+ struct odb_lock *lck,
+ NTSTATUS parent_status)
{
struct pvfs_state *pvfs = ntvfs->private_data;
- struct pvfs_open_retry *r;
NTSTATUS status;
struct timeval end_time;
@@ -936,40 +1025,21 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
}
}
- r = talloc(req, struct pvfs_open_retry);
- if (r == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
-
- r->ntvfs = ntvfs;
- r->req = req;
- r->io = io;
- r->odb_locking_key = data_blob_talloc(r,
- f->handle->odb_locking_key.data,
- f->handle->odb_locking_key.length);
-
- end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
-
- /* setup a pending lock */
- status = odb_open_file_pending(lck, r);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- talloc_free(lck);
+ /* the retry should allocate a new file handle */
talloc_free(f);
- talloc_set_destructor(r, pvfs_retry_destructor);
-
- r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
- pvfs_open_retry, r);
- if (r->wait_handle == NULL) {
- return NT_STATUS_NO_MEMORY;
+ if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ 0, pvfs->sharing_violation_delay);
+ } 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 {
+ return NT_STATUS_INTERNAL_ERROR;
}
- talloc_steal(pvfs, r);
-
- return NT_STATUS_OK;
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
+ pvfs_retry_open_sharing);
}
/*
@@ -1133,6 +1203,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
+ f->handle->oplock = NULL;
f->handle->have_opendb_entry = false;
f->handle->sticky_write_time = false;
f->handle->open_completed = false;
@@ -1186,15 +1257,21 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
}
/* see if we are allowed to open at the same time as existing opens */
- status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
+ status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
- name->full_name, oplock_level, &oplock_granted);
+ io->generic.in.open_disposition,
+ false, oplock_level, &oplock_granted);
- /* on a sharing violation we need to retry when the file is closed by
- the other user, or after 1 second */
- if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
+ /*
+ * 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_open_setup_retry(ntvfs, req, io, f, lck);
+ return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
}
if (!NT_STATUS_IS_OK(status)) {
@@ -1204,6 +1281,12 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
oplock_granted = OPLOCK_BATCH;
+ } else if (oplock_granted != OPLOCK_NONE) {
+ status = pvfs_setup_oplock(f, oplock_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
}
f->handle->have_opendb_entry = true;
@@ -1424,6 +1507,9 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
@@ -1436,15 +1522,18 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_can_open(lck,
- NTCREATEX_SHARE_ACCESS_READ |
- NTCREATEX_SHARE_ACCESS_WRITE |
- NTCREATEX_SHARE_ACCESS_DELETE,
- NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
- SEC_STD_DELETE);
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ access_mask = SEC_STD_DELETE;
+ delete_on_close = true;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ 0, false);
if (NT_STATUS_IS_OK(status)) {
- status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
+ status = pvfs_access_check_simple(pvfs, req, name, access_mask);
}
/*
@@ -1483,6 +1572,9 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
@@ -1495,11 +1587,76 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_can_open(lck,
- NTCREATEX_SHARE_ACCESS_READ |
- NTCREATEX_SHARE_ACCESS_WRITE,
- 0,
- SEC_STD_DELETE);
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = SEC_STD_DELETE;
+ delete_on_close = false;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ 0, false);
+
+ /*
+ * if it's a sharing violation or we got no oplock
+ * only keep the lock if the caller requested access
+ * to the lock
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ if (lckp) {
+ *lckp = lck;
+ } else {
+ talloc_free(lck);
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ if (lckp) {
+ *lckp = NULL;
+ }
+ } else if (lckp) {
+ *lckp = lck;
+ }
+
+ return status;
+}
+
+/*
+ determine if the file size of a file can be changed,
+ or if it is prevented by an already open file
+*/
+NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ struct odb_lock **lckp)
+{
+ NTSTATUS status;
+ DATA_BLOB key;
+ struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool break_to_none;
+ bool delete_on_close;
+
+ status = pvfs_locking_key(name, name, &key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* TODO: this may needs some more flags */
+ share_access = NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = 0;
+ delete_on_close = false;
+ break_to_none = true;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ 0, break_to_none);
/*
* if it's a sharing violation or we got no oplock
@@ -1536,6 +1693,9 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
@@ -1548,10 +1708,14 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_can_open(lck,
- NTCREATEX_SHARE_ACCESS_READ |
- NTCREATEX_SHARE_ACCESS_WRITE,
- 0, 0);
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = 0;
+ delete_on_close = false;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ 0, false);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
diff --git a/source4/ntvfs/posix/pvfs_oplock.c b/source4/ntvfs/posix/pvfs_oplock.c
new file mode 100644
index 0000000000..cf30ddbc59
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_oplock.c
@@ -0,0 +1,239 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - oplock handling
+
+ Copyright (C) Stefan Metzmacher 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "vfs_posix.h"
+
+
+struct pvfs_oplock {
+ struct pvfs_file_handle *handle;
+ struct pvfs_file *file;
+ uint32_t level;
+ struct messaging_context *msg_ctx;
+};
+
+/*
+ receive oplock breaks and forward them to the client
+*/
+static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
+{
+ NTSTATUS status;
+ struct pvfs_file *f = opl->file;
+ struct pvfs_file_handle *h = opl->handle;
+ struct pvfs_state *pvfs = h->pvfs;
+
+ DEBUG(10,("pvfs_oplock_break: sending oplock break level %d for '%s' %p\n",
+ level, h->name->original_name, h));
+ status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("pvfs_oplock_break: sending oplock break failed: %s\n",
+ nt_errstr(status)));
+ }
+}
+
+static void pvfs_oplock_break_dispatch(struct messaging_context *msg,
+ void *private_data, uint32_t msg_type,
+ struct server_id src, DATA_BLOB *data)
+{
+ struct pvfs_oplock *opl = talloc_get_type(private_data,
+ struct pvfs_oplock);
+ struct opendb_oplock_break opb;
+
+ ZERO_STRUCT(opb);
+
+ /* we need to check that this one is for us. See
+ messaging_send_ptr() for the other side of this.
+ */
+ if (data->length == sizeof(struct opendb_oplock_break)) {
+ struct opendb_oplock_break *p;
+ p = (struct opendb_oplock_break *)data->data;
+ opb = *p;
+ } else {
+ DEBUG(0,("%s: ignore oplock break with length[%u]\n",
+ __location__, data->length));
+ return;
+ }
+ if (opb.file_handle != opl->handle) {
+ return;
+ }
+
+ /*
+ * maybe we should use ntvfs_setup_async()
+ */
+ pvfs_oplock_break(opl, opb.level);
+}
+
+static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
+{
+ messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
+ return 0;
+}
+
+NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
+{
+ NTSTATUS status;
+ struct pvfs_oplock *opl;
+ uint32_t level = OPLOCK_NONE;
+
+ f->handle->oplock = NULL;
+
+ switch (oplock_granted) {
+ case EXCLUSIVE_OPLOCK_RETURN:
+ level = OPLOCK_EXCLUSIVE;
+ break;
+ case BATCH_OPLOCK_RETURN:
+ level = OPLOCK_BATCH;
+ break;
+ case LEVEL_II_OPLOCK_RETURN:
+ level = OPLOCK_LEVEL_II;
+ break;
+ }
+
+ if (level == OPLOCK_NONE) {
+ return NT_STATUS_OK;
+ }
+
+ opl = talloc(f->handle, struct pvfs_oplock);
+ NT_STATUS_HAVE_NO_MEMORY(opl);
+
+ opl->handle = f->handle;
+ opl->file = f;
+ opl->level = level;
+ opl->msg_ctx = f->pvfs->ntvfs->ctx->msg_ctx;
+
+ status = messaging_register(opl->msg_ctx,
+ opl,
+ MSG_NTVFS_OPLOCK_BREAK,
+ pvfs_oplock_break_dispatch);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* destructor */
+ talloc_set_destructor(opl, pvfs_oplock_destructor);
+
+ f->handle->oplock = opl;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ struct pvfs_state *pvfs = ntvfs->private_data;
+ struct pvfs_file *f;
+ struct pvfs_file_handle *h;
+ struct odb_lock *olck;
+ uint8_t oplock_break;
+ NTSTATUS status;
+
+ f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ h = f->handle;
+
+ if (h->fd == -1) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (!h->have_opendb_entry) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (!h->oplock) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (olck == NULL) {
+ DEBUG(0,("Unable to lock opendb for oplock update\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
+ if (oplock_break == OPLOCK_BREAK_TO_NONE) {
+ h->oplock->level = OPLOCK_NONE;
+ } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
+ h->oplock->level = OPLOCK_LEVEL_II;
+ } else {
+ /* fallback to level II in case of a invalid value */
+ DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
+ h->oplock->level = OPLOCK_LEVEL_II;
+ }
+ status = odb_update_oplock(olck, h, h->oplock->level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ talloc_free(olck);
+ return status;
+ }
+
+ talloc_free(olck);
+
+ /* after a break to none, we no longer have an oplock attached */
+ if (h->oplock->level == OPLOCK_NONE) {
+ talloc_free(h->oplock);
+ h->oplock = NULL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
+{
+ struct pvfs_file_handle *h = f->handle;
+ struct odb_lock *olck;
+ NTSTATUS status;
+
+ if (h->oplock && h->oplock->level == OPLOCK_EXCLUSIVE) {
+ return NT_STATUS_OK;
+ }
+
+ olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (olck == NULL) {
+ DEBUG(0,("Unable to lock opendb for oplock update\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (h->oplock && h->oplock->level == OPLOCK_BATCH) {
+ status = odb_update_oplock(olck, h, OPLOCK_LEVEL_II);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ talloc_free(olck);
+ return status;
+ }
+ }
+
+ status = odb_break_oplocks(olck);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ talloc_free(olck);
+ return status;
+ }
+
+ talloc_free(olck);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c
index fbbb8c2d4b..da1e31e639 100644
--- a/source4/ntvfs/posix/pvfs_setfileinfo.c
+++ b/source4/ntvfs/posix/pvfs_setfileinfo.c
@@ -152,6 +152,9 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
}
status = pvfs_can_delete(pvfs, req, name2, NULL);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
return NT_STATUS_ACCESS_DENIED;
}
@@ -352,6 +355,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_ALLOCATION_INFO:
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
if (newstats.dos.alloc_size < newstats.st.st_size) {
newstats.st.st_size = newstats.dos.alloc_size;
@@ -362,6 +368,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_END_OF_FILE_INFO:
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
newstats.st.st_size = info->end_of_file_info.in.size;
break;
@@ -463,6 +472,84 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
return pvfs_dosattrib_save(pvfs, h->name, h->fd);
}
+/*
+ retry an open after a sharing violation
+*/
+static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *_info,
+ void *private_data,
+ enum pvfs_wait_notice reason)
+{
+ union smb_setfileinfo *info = talloc_get_type(_info,
+ union smb_setfileinfo);
+ 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_setpathinfo(ntvfs, req, info);
+ 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 unlink retry after a sharing violation
+ or a non granted oplock
+*/
+static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info,
+ 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, info, NULL,
+ pvfs_retry_setpathinfo);
+}
/*
set info on a pathname
@@ -477,6 +564,7 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
struct utimbuf unix_times;
uint32_t access_needed;
uint32_t change_mask = 0;
+ struct odb_lock *lck = NULL;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
@@ -551,6 +639,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_ALLOCATION_INFO:
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ status = pvfs_can_update_file_size(pvfs, req, name, &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_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
/* strange. Increasing the allocation size via setpathinfo
should be silently ignored */
@@ -566,6 +668,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_END_OF_FILE_INFO:
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ status = pvfs_can_update_file_size(pvfs, req, name, &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_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
newstats.st.st_size = info->end_of_file_info.in.size;
break;
diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c
index bda4014bee..7a2d964b9d 100644
--- a/source4/ntvfs/posix/pvfs_unlink.c
+++ b/source4/ntvfs/posix/pvfs_unlink.c
@@ -23,6 +23,84 @@
#include "vfs_posix.h"
#include "system/dir.h"
+/*
+ retry an open after a sharing violation
+*/
+static void pvfs_retry_unlink(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_unlink *io = talloc_get_type(_io, union smb_unlink);
+ 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_unlink(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 unlink retry after a sharing violation
+ or a non granted oplock
+*/
+static NTSTATUS pvfs_unlink_setup_retry(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *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_unlink);
+}
+
/*
unlink a file
@@ -67,6 +145,7 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
struct pvfs_filename *name)
{
NTSTATUS status;
+ struct odb_lock *lck = NULL;
/* make sure its matches the given attributes */
status = pvfs_match_attrib(pvfs, name,
@@ -75,7 +154,20 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
return status;
}
- status = pvfs_can_delete(pvfs, req, name, NULL);
+ status = pvfs_can_delete(pvfs, req, name, &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_unlink_setup_retry(pvfs->ntvfs, req, unl, lck, status);
+ }
+
if (!NT_STATUS_IS_OK(status)) {
return status;
}
diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c
index 5a11f01952..dda8c83407 100644
--- a/source4/ntvfs/posix/pvfs_write.c
+++ b/source4/ntvfs/posix/pvfs_write.c
@@ -57,7 +57,10 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs,
wr->writex.in.count,
WRITE_LOCK);
NT_STATUS_NOT_OK_RETURN(status);
-
+
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
if (f->handle->name->stream_name) {
ret = pvfs_stream_write(pvfs,
f->handle,
diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c
index e058e6f546..ca874d1db1 100644
--- a/source4/ntvfs/posix/vfs_posix.c
+++ b/source4/ntvfs/posix/vfs_posix.c
@@ -91,6 +91,10 @@ static void pvfs_setup_options(struct pvfs_state *pvfs)
PVFS_SHARE_DELAY,
PVFS_SHARE_DELAY_DEFAULT);
+ pvfs->oplock_break_timeout = share_int_option(scfg,
+ PVFS_OPLOCK_TIMEOUT,
+ PVFS_OPLOCK_TIMEOUT_DEFAULT);
+
pvfs->share_name = talloc_strdup(pvfs, scfg->name);
pvfs->fs_attribs =
diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h
index 84c456dcfe..4d22a91714 100644
--- a/source4/ntvfs/posix/vfs_posix.h
+++ b/source4/ntvfs/posix/vfs_posix.h
@@ -29,6 +29,7 @@
#include "dsdb/samdb/samdb.h"
struct pvfs_wait;
+struct pvfs_oplock;
/* this is the private structure for the posix vfs backend. It is used
to hold per-connection (per tree connect) state information */
@@ -51,9 +52,12 @@ struct pvfs_state {
ntcancel */
struct pvfs_wait *wait_list;
- /* the sharing violation timeout */
+ /* the sharing violation timeout (nsecs) */
uint_t sharing_violation_delay;
+ /* the oplock break timeout (secs) */
+ uint_t oplock_break_timeout;
+
/* filesystem attributes (see FS_ATTR_*) */
uint32_t fs_attribs;
@@ -154,6 +158,13 @@ struct pvfs_file_handle {
bool have_opendb_entry;
+ /*
+ * we need to wait for oplock break requests from other processes,
+ * and we need to remember the pvfs_file so we can correctly
+ * forward the oplock break to the client
+ */
+ struct pvfs_oplock *oplock;
+
/* we need this hook back to our parent for lock destruction */
struct pvfs_state *pvfs;
@@ -230,10 +241,16 @@ struct pvfs_dir;
/* types of notification for pvfs wait events */
enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
+/*
+ state of a pending retry
+*/
+struct pvfs_odb_retry;
+
#define PVFS_EADB "posix:eadb"
#define PVFS_XATTR "posix:xattr"
#define PVFS_FAKE_OPLOCKS "posix:fakeoplocks"
#define PVFS_SHARE_DELAY "posix:sharedelay"
+#define PVFS_OPLOCK_TIMEOUT "posix:oplocktimeout"
#define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding"
#define PVFS_SEARCH_INACTIVITY "posix:searchinactivity"
#define PVFS_ACL "posix:acl"
@@ -241,7 +258,8 @@ enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
#define PVFS_XATTR_DEFAULT true
#define PVFS_FAKE_OPLOCKS_DEFAULT false
-#define PVFS_SHARE_DELAY_DEFAULT 1000000
+#define PVFS_SHARE_DELAY_DEFAULT 1000000 /* nsecs */
+#define PVFS_OPLOCK_TIMEOUT_DEFAULT 30 /* secs */
#define PVFS_ALLOCATION_ROUNDING_DEFAULT 512
#define PVFS_SEARCH_INACTIVITY_DEFAULT 300