summaryrefslogtreecommitdiff
path: root/source3/smbd/notify_kernel.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2000-06-12 15:53:31 +0000
committerAndrew Tridgell <tridge@samba.org>2000-06-12 15:53:31 +0000
commitb2d01bd2dbfed8b35cc324fad42eac562fcad3b4 (patch)
tree6d2708c53b61bbd441d7c4a65945b65d543b2f73 /source3/smbd/notify_kernel.c
parent6de513cef43ad83ecd1823bde5a4e05c22224b0f (diff)
downloadsamba-b2d01bd2dbfed8b35cc324fad42eac562fcad3b4.tar.gz
samba-b2d01bd2dbfed8b35cc324fad42eac562fcad3b4.tar.bz2
samba-b2d01bd2dbfed8b35cc324fad42eac562fcad3b4.zip
totally rewrote the async signal, notification and oplock notification
handling in Samba. This was needed due to several limitations and races in the previous code - as a side effect the new code is much cleaner :) in summary: - changed sys_select() to avoid a signal/select race condition. It is a rare race but once we have signals doing notification and oplocks it is important. - changed our main processing loop to take advantage of the new sys_select semantics - split the notify code into implementaion dependent and general parts. Added the following structure that defines an implementation: struct cnotify_fns { void * (*register_notify)(connection_struct *conn, char *path, uint32 flags); BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t); void (*remove_notify)(void *data); }; then I wrote two implementations, one using hash/poll (like our old code) and the other using the new Linux kernel change notify. It should be easy to add other change notify implementations by creating a sructure of the above type. - fixed a bug in change notify where we were returning the wrong error code. - rewrote the core change notify code to be much simpler - moved to real-time signals for leases and change notify Amazingly, it all seems to work. I was very surprised! (This used to be commit 44766c39e0027c762bee8b33b12c621c109a3267)
Diffstat (limited to 'source3/smbd/notify_kernel.c')
-rw-r--r--source3/smbd/notify_kernel.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/source3/smbd/notify_kernel.c b/source3/smbd/notify_kernel.c
new file mode 100644
index 0000000000..7732bc646f
--- /dev/null
+++ b/source3/smbd/notify_kernel.c
@@ -0,0 +1,170 @@
+#define OLD_NTDOMAIN 1
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ change notify handling - linux kernel based implementation
+ Copyright (C) Andrew Tridgell 2000
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if HAVE_KERNEL_CHANGE_NOTIFY
+
+extern int DEBUGLEVEL;
+static int fd_pending;
+static unsigned signals_received;
+static unsigned signals_processed;
+
+#ifndef DN_ACCESS
+#define DN_ACCESS 0x00000001 /* File accessed in directory */
+#define DN_MODIFY 0x00000002 /* File modified in directory */
+#define DN_CREATE 0x00000004 /* File created in directory */
+#define DN_DELETE 0x00000008 /* File removed from directory */
+#define DN_RENAME 0x00000010 /* File renamed in directory */
+#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
+#endif
+
+
+#ifndef RT_SIGNAL_NOTIFY
+#define RT_SIGNAL_NOTIFY 34
+#endif
+
+/****************************************************************************
+ This is the structure to keep the information needed to
+ determine if a directory has changed.
+*****************************************************************************/
+struct change_data {
+ int directory_handle;
+};
+
+/****************************************************************************
+the signal handler for change notify
+*****************************************************************************/
+static void signal_handler(int signal, siginfo_t *info, void *unused)
+{
+ BlockSignals(True, signal);
+ fd_pending = info->si_fd;
+ signals_received++;
+ sys_select_signal();
+}
+
+
+
+/****************************************************************************
+check if a change notify should be issued
+*****************************************************************************/
+static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+{
+ struct change_data *data = (struct change_data *)datap;
+
+ if (data->directory_handle != fd_pending) return False;
+
+ close(fd_pending);
+ data->directory_handle = fd_pending = -1;
+ signals_processed++;
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ return True;
+}
+
+/****************************************************************************
+remove a change notify data structure
+*****************************************************************************/
+static void kernel_remove_notify(void *datap)
+{
+ struct change_data *data = (struct change_data *)datap;
+ if (data->directory_handle != -1) {
+ if (data->directory_handle == fd_pending) {
+ data->directory_handle = fd_pending = -1;
+ signals_processed++;
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ }
+ close(data->directory_handle);
+ }
+ free(data);
+}
+
+
+/****************************************************************************
+register a change notify request
+*****************************************************************************/
+static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags)
+{
+ struct change_data data;
+ int fd;
+ unsigned long kernel_flags;
+
+ fd = dos_open(fsp->fsp_name, O_RDONLY, 0);
+
+ if (fd == -1) {
+ DEBUG(3,("Failed to open directory %s for change notify\n", fsp->fsp_name));
+ return NULL;
+ }
+
+ if (fcntl(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
+ DEBUG(3,("Failed to set signal handler for change notify\n"));
+ return NULL;
+ }
+
+ kernel_flags = 0;
+ if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME;
+ if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME;
+ if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_ACCESS) kernel_flags |= DN_ACCESS;
+ if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE;
+
+ if (fcntl(fd, F_NOTIFY, kernel_flags) == -1) {
+ DEBUG(3,("Failed to set async flag for change notify\n"));
+ return NULL;
+ }
+
+ data.directory_handle = fd;
+
+ return (void *)memdup(&data, sizeof(data));
+}
+
+
+/****************************************************************************
+setup kernel based change notify
+****************************************************************************/
+struct cnotify_fns *kernel_notify_init(void)
+{
+ static struct cnotify_fns cnotify;
+ struct sigaction act;
+
+ act.sa_handler = NULL;
+ act.sa_sigaction = signal_handler;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
+ DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n"));
+ return NULL;
+ }
+
+ cnotify.register_notify = kernel_register_notify;
+ cnotify.check_notify = kernel_check_notify;
+ cnotify.remove_notify = kernel_remove_notify;
+
+ return &cnotify;
+}
+
+
+#else
+ void notify_kernel_dummy(void) {}
+#endif /* HAVE_KERNEL_CHANGE_NOTIFY */
+
+#undef OLD_NTDOMAIN