diff options
Diffstat (limited to 'source4/ntvfs')
-rw-r--r-- | source4/ntvfs/cifs/vfs_cifs.c | 7 | ||||
-rw-r--r-- | source4/ntvfs/common/config.mk | 2 | ||||
-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 | 50 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_open.c | 125 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_rename.c | 12 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_unlink.c | 6 | ||||
-rw-r--r-- | source4/ntvfs/sysdep/config.m4 | 10 | ||||
-rw-r--r-- | source4/ntvfs/sysdep/config.mk | 8 | ||||
-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 |
13 files changed, 594 insertions, 52 deletions
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 356f6465c3..c66257b73f 100644 --- a/source4/ntvfs/common/config.mk +++ b/source4/ntvfs/common/config.mk @@ -2,7 +2,7 @@ # Start LIBRARY ntvfs_common [SUBSYSTEM::ntvfs_common] PRIVATE_PROTO_HEADER = proto.h -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..fc05b342a8 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 @@ -96,7 +102,12 @@ static struct odb_context *odb_tdb_init(TALLOC_CTX *mem_ctx, odb->ntvfs_ctx = ntvfs_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->oplocks = lp_parm_bool(ntvfs_ctx->lp_ctx, NULL, "opendb", "oplocks", true); + + 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..6e77cb7c75 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; } @@ -876,8 +881,6 @@ NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs, talloc_steal(r, wait_handle); - talloc_steal(pvfs, r); - return NT_STATUS_OK; } @@ -892,8 +895,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 +910,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 +928,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 +1048,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 +1069,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 +1334,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)) { @@ -1307,6 +1369,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return status; } + f->handle->have_opendb_entry = true; + if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) { oplock_granted = OPLOCK_BATCH; } else if (oplock_granted != OPLOCK_NONE) { @@ -1317,23 +1381,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 29b2d03005..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); @@ -575,6 +578,15 @@ NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs, case RAW_RENAME_NTRENAME: return pvfs_rename_nt(ntvfs, req, ren); + case RAW_RENAME_NTTRANS: + 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/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index 7a2d964b9d..4cb47a4f1f 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -219,6 +219,12 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, return pvfs_unlink_one(pvfs, req, unl, name); } + /* + * disable async requests in the wildcard case + * untill we have proper tests for this + */ + req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC; + /* get list of matching files */ status = pvfs_list_start(pvfs, name, req, &dir); if (!NT_STATUS_IS_OK(status)) { 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 6cd5d88aca..68be660049 100644 --- a/source4/ntvfs/sysdep/config.mk +++ b/source4/ntvfs/sysdep/config.mk @@ -15,3 +15,11 @@ sys_notify_inotify_OBJ_FILES = ntvfs/sysdep/inotify.o ################################################ sys_notify_OBJ_FILES = ntvfs/sysdep/sys_notify.o + +[SUBSYSTEM::sys_lease_linux] + +sys_lease_linux_OBJ_FILES = ntvfs/sysdep/sys_lease_linux.o + +[SUBSYSTEM::sys_lease] + +sys_lease_OBJ_FILES = ntvfs/sysdep/sys_lease.o 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); +} |