Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define DBGC_CLASS DBGC_LOCKING
#include "includes.h"
#if HAVE_KERNEL_OPLOCKS_LINUX
-static VOLATILE sig_atomic_t signals_received;
-static VOLATILE sig_atomic_t signals_processed;
-static VOLATILE sig_atomic_t fd_pending; /* the fd of the current pending signal */
+/* these can be removed when they are in glibc headers */
+struct cap_user_header {
+ uint32 version;
+ int pid;
+} header;
+struct cap_user_data {
+ uint32 effective;
+ uint32 permitted;
+ uint32 inheritable;
+} data;
+
+extern int capget(struct cap_user_header * hdrp,
+ struct cap_user_data * datap);
+extern int capset(struct cap_user_header * hdrp,
+ const struct cap_user_data * datap);
+
+static SIG_ATOMIC_T signals_received;
+#define FD_PENDING_SIZE 100
+static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
#ifndef F_SETLEASE
#define F_SETLEASE 1024
#endif
#ifndef RT_SIGNAL_LEASE
-#define RT_SIGNAL_LEASE 33
+#define RT_SIGNAL_LEASE (SIGRTMIN+1)
#endif
#ifndef F_SETSIG
static void signal_handler(int sig, siginfo_t *info, void *unused)
{
- BlockSignals(True, sig);
- fd_pending = (sig_atomic_t)info->si_fd;
- signals_received++;
- sys_select_signal();
+ if (signals_received < FD_PENDING_SIZE - 1) {
+ fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd;
+ signals_received++;
+ } /* Else signal is lost. */
+ sys_select_signal(RT_SIGNAL_LEASE);
}
/****************************************************************************
#ifndef _LINUX_CAPABILITY_VERSION
#define _LINUX_CAPABILITY_VERSION 0x19980330
#endif
- /* these can be removed when they are in glibc headers */
- struct {
- uint32 version;
- int pid;
- } header;
- struct {
- uint32 effective;
- uint32 permitted;
- uint32 inheritable;
- } data;
-
header.version = _LINUX_CAPABILITY_VERSION;
header.pid = 0;
if (capget(&header, &data) == -1) {
- DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
+ DEBUG(3,("Unable to get kernel capabilities (%s)\n",
+ strerror(errno)));
return;
}
}
}
+/*
+ Call to set the kernel lease signal handler
+*/
+int linux_set_lease_sighandler(int fd)
+{
+ if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
+ DEBUG(3,("Failed to set signal handler for kernel lease\n"));
+ return -1;
+ }
+
+ return 0;
+}
+
/****************************************************************************
Call SETLEASE. If we get EACCES then we try setting up the right capability and
- try again
+ try again.
+ Use the SMB_VFS_LINUX_SETLEASE instead of this call directly.
****************************************************************************/
-static int linux_setlease(int fd, int leasetype)
+int linux_setlease(int fd, int leasetype)
{
int ret;
- if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
- DEBUG(3,("Failed to set signal handler for kernel lease\n"));
- return -1;
- }
-
ret = fcntl(fd, F_SETLEASE, leasetype);
if (ret == -1 && errno == EACCES) {
set_capability(CAP_LEASE);
* oplock break protocol.
****************************************************************************/
-static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
+static files_struct *linux_oplock_receive_message(fd_set *fds)
{
- BOOL ret = True;
- struct files_struct *fsp;
-
- if (signals_received == signals_processed)
- return False;
-
- if ((fsp = file_find_fd(fd_pending)) == NULL) {
- DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd_pending));
- ret = False;
- goto out;
- }
-
- DEBUG(3,("receive_local_message: kernel oplock break request received for \
-dev = %x, inode = %.0f\n", (unsigned int)fsp->dev, (double)fsp->inode ));
-
- /*
- * Create a kernel oplock break message.
- */
-
- /* Setup the message header */
- SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
- SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
-
- buffer += OPBRK_CMD_HEADER_LEN;
-
- SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
-
- memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
- memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
- memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
-
- out:
+ int fd;
+ files_struct *fsp;
+
+ BlockSignals(True, RT_SIGNAL_LEASE);
+ fd = fd_pending_array[0];
+ fsp = file_find_fd(fd);
+ fd_pending_array[0] = (SIG_ATOMIC_T)-1;
+ if (signals_received > 1)
+ memmove(CONST_DISCARD(void *, &fd_pending_array[0]),
+ CONST_DISCARD(void *, &fd_pending_array[1]),
+ sizeof(SIG_ATOMIC_T)*(signals_received-1));
+ signals_received--;
/* now we can receive more signals */
- fd_pending = (sig_atomic_t)-1;
- signals_processed++;
BlockSignals(False, RT_SIGNAL_LEASE);
-
- return ret;
+
+ return fsp;
}
/****************************************************************************
static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
{
- if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
- DEBUG(3,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
-inode = %.0f. (%s)\n",
- fsp->fsp_name, fsp->fd,
- (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
+ if ( SMB_VFS_LINUX_SETLEASE(fsp,fsp->fh->fd, F_WRLCK) == -1) {
+ DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, "
+ "fd = %d, file_id = %s. (%s)\n",
+ fsp->fsp_name, fsp->fh->fd,
+ file_id_static_string(&fsp->file_id),
+ strerror(errno)));
return False;
}
- DEBUG(3,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %lu\n",
- fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
+ DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, "
+ "file_id = %s gen_id = %lu\n",
+ fsp->fsp_name, file_id_static_string(&fsp->file_id),
+ fsp->fh->gen_id));
return True;
}
* Check and print out the current kernel
* oplock state of this file.
*/
- int state = fcntl(fsp->fd, F_GETLEASE, 0);
- dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %lu has kernel \
-oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
- (double)fsp->inode, fsp->file_id, state );
+ int state = fcntl(fsp->fh->fd, F_GETLEASE, 0);
+ dbgtext("linux_release_kernel_oplock: file %s, file_id = %s "
+ "gen_id = %lu has kernel oplock state "
+ "of %x.\n", fsp->fsp_name, file_id_static_string(&fsp->file_id),
+ fsp->fh->gen_id, state );
}
/*
* Remove the kernel oplock on this file.
*/
- if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
+ if ( SMB_VFS_LINUX_SETLEASE(fsp,fsp->fh->fd, F_UNLCK) == -1) {
if (DEBUGLVL(0)) {
- dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " );
- dbgtext("%s, dev = %x, inode = %.0f, file_id = %lu. Error was %s\n",
- fsp->fsp_name, (unsigned int)fsp->dev,
- (double)fsp->inode, fsp->file_id, strerror(errno) );
+ dbgtext("linux_release_kernel_oplock: Error when "
+ "removing kernel oplock on file " );
+ dbgtext("%s, file_id = %s, gen_id = %lu. "
+ "Error was %s\n", fsp->fsp_name,
+ file_id_static_string(&fsp->file_id),
+ fsp->fh->gen_id, strerror(errno) );
}
}
}
-/****************************************************************************
- Parse a kernel oplock message.
-****************************************************************************/
-
-static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode,
- SMB_DEV_T *dev, unsigned long *file_id)
-{
- /* Ensure that the msg length is correct. */
- if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
- DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
- msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
- return False;
- }
-
- memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
- memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
- memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
-
- DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %lu\n",
- (unsigned int)*dev, (double)*inode, *file_id));
-
- return True;
-}
-
/****************************************************************************
See if a oplock message is waiting.
****************************************************************************/
static BOOL linux_oplock_msg_waiting(fd_set *fds)
{
- return signals_processed != signals_received;
+ return signals_received != 0;
}
/****************************************************************************
return NULL;
}
+ ZERO_STRUCT(act);
+
act.sa_handler = NULL;
act.sa_sigaction = signal_handler;
act.sa_flags = SA_SIGINFO;
+ sigemptyset( &act.sa_mask );
if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
return NULL;
koplocks.receive_message = linux_oplock_receive_message;
koplocks.set_oplock = linux_set_kernel_oplock;
koplocks.release_oplock = linux_release_kernel_oplock;
- koplocks.parse_message = linux_kernel_oplock_parse;
koplocks.msg_waiting = linux_oplock_msg_waiting;
koplocks.notification_fd = -1;
+ /* the signal can start off blocked due to a bug in bash */
+ BlockSignals(False, RT_SIGNAL_LEASE);
+
DEBUG(3,("Linux kernel oplocks enabled\n"));
return &koplocks;
}
#else
+ void oplock_linux_dummy(void);
+
void oplock_linux_dummy(void) {}
#endif /* HAVE_KERNEL_OPLOCKS_LINUX */