LCOV - code coverage report
Current view: top level - source3/smbd - oplock_linux.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 45 79 57.0 %
Date: 2024-04-21 15:09:00 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             :    kernel oplock processing for Linux
       4             :    Copyright (C) Andrew Tridgell 2000
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #define DBGC_CLASS DBGC_LOCKING
      21             : #include "includes.h"
      22             : #include "system/filesys.h"
      23             : #include "smbd/smbd.h"
      24             : #include "smbd/globals.h"
      25             : 
      26             : #ifdef HAVE_KERNEL_OPLOCKS_LINUX
      27             : 
      28             : #ifndef RT_SIGNAL_LEASE
      29             : #define RT_SIGNAL_LEASE (SIGRTMIN+1)
      30             : #endif
      31             : 
      32             : /* 
      33             :  * Call to set the kernel lease signal handler
      34             :  */
      35          12 : int linux_set_lease_sighandler(int fd)
      36             : {
      37          12 :         if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
      38           0 :                 DBG_NOTICE("Failed to set signal handler for kernel lease\n");
      39           0 :                 return -1;
      40             :         }
      41             : 
      42          12 :         return 0;
      43             : }
      44             : 
      45             : /****************************************************************************
      46             :  Call SETLEASE. If we get EACCES then we try setting up the right capability and
      47             :  try again.
      48             :  Use the SMB_VFS_LINUX_SETLEASE instead of this call directly.
      49             : ****************************************************************************/
      50             : 
      51          12 : int linux_setlease(int fd, int leasetype)
      52             : {
      53           0 :         int ret;
      54          12 :         int saved_errno = 0;
      55             : 
      56             :         /*
      57             :          * Ensure the lease owner is root to allow
      58             :          * correct delivery of lease-break signals.
      59             :          */
      60             : 
      61          12 :         become_root();
      62             : 
      63             :         /* First set the signal handler. */
      64          12 :         if (linux_set_lease_sighandler(fd) == -1) {
      65           0 :                 saved_errno = errno;
      66           0 :                 ret = -1;
      67           0 :                 goto out;
      68             :         }
      69          12 :         ret = fcntl(fd, F_SETLEASE, leasetype);
      70          12 :         if (ret == -1) {
      71           0 :                 saved_errno = errno;
      72             :         }
      73             : 
      74          12 :   out:
      75             : 
      76          12 :         unbecome_root();
      77             : 
      78          12 :         if (ret == -1) {
      79           0 :                 errno = saved_errno;
      80             :         }
      81          12 :         return ret;
      82             : }
      83             : 
      84             : /****************************************************************************
      85             :  * Deal with the Linux kernel <--> smbd
      86             :  * oplock break protocol.
      87             : ****************************************************************************/
      88             : 
      89           4 : static void linux_oplock_signal_handler(struct tevent_context *ev_ctx,
      90             :                                         struct tevent_signal *se,
      91             :                                         int signum, int count,
      92             :                                         void *_info, void *private_data)
      93             : {
      94           0 :         struct kernel_oplocks *ctx =
      95           4 :                 talloc_get_type_abort(private_data,
      96             :                 struct kernel_oplocks);
      97           0 :         struct smbd_server_connection *sconn =
      98           4 :                 talloc_get_type_abort(ctx->private_data,
      99             :                 struct smbd_server_connection);
     100           4 :         siginfo_t *info = (siginfo_t *)_info;
     101           4 :         int fd = info->si_fd;
     102           0 :         files_struct *fsp;
     103             : 
     104           4 :         fsp = file_find_fd(sconn, fd);
     105           4 :         if (fsp == NULL) {
     106           0 :                 DBG_ERR("linux_oplock_signal_handler: failed to find fsp for file fd=%d (file was closed ?)\n", fd );
     107           0 :                 return;
     108             :         }
     109           4 :         break_kernel_oplock(sconn->msg_ctx, fsp);
     110             : }
     111             : 
     112             : /****************************************************************************
     113             :  Attempt to set an kernel oplock on a file.
     114             : ****************************************************************************/
     115             : 
     116           7 : static bool linux_set_kernel_oplock(struct kernel_oplocks *ctx,
     117             :                                     files_struct *fsp, int oplock_type)
     118             : {
     119           0 :         struct file_id_buf idbuf;
     120             : 
     121           7 :         if ( SMB_VFS_LINUX_SETLEASE(fsp, F_WRLCK) == -1) {
     122           0 :                 DBG_NOTICE("Refused oplock on file %s, "
     123             :                            "fd = %d, file_id = %s. (%s)\n",
     124             :                            fsp_str_dbg(fsp),
     125             :                            fsp_get_io_fd(fsp),
     126             :                            file_id_str_buf(fsp->file_id, &idbuf),
     127             :                            strerror(errno));
     128           0 :                 return False;
     129             :         }
     130             :         
     131           7 :         DBG_NOTICE("got kernel oplock on file %s, "
     132             :                    "file_id = %s gen_id = %"PRIu64"\n",
     133             :                    fsp_str_dbg(fsp),
     134             :                    file_id_str_buf(fsp->file_id, &idbuf),
     135             :                    fh_get_gen_id(fsp->fh));
     136             : 
     137           7 :         return True;
     138             : }
     139             : 
     140             : /****************************************************************************
     141             :  Release a kernel oplock on a file.
     142             : ****************************************************************************/
     143             : 
     144           7 : static void linux_release_kernel_oplock(struct kernel_oplocks *ctx,
     145             :                                         files_struct *fsp, int oplock_type)
     146             : {
     147           0 :         struct file_id_buf idbuf;
     148             : 
     149           7 :         if (DEBUGLVL(DBGLVL_DEBUG)) {
     150             :                 /*
     151             :                  * Check and print out the current kernel
     152             :                  * oplock state of this file.
     153             :                  */
     154           0 :                 int state = fcntl(fsp_get_io_fd(fsp), F_GETLEASE, 0);
     155           0 :                 dbgtext("linux_release_kernel_oplock: file %s, file_id = %s "
     156             :                         "gen_id = %"PRIu64" has kernel oplock state "
     157             :                         "of %x.\n",
     158             :                         fsp_str_dbg(fsp),
     159             :                         file_id_str_buf(fsp->file_id, &idbuf),
     160             :                         fh_get_gen_id(fsp->fh),
     161             :                         state);
     162             :         }
     163             : 
     164             :         /*
     165             :          * Remove the kernel oplock on this file.
     166             :          */
     167           7 :         if ( SMB_VFS_LINUX_SETLEASE(fsp, F_UNLCK) == -1) {
     168           0 :                 if (DEBUGLVL(DBGLVL_ERR)) {
     169           0 :                         dbgtext("linux_release_kernel_oplock: Error when "
     170             :                                 "removing kernel oplock on file " );
     171           0 :                         dbgtext("%s, file_id = %s, gen_id = %"PRIu64". "
     172             :                                 "Error was %s\n",
     173             :                                 fsp_str_dbg(fsp),
     174             :                                 file_id_str_buf(fsp->file_id, &idbuf),
     175             :                                 fh_get_gen_id(fsp->fh),
     176           0 :                                 strerror(errno));
     177             :                 }
     178             :         }
     179           7 : }
     180             : 
     181             : /****************************************************************************
     182             :  See if the kernel supports oplocks.
     183             : ****************************************************************************/
     184             : 
     185          17 : static bool linux_oplocks_available(void)
     186             : {
     187           0 :         int fd, ret;
     188          17 :         fd = open("/dev/null", O_RDONLY);
     189          17 :         if (fd == -1)
     190           0 :                 return False; /* uggh! */
     191          17 :         ret = fcntl(fd, F_GETLEASE, 0);
     192          17 :         close(fd);
     193          17 :         return ret == F_UNLCK;
     194             : }
     195             : 
     196             : /****************************************************************************
     197             :  Setup kernel oplocks.
     198             : ****************************************************************************/
     199             : 
     200             : static const struct kernel_oplocks_ops linux_koplocks = {
     201             :         .set_oplock                     = linux_set_kernel_oplock,
     202             :         .release_oplock                 = linux_release_kernel_oplock,
     203             : };
     204             : 
     205          17 : struct kernel_oplocks *linux_init_kernel_oplocks(struct smbd_server_connection *sconn)
     206             : {
     207           0 :         struct kernel_oplocks *ctx;
     208           0 :         struct tevent_signal *se;
     209             : 
     210          17 :         if (!linux_oplocks_available()) {
     211           0 :                 DBG_NOTICE("Linux kernel oplocks not available\n");
     212           0 :                 return NULL;
     213             :         }
     214             : 
     215          17 :         ctx = talloc_zero(sconn, struct kernel_oplocks);
     216          17 :         if (!ctx) {
     217           0 :                 DBG_ERR("Linux Kernel oplocks talloc_Zero failed\n");
     218           0 :                 return NULL;
     219             :         }
     220             : 
     221          17 :         ctx->ops = &linux_koplocks;
     222          17 :         ctx->private_data = sconn;
     223             : 
     224          17 :         se = tevent_add_signal(sconn->ev_ctx,
     225             :                                ctx,
     226             :                                RT_SIGNAL_LEASE, SA_SIGINFO,
     227             :                                linux_oplock_signal_handler,
     228             :                                ctx);
     229          17 :         if (!se) {
     230           0 :                 DBG_ERR("Failed to setup RT_SIGNAL_LEASE handler\n");
     231           0 :                 TALLOC_FREE(ctx);
     232           0 :                 return NULL;
     233             :         }
     234             : 
     235          17 :         DBG_NOTICE("Linux kernel oplocks enabled\n");
     236             : 
     237          17 :         return ctx;
     238             : }
     239             : #else
     240             :  void oplock_linux_dummy(void);
     241             : 
     242             :  void oplock_linux_dummy(void) {}
     243             : #endif /* HAVE_KERNEL_OPLOCKS_LINUX */

Generated by: LCOV version 1.14