1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-19 10:03:58 +03:00
samba-mirror/source3/smbd/notify_kernel.c
Volker Lendecke b0bf3ddb38 r21079: Minimizing diff: Adopt the Samba4 style ChangeNotify flags.
Volker
(This used to be commit a3c1069b0c3da914e2ac7337fd9e924b1c811d39)
2007-10-10 12:17:33 -05:00

288 lines
8.4 KiB
C

/*
Unix SMB/Netbios implementation.
Version 3.0
change notify handling - linux kernel based implementation
Copyright (C) Andrew Tridgell 2000
Copyright (C) Volker Lendecke 2007
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
#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_ATTRIB 0x00000020 /* File changed attribute */
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
#endif
#ifndef RT_SIGNAL_NOTIFY
#define RT_SIGNAL_NOTIFY (SIGRTMIN+2)
#endif
#ifndef F_SETSIG
#define F_SETSIG 10
#endif
#ifndef F_NOTIFY
#define F_NOTIFY 1026
#endif
/****************************************************************************
This is the structure to keep the information needed to
determine if a directory has changed.
*****************************************************************************/
struct dnotify_ctx {
struct dnotify_ctx *prev, *next;
int fd;
files_struct *fsp;
};
static struct dnotify_ctx *dnotify_list;
static int dnotify_signal_pipe[2];
/****************************************************************************
The signal handler for change notify.
The Linux kernel has a bug in that we should be able to block any
further delivery of RT signals until the kernel_check_notify() function
unblocks them, but it seems that any signal mask we're setting here is
being overwritten on exit from this handler. I should create a standalone
test case for the kernel hackers. JRA.
*****************************************************************************/
static void dnotify_signal_handler(int sig, siginfo_t *info, void *unused)
{
int saved_errno;
/*
* According to http://www.opengroup.org/onlinepubs/009695399/ write
* to a pipe either writes all or nothing, so we can safely write a
* full sizeof(int) and not risk the pipe to become out of sync with
* the receiving end.
*
* We don't care about the result of the write() call. If the pipe is
* full, then this signal is lost, we can't do anything about it.
*/
saved_errno = errno;
write(dnotify_signal_pipe[1], (const void *)&info->si_fd, sizeof(int));
errno = saved_errno;
sys_select_signal(RT_SIGNAL_NOTIFY);
}
/****************************************************************************
The upper level handler informed when the pipe is ready for reading
*****************************************************************************/
static void dnotify_pipe_handler(struct event_context *event_ctx,
struct fd_event *event,
uint16 flags,
void *private_data)
{
int res, fd;
struct dnotify_ctx *ctx;
res = read(dnotify_signal_pipe[0], (void *)&fd, sizeof(int));
if (res == -1) {
DEBUG(0, ("Read from the dnotify pipe failed: %s\n",
strerror(errno)));
TALLOC_FREE(event); /* Don't try again */
return;
}
if (res != sizeof(int)) {
smb_panic("read from dnotify pipe gave wrong number of "
"bytes\n");
}
for (ctx = dnotify_list; ctx; ctx = ctx->next) {
if (ctx->fd == fd) {
notify_fsp(ctx->fsp, 0, NULL);
}
}
}
/****************************************************************************
Register a change notify request.
*****************************************************************************/
static int kernel_register_notify(connection_struct *conn, char *path,
uint32 flags)
{
int fd;
unsigned long kernel_flags;
fd = sys_open(path,O_RDONLY, 0);
if (fd == -1) {
DEBUG(3,("Failed to open directory %s for change notify\n",
path));
return -1;
}
if (sys_fcntl_long(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
DEBUG(3,("Failed to set signal handler for change notify\n"));
close(fd);
return -1;
}
kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion
* changes
* everything! */
if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_MODIFY;
if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags
|= DN_RENAME
|DN_DELETE;
if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_ATTRIB;
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 (flags & FILE_NOTIFY_CHANGE_SECURITY) kernel_flags |= DN_ATTRIB;
if (flags & FILE_NOTIFY_CHANGE_EA) kernel_flags |= DN_ATTRIB;
if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags
|= DN_RENAME
|DN_DELETE;
if (sys_fcntl_long(fd, F_NOTIFY, kernel_flags) == -1) {
DEBUG(3,("Failed to set async flag for change notify\n"));
close(fd);
return -1;
}
DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) "
"fd=%d\n", path, (int)flags, (int)kernel_flags, fd));
return fd;
}
/****************************************************************************
See if the kernel supports change notify.
****************************************************************************/
static BOOL kernel_notify_available(void)
{
int fd, ret;
fd = open("/tmp", O_RDONLY);
if (fd == -1)
return False; /* uggh! */
ret = sys_fcntl_long(fd, F_NOTIFY, 0);
close(fd);
return ret == 0;
}
static int dnotify_ctx_destructor(struct dnotify_ctx *ctx)
{
close(ctx->fd);
DLIST_REMOVE(dnotify_list, ctx);
return 0;
}
static void *kernel_notify_add(TALLOC_CTX *mem_ctx,
struct event_context *event_ctx,
files_struct *fsp,
uint32 *filter)
{
struct dnotify_ctx *ctx;
if (!(ctx = TALLOC_P(mem_ctx, struct dnotify_ctx))) {
DEBUG(0, ("talloc failed\n"));
return NULL;
}
ctx->fsp = fsp;
ctx->fd = kernel_register_notify(fsp->conn, fsp->fsp_name, *filter);
if (ctx->fd == -1) {
TALLOC_FREE(ctx);
return NULL;
}
DLIST_ADD(dnotify_list, ctx);
talloc_set_destructor(ctx, dnotify_ctx_destructor);
return ctx;
}
/****************************************************************************
Setup kernel based change notify.
****************************************************************************/
struct cnotify_fns *kernel_notify_init(struct event_context *event_ctx)
{
static struct cnotify_fns cnotify;
struct sigaction act;
if (pipe(dnotify_signal_pipe) == -1) {
DEBUG(0, ("Failed to create signal pipe: %s\n",
strerror(errno)));
return NULL;
}
if ((set_blocking(dnotify_signal_pipe[0], False) == -1)
|| (set_blocking(dnotify_signal_pipe[1], False) == -1)) {
DEBUG(0, ("Failed to set signal pipe to non-blocking: %s\n",
strerror(errno)));
close(dnotify_signal_pipe[0]);
close(dnotify_signal_pipe[1]);
return NULL;
}
if (event_add_fd(event_ctx, NULL, dnotify_signal_pipe[0],
EVENT_FD_READ, dnotify_pipe_handler, NULL) == NULL) {
DEBUG(0, ("Failed to set signal event handler\n"));
close(dnotify_signal_pipe[0]);
close(dnotify_signal_pipe[1]);
return NULL;
}
ZERO_STRUCT(act);
act.sa_sigaction = dnotify_signal_handler;
act.sa_flags = SA_SIGINFO;
sigemptyset( &act.sa_mask );
if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n"));
return NULL;
}
if (!kernel_notify_available())
return NULL;
cnotify.notify_add = kernel_notify_add;
/* the signal can start off blocked due to a bug in bash */
BlockSignals(False, RT_SIGNAL_NOTIFY);
return &cnotify;
}
#else
void notify_kernel_dummy(void);
void notify_kernel_dummy(void) {}
#endif /* HAVE_KERNEL_CHANGE_NOTIFY */