ntvfs/sysdep: implement linux kernel oplocks based F_SETLEASE
authorStefan Metzmacher <metze@samba.org>
Fri, 7 Mar 2008 11:19:06 +0000 (12:19 +0100)
committerStefan Metzmacher <metze@samba.org>
Wed, 12 Mar 2008 16:35:06 +0000 (17:35 +0100)
metze
(This used to be commit 3f165d3114519c317b9e7c871bb61d4fcbb8fb09)

source4/ntvfs/sysdep/config.m4
source4/ntvfs/sysdep/config.mk
source4/ntvfs/sysdep/sys_lease_linux.c [new file with mode: 0644]

index f70cac5e642dc0039e77f3936074995c4cd7afb6..6de75a4294140487b6150bca029a1ec424092ea9 100644 (file)
@@ -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
index 753c8833a4d3a64f425d4abaa73d88e795922639..048226efad14cd563bc39151048dc15532b1d851 100644 (file)
@@ -17,6 +17,16 @@ 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]
diff --git a/source4/ntvfs/sysdep/sys_lease_linux.c b/source4/ntvfs/sysdep/sys_lease_linux.c
new file mode 100644 (file)
index 0000000..0727eed
--- /dev/null
@@ -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);
+}