r10656: BIG merge from trunk. Features not copied over
[samba.git] / source3 / smbd / oplock_linux.c
index 8c4259ac5359700831eaa98bb2efea84211baf04..ab0c08f7fcc524b3b74eccde5ea1646889617992 100644 (file)
@@ -1,6 +1,5 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 3.0
+   Unix SMB/CIFS implementation.
    kernel oplock processing for Linux
    Copyright (C) Andrew Tridgell 2000
    
 
 #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
@@ -40,7 +55,7 @@ static VOLATILE sig_atomic_t fd_pending; /* the fd of the current pending signal
 #endif
 
 #ifndef RT_SIGNAL_LEASE
-#define RT_SIGNAL_LEASE 33
+#define RT_SIGNAL_LEASE (SIGRTMIN+1)
 #endif
 
 #ifndef F_SETSIG
@@ -48,35 +63,27 @@ static VOLATILE sig_atomic_t fd_pending; /* the fd of the current pending signal
 #endif
 
 /****************************************************************************
-handle a LEASE signal, incrementing the signals_received and blocking the signal
+ Handle a LEASE signal, incrementing the signals_received and blocking the signal.
 ****************************************************************************/
+
 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);
 }
 
 /****************************************************************************
-try to gain a linux capability
+ Try to gain a linux capability.
 ****************************************************************************/
+
 static void set_capability(unsigned capability)
 {
 #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;
 
@@ -93,11 +100,11 @@ static void set_capability(unsigned capability)
        }
 }
 
-
 /****************************************************************************
-call SETLEASE. If we get EACCES then we try setting up the right capability and
-try again
+ Call SETLEASE. If we get EACCES then we try setting up the right capability and
+ try again
 ****************************************************************************/
+
 static int linux_setlease(int fd, int leasetype)
 {
        int ret;
@@ -116,80 +123,55 @@ static int linux_setlease(int fd, int leasetype)
        return ret;
 }
 
-
 /****************************************************************************
  * Deal with the Linux kernel <--> smbd
  * oplock break protocol.
 ****************************************************************************/
-static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
-{
-       SMB_DEV_T dev;
-       SMB_INO_T inode;
-       SMB_STRUCT_STAT sbuf;
-       BOOL ret = True;
-
-       if (signals_received == signals_processed) return False;
 
-       if (sys_fstat((int)fd_pending,&sbuf) == -1) {
-               DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd_pending));
-               ret = False;
-               goto out;
-       }
-
-       dev = sbuf.st_dev;
-       inode = sbuf.st_ino;
-     
-       DEBUG(3,("receive_local_message: kernel oplock break request received for \
-dev = %x, inode = %.0f\n", (unsigned int)dev, (double)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 *)&dev, sizeof(dev));
-       memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&inode, sizeof(inode));       
-
- out:
+static files_struct *linux_oplock_receive_message(fd_set *fds)
+{
+       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;
+}
 
 /****************************************************************************
  Attempt to set an kernel oplock on a file.
 ****************************************************************************/
+
 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, \
+       if (linux_setlease(fsp->fh->fd, F_WRLCK) == -1) {
+               DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
 inode = %.0f. (%s)\n",
-                        fsp->fsp_name, fsp->fd, 
+                        fsp->fsp_name, fsp->fh->fd, 
                         (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
                return False;
        }
        
-       DEBUG(3,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f\n",
-                 fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode));
+       DEBUG(3,("linux_set_kernel_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));
 
        return True;
 }
 
-
 /****************************************************************************
  Release a kernel oplock on a file.
 ****************************************************************************/
+
 static void linux_release_kernel_oplock(files_struct *fsp)
 {
        if (DEBUGLVL(10)) {
@@ -197,73 +179,53 @@ static void linux_release_kernel_oplock(files_struct *fsp)
                 * 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 has kernel \
+               int state = fcntl(fsp->fh->fd, F_GETLEASE, 0);
+               dbgtext("linux_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, state );
+                        (double)fsp->inode, fsp->file_id, state );
        }
 
        /*
         * Remove the kernel oplock on this file.
         */
-       if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
+       if (linux_setlease(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. Error was %s\n",
+                       dbgtext("linux_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, strerror(errno) );
+                               (double)fsp->inode, fsp->file_id, strerror(errno) );
                }
        }
 }
 
-
 /****************************************************************************
-parse a kernel oplock message
+ See if a oplock message is waiting.
 ****************************************************************************/
-static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode, SMB_DEV_T *dev)
-{
-       /* 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));
 
-        DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f\n", 
-                (unsigned int)*dev, (double)*inode));
-
-       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;
 }
 
 /****************************************************************************
-see if the kernel supports oplocks
+ See if the kernel supports oplocks.
 ****************************************************************************/
+
 static BOOL linux_oplocks_available(void)
 {
        int fd, ret;
        fd = open("/dev/null", O_RDONLY);
-       if (fd == -1) return False; /* uggh! */
+       if (fd == -1)
+               return False; /* uggh! */
        ret = fcntl(fd, F_GETLEASE, 0);
        close(fd);
        return ret == F_UNLCK;
 }
 
-
 /****************************************************************************
-setup kernel oplocks
+ Setup kernel oplocks.
 ****************************************************************************/
+
 struct kernel_oplocks *linux_init_kernel_oplocks(void) 
 {
        static struct kernel_oplocks koplocks;
@@ -274,28 +236,32 @@ struct kernel_oplocks *linux_init_kernel_oplocks(void)
                return NULL;
        }
 
-        act.sa_handler = NULL;
-        act.sa_sigaction = signal_handler;
-        act.sa_flags = SA_SIGINFO;
-        if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
+       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 */