diff options
-rw-r--r-- | source4/cluster/ctdb/opendb_ctdb.c | 2 | ||||
-rw-r--r-- | source4/librpc/idl/opendb.idl | 1 | ||||
-rw-r--r-- | source4/ntvfs/cifs/vfs_cifs.c | 7 | ||||
-rw-r--r-- | source4/ntvfs/common/config.mk | 5 | ||||
-rw-r--r-- | source4/ntvfs/common/opendb.c | 4 | ||||
-rw-r--r-- | source4/ntvfs/common/opendb.h | 2 | ||||
-rw-r--r-- | source4/ntvfs/common/opendb_tdb.c | 48 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_open.c | 119 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_rename.c | 11 | ||||
-rw-r--r-- | source4/ntvfs/sysdep/config.m4 | 10 | ||||
-rw-r--r-- | source4/ntvfs/sysdep/config.mk | 18 | ||||
-rw-r--r-- | source4/ntvfs/sysdep/sys_lease.c | 142 | ||||
-rw-r--r-- | source4/ntvfs/sysdep/sys_lease.h | 65 | ||||
-rw-r--r-- | source4/ntvfs/sysdep/sys_lease_linux.c | 213 | ||||
-rw-r--r-- | source4/torture/raw/oplock.c | 65 | ||||
-rw-r--r-- | source4/torture/raw/streams.c | 10 |
16 files changed, 655 insertions, 67 deletions
diff --git a/source4/cluster/ctdb/opendb_ctdb.c b/source4/cluster/ctdb/opendb_ctdb.c index a7b8ddf760..402f3a2a1a 100644 --- a/source4/cluster/ctdb/opendb_ctdb.c +++ b/source4/cluster/ctdb/opendb_ctdb.c @@ -283,7 +283,7 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_ent */ static NTSTATUS odb_ctdb_open_file(struct odb_lock *lck, void *file_handle, const char *path, - bool allow_level_II_oplock, + int *fd, bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted) { diff --git a/source4/librpc/idl/opendb.idl b/source4/librpc/idl/opendb.idl index e3bc2d0f17..72bf23a9b4 100644 --- a/source4/librpc/idl/opendb.idl +++ b/source4/librpc/idl/opendb.idl @@ -20,6 +20,7 @@ interface opendb uint32 share_access; uint32 access_mask; pointer file_handle; + pointer fd; /* we need a per-entry delete on close, as well as a per-file one, to cope with strange semantics on open */ boolean8 delete_on_close; diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c index 58183b5e60..3c090b5f5c 100644 --- a/source4/ntvfs/cifs/vfs_cifs.c +++ b/source4/ntvfs/cifs/vfs_cifs.c @@ -595,6 +595,13 @@ static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs, SETUP_PID; + if (ren->nttrans.level == RAW_RENAME_NTTRANS) { + struct cvfs_file *f; + f = ntvfs_handle_get_backend_data(ren->nttrans.in.file.ntvfs, ntvfs); + if (!f) return NT_STATUS_INVALID_HANDLE; + ren->nttrans.in.file.fnum = f->fnum; + } + if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { return smb_raw_rename(private->tree, ren); } diff --git a/source4/ntvfs/common/config.mk b/source4/ntvfs/common/config.mk index 2fc0942ed4..3963ebcdee 100644 --- a/source4/ntvfs/common/config.mk +++ b/source4/ntvfs/common/config.mk @@ -9,7 +9,10 @@ OBJ_FILES = \ opendb.o \ opendb_tdb.o \ notify.o -PUBLIC_DEPENDENCIES = NDR_OPENDB NDR_NOTIFY sys_notify share LIBDBWRAP +PUBLIC_DEPENDENCIES = \ + NDR_OPENDB NDR_NOTIFY \ + sys_notify sys_lease \ + share LIBDBWRAP PRIVATE_DEPENDENCIES = brlock_ctdb opendb_ctdb # End LIBRARY ntvfs_common ################################################ diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c index 1cc077137c..676706e03f 100644 --- a/source4/ntvfs/common/opendb.c +++ b/source4/ntvfs/common/opendb.c @@ -97,11 +97,11 @@ _PUBLIC_ DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck) */ _PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle, const char *path, - bool allow_level_II_oplock, + int *fd, bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted) { return ops->odb_open_file(lck, file_handle, path, - allow_level_II_oplock, + fd, allow_level_II_oplock, oplock_level, oplock_granted); } diff --git a/source4/ntvfs/common/opendb.h b/source4/ntvfs/common/opendb.h index fb3223aea9..045476337a 100644 --- a/source4/ntvfs/common/opendb.h +++ b/source4/ntvfs/common/opendb.h @@ -27,7 +27,7 @@ struct opendb_ops { DATA_BLOB (*odb_get_key)(TALLOC_CTX *mem_ctx, struct odb_lock *lck); NTSTATUS (*odb_open_file)(struct odb_lock *lck, void *file_handle, const char *path, - bool allow_level_II_oplock, + int *fd, bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted); NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private); NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle, diff --git a/source4/ntvfs/common/opendb_tdb.c b/source4/ntvfs/common/opendb_tdb.c index 17fcdfbbb4..be78e09958 100644 --- a/source4/ntvfs/common/opendb_tdb.c +++ b/source4/ntvfs/common/opendb_tdb.c @@ -49,11 +49,13 @@ #include "ntvfs/common/ntvfs_common.h" #include "cluster/cluster.h" #include "param/param.h" +#include "ntvfs/sysdep/sys_lease.h" struct odb_context { struct tdb_wrap *w; struct ntvfs_context *ntvfs_ctx; bool oplocks; + struct sys_lease_context *lease_ctx; }; /* @@ -72,6 +74,10 @@ struct odb_lock { } can_open; }; +static NTSTATUS odb_oplock_break_send(struct messaging_context *msg_ctx, + struct opendb_entry *e, + uint8_t level); + /* Open up the openfiles.tdb database. Close it down using talloc_free(). We need the messaging_ctx to allow for pending open @@ -98,6 +104,11 @@ static struct odb_context *odb_tdb_init(TALLOC_CTX *mem_ctx, /* leave oplocks disabled by default until the code is working */ odb->oplocks = lp_parm_bool(ntvfs_ctx->lp_ctx, NULL, "opendb", "oplocks", false); + odb->lease_ctx = sys_lease_context_create(ntvfs_ctx->config, odb, + ntvfs_ctx->event_ctx, + ntvfs_ctx->msg_ctx, + odb_oplock_break_send); + return odb; } @@ -442,7 +453,7 @@ static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb, */ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle, const char *path, - bool allow_level_II_oplock, + int *fd, bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted) { struct odb_context *odb = lck->odb; @@ -491,22 +502,29 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, oplock_level = OPLOCK_NONE; } + lck->can_open.e->file_handle = file_handle; + lck->can_open.e->fd = fd; + lck->can_open.e->allow_level_II_oplock = allow_level_II_oplock; + lck->can_open.e->oplock_level = oplock_level; + + if (odb->lease_ctx && fd) { + NTSTATUS status; + status = sys_lease_setup(odb->lease_ctx, lck->can_open.e); + NT_STATUS_NOT_OK_RETURN(status); + } + if (oplock_granted) { - if (oplock_level == OPLOCK_EXCLUSIVE) { + if (lck->can_open.e->oplock_level == OPLOCK_EXCLUSIVE) { *oplock_granted = EXCLUSIVE_OPLOCK_RETURN; - } else if (oplock_level == OPLOCK_BATCH) { + } else if (lck->can_open.e->oplock_level == OPLOCK_BATCH) { *oplock_granted = BATCH_OPLOCK_RETURN; - } else if (oplock_level == OPLOCK_LEVEL_II) { + } else if (lck->can_open.e->oplock_level == OPLOCK_LEVEL_II) { *oplock_granted = LEVEL_II_OPLOCK_RETURN; } else { *oplock_granted = NO_OPLOCK_RETURN; } } - lck->can_open.e->file_handle = file_handle; - lck->can_open.e->allow_level_II_oplock = allow_level_II_oplock; - lck->can_open.e->oplock_level = oplock_level; - /* it doesn't conflict, so add it to the end */ lck->file.entries = talloc_realloc(lck, lck->file.entries, struct opendb_entry, @@ -569,6 +587,11 @@ static NTSTATUS odb_tdb_close_file(struct odb_lock *lck, void *file_handle, if (lck->file.entries[i].delete_on_close) { lck->file.delete_on_close = true; } + if (odb->lease_ctx && lck->file.entries[i].fd) { + NTSTATUS status; + status = sys_lease_remove(odb->lease_ctx, &lck->file.entries[i]); + NT_STATUS_NOT_OK_RETURN(status); + } if (i < lck->file.num_entries-1) { memmove(lck->file.entries+i, lck->file.entries+i+1, (lck->file.num_entries - (i+1)) * @@ -622,6 +645,13 @@ static NTSTATUS odb_tdb_update_oplock(struct odb_lock *lck, void *file_handle, if (file_handle == lck->file.entries[i].file_handle && cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.entries[i].server)) { lck->file.entries[i].oplock_level = oplock_level; + + if (odb->lease_ctx && lck->file.entries[i].fd) { + NTSTATUS status; + status = sys_lease_update(odb->lease_ctx, &lck->file.entries[i]); + NT_STATUS_NOT_OK_RETURN(status); + } + break; } } @@ -800,6 +830,7 @@ static NTSTATUS odb_tdb_can_open(struct odb_lock *lck, lck->can_open.e->server = odb->ntvfs_ctx->server_id; lck->can_open.e->file_handle = NULL; + lck->can_open.e->fd = NULL; lck->can_open.e->stream_id = stream_id; lck->can_open.e->share_access = share_access; lck->can_open.e->access_mask = access_mask; @@ -831,5 +862,6 @@ static const struct opendb_ops opendb_tdb_ops = { void odb_tdb_init_ops(void) { + sys_lease_init(); odb_set_ops(&opendb_tdb_ops); } diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 2e757e1742..1399f120a7 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -300,7 +300,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, /* now really mark the file as open */ status = odb_open_file(lck, f->handle, name->full_name, - false, OPLOCK_NONE, NULL); + NULL, false, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { talloc_free(lck); @@ -360,7 +360,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, } status = odb_open_file(lck, f->handle, name->full_name, - false, OPLOCK_NONE, NULL); + NULL, false, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; @@ -600,7 +600,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, mode = pvfs_fileperms(pvfs, attrib); /* create the file */ - fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode); + fd = open(name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode); if (fd == -1) { return pvfs_map_errno(pvfs, errno); } @@ -688,19 +688,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, return status; } - status = odb_open_file(lck, f->handle, name->full_name, - allow_level_II_oplock, - 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 - here as the most likely scenario is that someone else created - the file at the same time */ - close(fd); - return status; - } - - f->ntvfs = h; f->pvfs = pvfs; f->pending_list = NULL; @@ -723,6 +710,18 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, f->handle->sticky_write_time = false; f->handle->open_completed = false; + status = odb_open_file(lck, f->handle, name->full_name, + &f->handle->fd, allow_level_II_oplock, + 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 + here as the most likely scenario is that someone else created + the file at the same time */ + close(fd); + return status; + } + DLIST_ADD(pvfs->files.list, f); /* setup a destructor to avoid file descriptor leaks on @@ -859,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; } @@ -892,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) { @@ -901,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); @@ -909,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 @@ -1028,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)) { @@ -1048,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); } /* @@ -1297,9 +1336,34 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return status; } + if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) { + flags |= O_RDWR; + } else { + flags |= O_RDONLY; + } + + /* 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 status; + } + + f->handle->fd = fd; + /* now really mark the file as open */ status = odb_open_file(lck, f->handle, name->full_name, - allow_level_II_oplock, + &f->handle->fd, allow_level_II_oplock, oplock_level, &oplock_granted); if (!NT_STATUS_IS_OK(status)) { @@ -1319,21 +1383,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, f->handle->have_opendb_entry = true; - if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) { - flags |= O_RDWR; - } else { - flags |= O_RDONLY; - } - - /* do the actual open */ - fd = open(f->handle->name->full_name, flags); - if (fd == -1) { - talloc_free(lck); - return pvfs_map_errno(f->pvfs, errno); - } - - f->handle->fd = fd; - stream_existed = name->stream_exists; /* if this was a stream create then create the stream as well */ diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c index e94de8b28e..5c2a627084 100644 --- a/source4/ntvfs/posix/pvfs_rename.c +++ b/source4/ntvfs/posix/pvfs_rename.c @@ -568,6 +568,9 @@ static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs, NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, union smb_rename *ren) { + struct pvfs_state *pvfs = ntvfs->private_data; + struct pvfs_file *f; + switch (ren->generic.level) { case RAW_RENAME_RENAME: return pvfs_rename_mv(ntvfs, req, ren); @@ -576,7 +579,13 @@ NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs, return pvfs_rename_nt(ntvfs, req, ren); case RAW_RENAME_NTTRANS: - return NT_STATUS_NOT_IMPLEMENTED; + f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs); + if (!f) { + return NT_STATUS_INVALID_HANDLE; + } + + /* wk23 ignores the request */ + return NT_STATUS_OK; default: break; diff --git a/source4/ntvfs/sysdep/config.m4 b/source4/ntvfs/sysdep/config.m4 index f70cac5e64..6de75a4294 100644 --- a/source4/ntvfs/sysdep/config.m4 +++ b/source4/ntvfs/sysdep/config.m4 @@ -11,3 +11,13 @@ fi if test x"$ac_cv_header_linux_inotify_h" = x"yes" -a x"$ac_cv_have___NR_inotify_init_decl" = x"yes"; then SMB_ENABLE(sys_notify_inotify, YES) fi + +AC_HAVE_DECL(F_SETLEASE, [#include <fcntl.h>]) +AC_HAVE_DECL(SA_SIGINFO, [#include <signal.h>]) + +SMB_ENABLE(sys_lease_linux, NO) + +if test x"$ac_cv_have_F_SETLEASE_decl" = x"yes" \ + -a x"$ac_cv_have_SA_SIGINFO_decl" = x"yes"; then + SMB_ENABLE(sys_lease_linux, YES) +fi diff --git a/source4/ntvfs/sysdep/config.mk b/source4/ntvfs/sysdep/config.mk index dee198c9da..048226efad 100644 --- a/source4/ntvfs/sysdep/config.mk +++ b/source4/ntvfs/sysdep/config.mk @@ -16,3 +16,21 @@ OBJ_FILES = \ PUBLIC_DEPENDENCIES = # End SUBSYSTEM sys_notify ################################################ + +################################################ +# Start MODULE sys_lease_linux +[MODULE::sys_lease_linux] +SUBSYSTEM = sys_lease +INIT_FUNCTION = sys_lease_linux_init +OBJ_FILES = \ + sys_lease_linux.o +# End MODULE sys_lease_linux +################################################ + +################################################ +# Start SUBSYSTEM sys_lease +[SUBSYSTEM::sys_lease] +OBJ_FILES = \ + sys_lease.o +# End SUBSYSTEM sys_lease +################################################ diff --git a/source4/ntvfs/sysdep/sys_lease.c b/source4/ntvfs/sysdep/sys_lease.c new file mode 100644 index 0000000000..28dd27a708 --- /dev/null +++ b/source4/ntvfs/sysdep/sys_lease.c @@ -0,0 +1,142 @@ +/* + Unix SMB/CIFS implementation. + + 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/>. +*/ + +/* + abstract the various kernel interfaces to leases (oplocks) into a + single Samba friendly interface +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "ntvfs/sysdep/sys_lease.h" +#include "lib/events/events.h" +#include "lib/util/dlinklist.h" +#include "param/param.h" +#include "build.h" + +/* list of registered backends */ +static struct sys_lease_ops *backends; +static uint32_t num_backends; + +#define LEASE_BACKEND "lease:backend" + +/* + initialise a system change notify backend +*/ +_PUBLIC_ struct sys_lease_context *sys_lease_context_create(struct share_config *scfg, + TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct messaging_context *msg, + sys_lease_send_break_fn break_send) +{ + struct sys_lease_context *ctx; + const char *bname; + int i; + NTSTATUS status; + + if (num_backends == 0) { + return NULL; + } + + if (ev == NULL) { + ev = event_context_find(mem_ctx); + } + + ctx = talloc_zero(mem_ctx, struct sys_lease_context); + if (ctx == NULL) { + return NULL; + } + + ctx->event_ctx = ev; + ctx->msg_ctx = msg; + ctx->break_send = break_send; + + bname = share_string_option(scfg, LEASE_BACKEND, NULL); + if (!bname) { + talloc_free(ctx); + return NULL; + } + + for (i=0;i<num_backends;i++) { + if (strcasecmp(backends[i].name, bname) == 0) { + ctx->ops = &backends[i]; + break; + } + } + + if (!ctx->ops) { + talloc_free(ctx); + return NULL; + } + + status = ctx->ops->init(ctx); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(ctx); + return NULL; + } + + return ctx; +} + +/* + register a lease backend +*/ +_PUBLIC_ NTSTATUS sys_lease_register(const struct sys_lease_ops *backend) +{ + struct sys_lease_ops *b; + b = talloc_realloc(talloc_autofree_context(), backends, + struct sys_lease_ops, num_backends+1); + NT_STATUS_HAVE_NO_MEMORY(b); + backends = b; + backends[num_backends] = *backend; + num_backends++; + return NT_STATUS_OK; +} + +_PUBLIC_ NTSTATUS sys_lease_init(void) +{ + static bool initialized = false; + + init_module_fn static_init[] = { STATIC_sys_lease_MODULES }; + + if (initialized) return NT_STATUS_OK; + initialized = true; + + run_init_functions(static_init); + + return NT_STATUS_OK; +} + +NTSTATUS sys_lease_setup(struct sys_lease_context *ctx, + struct opendb_entry *e) +{ + return ctx->ops->setup(ctx, e); +} + +NTSTATUS sys_lease_update(struct sys_lease_context *ctx, + struct opendb_entry *e) +{ + return ctx->ops->update(ctx, e); +} + +NTSTATUS sys_lease_remove(struct sys_lease_context *ctx, + struct opendb_entry *e) +{ + return ctx->ops->remove(ctx, e); +} diff --git a/source4/ntvfs/sysdep/sys_lease.h b/source4/ntvfs/sysdep/sys_lease.h new file mode 100644 index 0000000000..e53760fb1e --- /dev/null +++ b/source4/ntvfs/sysdep/sys_lease.h @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + + 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 "param/share.h" + +struct sys_lease_context; +struct opendb_entry; +struct messaging_context; + +typedef NTSTATUS (*sys_lease_send_break_fn)(struct messaging_context *, + struct opendb_entry *, + uint8_t level); + +struct sys_lease_ops { + const char *name; + NTSTATUS (*init)(struct sys_lease_context *ctx); + NTSTATUS (*setup)(struct sys_lease_context *ctx, + struct opendb_entry *e); + NTSTATUS (*update)(struct sys_lease_context *ctx, + struct opendb_entry *e); + NTSTATUS (*remove)(struct sys_lease_context *ctx, + struct opendb_entry *e); +}; + +struct sys_lease_context { + struct event_context *event_ctx; + struct messaging_context *msg_ctx; + sys_lease_send_break_fn break_send; + void *private_data; /* for use of backend */ + const struct sys_lease_ops *ops; +}; + +NTSTATUS sys_lease_register(const struct sys_lease_ops *ops); +NTSTATUS sys_lease_init(void); + +struct sys_lease_context *sys_lease_context_create(struct share_config *scfg, + TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct messaging_context *msg_ctx, + sys_lease_send_break_fn break_send); + +NTSTATUS sys_lease_setup(struct sys_lease_context *ctx, + struct opendb_entry *e); + +NTSTATUS sys_lease_update(struct sys_lease_context *ctx, + struct opendb_entry *e); + +NTSTATUS sys_lease_remove(struct sys_lease_context *ctx, + struct opendb_entry *e); diff --git a/source4/ntvfs/sysdep/sys_lease_linux.c b/source4/ntvfs/sysdep/sys_lease_linux.c new file mode 100644 index 0000000000..0727eed212 --- /dev/null +++ b/source4/ntvfs/sysdep/sys_lease_linux.c @@ -0,0 +1,213 @@ +/* + Unix SMB/CIFS implementation. + + 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/>. +*/ + +/* + lease (oplock) implementation using fcntl F_SETLEASE on linux +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/events/events.h" +#include "ntvfs/sysdep/sys_lease.h" +#include "ntvfs/ntvfs.h" +#include "librpc/gen_ndr/ndr_opendb.h" +#include "lib/util/dlinklist.h" +#include "cluster/cluster.h" + +#define LINUX_LEASE_RT_SIGNAL (SIGRTMIN+1) + +struct linux_lease_pending { + struct linux_lease_pending *prev, *next; + struct sys_lease_context *ctx; + struct opendb_entry e; +}; + +/* the global linked list of pending leases */ +static struct linux_lease_pending *leases; + +static void linux_lease_signal_handler(struct event_context *ev_ctx, + struct signal_event *se, + int signum, int count, + void *_info, void *private_data) +{ + struct sys_lease_context *ctx = talloc_get_type(private_data, + struct sys_lease_context); + siginfo_t *info = (siginfo_t *)_info; + struct linux_lease_pending *c; + int got_fd = info->si_fd; + + for (c = leases; c; c = c->next) { + int *fd = (int *)c->e.fd; + + if (got_fd == *fd) { + break; + } + } + + if (!c) { + return; + } + + ctx->break_send(ctx->msg_ctx, &c->e, OPLOCK_BREAK_TO_NONE); +} + +static int linux_lease_pending_destructor(struct linux_lease_pending *p) +{ + int ret; + int *fd = (int *)p->e.fd; + + DLIST_REMOVE(leases, p); + + if (*fd == -1) { + return 0; + } + + ret = fcntl(*fd, F_SETLEASE, F_UNLCK); + if (ret == -1) { + DEBUG(0,("%s: failed to remove oplock: %s\n", + __FUNCTION__, strerror(errno))); + } + + return 0; +} + +static NTSTATUS linux_lease_init(struct sys_lease_context *ctx) +{ + struct signal_event *se; + + se = event_add_signal(ctx->event_ctx, ctx, + LINUX_LEASE_RT_SIGNAL, SA_SIGINFO, + linux_lease_signal_handler, ctx); + NT_STATUS_HAVE_NO_MEMORY(se); + + return NT_STATUS_OK; +} + +static NTSTATUS linux_lease_setup(struct sys_lease_context *ctx, + struct opendb_entry *e) +{ + int ret; + int *fd = (int *)e->fd; + struct linux_lease_pending *p; + + if (e->oplock_level == OPLOCK_NONE) { + e->fd = NULL; + return NT_STATUS_OK; + } else if (e->oplock_level == OPLOCK_LEVEL_II) { + /* + * the linux kernel doesn't support level2 oplocks + * so fix up the granted oplock level + */ + e->oplock_level = OPLOCK_NONE; + e->allow_level_II_oplock = false; + e->fd = NULL; + return NT_STATUS_OK; + } + + p = talloc(ctx, struct linux_lease_pending); + NT_STATUS_HAVE_NO_MEMORY(p); + + p->ctx = ctx; + p->e = *e; + + ret = fcntl(*fd, F_SETSIG, LINUX_LEASE_RT_SIGNAL); + if (ret == -1) { + talloc_free(p); + return map_nt_error_from_unix(errno); + } + + ret = fcntl(*fd, F_SETLEASE, F_WRLCK); + if (ret == -1) { + talloc_free(p); + return map_nt_error_from_unix(errno); + } + + DLIST_ADD(leases, p); + + talloc_set_destructor(p, linux_lease_pending_destructor); + + return NT_STATUS_OK; +} + +static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx, + struct opendb_entry *e); + +static NTSTATUS linux_lease_update(struct sys_lease_context *ctx, + struct opendb_entry *e) +{ + struct linux_lease_pending *c; + + for (c = leases; c; c = c->next) { + if (c->e.fd == e->fd) { + break; + } + } + + if (!c) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* + * set the fd pointer to NULL so that the caller + * will not call the remove function as the oplock + * is already removed + */ + e->fd = NULL; + + talloc_free(c); + + return NT_STATUS_OK; +} + +static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx, + struct opendb_entry *e) +{ + struct linux_lease_pending *c; + + for (c = leases; c; c = c->next) { + if (c->e.fd == e->fd) { + break; + } + } + + if (!c) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + talloc_free(c); + + return NT_STATUS_OK; +} + +static struct sys_lease_ops linux_lease_ops = { + .name = "linux", + .init = linux_lease_init, + .setup = linux_lease_setup, + .update = linux_lease_update, + .remove = linux_lease_remove +}; + +/* + initialialise the linux lease module + */ +NTSTATUS sys_lease_linux_init(void) +{ + /* register ourselves as a system lease module */ + return sys_lease_register(&linux_lease_ops); +} diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c index 89f3607ecd..5ab43d5e78 100644 --- a/source4/torture/raw/oplock.c +++ b/source4/torture/raw/oplock.c @@ -2218,7 +2218,7 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1) NTSTATUS status; bool ret = true; union smb_open io; - union smb_fileinfo qfi; + union smb_fileinfo qfi, qpi; union smb_rename rn; uint16_t fnum=0; @@ -2237,7 +2237,7 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1) */ io.generic.level = RAW_OPEN_NTCREATEX; io.ntcreatex.in.root_fid = 0; - io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.access_mask = 0;/* ask for no access at all */; io.ntcreatex.in.alloc_size = 0; io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; @@ -2256,7 +2256,7 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1) fnum = io.ntcreatex.out.file.fnum; CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); - torture_comment(tctx, "nttrans_rename should not trigger a break nor a share mode violation\n"); + torture_comment(tctx, "nttrans_rename: should not trigger a break nor a share mode violation\n"); ZERO_STRUCT(rn); rn.generic.level = RAW_RENAME_NTTRANS; rn.nttrans.in.file.fnum = fnum; @@ -2268,25 +2268,60 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1) CHECK_STATUS(tctx, status, NT_STATUS_OK); CHECK_VAL(break_info.count, 0); - /* Seems name is only updated on close. */ - smbcli_close(cli1->tree, fnum); - - /* Check if we can open with the new name. */ - io.ntcreatex.in.fname = fname2; - status = smb_raw_open(cli1->tree, tctx, &io); - CHECK_STATUS(tctx, status, NT_STATUS_OK); - fnum = io.ntcreatex.out.file.fnum; - CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); - + /* w2k3 does nothing, it doesn't rename the file */ + torture_comment(tctx, "nttrans_rename: the server should have done nothing\n"); ZERO_STRUCT(qfi); qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION; qfi.generic.in.file.fnum = fnum; status = smb_raw_fileinfo(cli1->tree, tctx, &qfi); CHECK_STATUS(tctx, status, NT_STATUS_OK); - CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2); + CHECK_STRMATCH(qfi.all_info.out.fname.s, fname1); - smbcli_close(cli1->tree, fnum); + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname1; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1); + + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname2; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + torture_comment(tctx, "nttrans_rename: after closing the file the file is still not renamed\n"); + status = smbcli_close(cli1->tree, fnum); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname1; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1); + + ZERO_STRUCT(qpi); + qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION; + qpi.generic.in.file.path = fname2; + + status = smb_raw_pathinfo(cli1->tree, tctx, &qpi); + CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + torture_comment(tctx, "nttrans_rename: rename with an invalid handle gives NT_STATUS_INVALID_HANDLE\n"); + ZERO_STRUCT(rn); + rn.generic.level = RAW_RENAME_NTTRANS; + rn.nttrans.in.file.fnum = fnum+1; + rn.nttrans.in.flags = 0; + rn.nttrans.in.new_name = fname2+strlen(BASEDIR)+1; + + status = smb_raw_rename(cli1->tree, &rn); + + CHECK_STATUS(tctx, status, NT_STATUS_INVALID_HANDLE); done: smb_raw_exit(cli1->session); diff --git a/source4/torture/raw/streams.c b/source4/torture/raw/streams.c index ca6b488af5..1dab36c28b 100644 --- a/source4/torture/raw/streams.c +++ b/source4/torture/raw/streams.c @@ -356,22 +356,24 @@ static bool test_stream_sharemodes(struct torture_context *tctx, * A different stream does not give a sharing violation */ + io.ntcreatex.in.fname = sname2; + status = smb_raw_open(cli->tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); - fnum1 = io.ntcreatex.out.file.fnum; + fnum2 = io.ntcreatex.out.file.fnum; /* * ... whereas the same stream does with unchanged access/share_access * flags */ + io.ntcreatex.in.fname = sname1; io.ntcreatex.in.open_disposition = 0; status = smb_raw_open(cli->tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); io.ntcreatex.in.fname = sname2; status = smb_raw_open(cli->tree, mem_ctx, &io); - CHECK_STATUS(status, NT_STATUS_OK); - fnum2 = io.ntcreatex.out.file.fnum; + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); done: if (fnum1 != -1) smbcli_close(cli->tree, fnum1); @@ -558,7 +560,9 @@ bool torture_raw_streams(struct torture_context *torture, } ret &= test_stream_io(torture, cli, torture); + smb_raw_exit(cli->session); ret &= test_stream_sharemodes(torture, cli, torture); + smb_raw_exit(cli->session); if (!torture_setting_bool(torture, "samba4", false)) { ret &= test_stream_delete(torture, cli, torture); } |