Coverage Report

Created: 2025-07-23 07:04

/src/samba/source3/smbd/oplock_linux.c
Line
Count
Source (jump to first uncovered line)
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
0
#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
0
#define RT_SIGNAL_LEASE (SIGRTMIN+1)
30
#endif
31
32
/* 
33
 * Call to set the kernel lease signal handler
34
 */
35
int linux_set_lease_sighandler(int fd)
36
0
{
37
0
        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
0
        }
41
42
0
  return 0;
43
0
}
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
int linux_setlease(int fd, int leasetype)
52
0
{
53
0
  int ret;
54
0
  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
0
  become_root();
62
63
  /* First set the signal handler. */
64
0
  if (linux_set_lease_sighandler(fd) == -1) {
65
0
    saved_errno = errno;
66
0
    ret = -1;
67
0
    goto out;
68
0
  }
69
0
  ret = fcntl(fd, F_SETLEASE, leasetype);
70
0
  if (ret == -1) {
71
0
    saved_errno = errno;
72
0
  }
73
74
0
  out:
75
76
0
  unbecome_root();
77
78
0
  if (ret == -1) {
79
0
    errno = saved_errno;
80
0
  }
81
0
  return ret;
82
0
}
83
84
/****************************************************************************
85
 * Deal with the Linux kernel <--> smbd
86
 * oplock break protocol.
87
****************************************************************************/
88
89
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
0
{
94
0
  struct kernel_oplocks *ctx =
95
0
    talloc_get_type_abort(private_data,
96
0
    struct kernel_oplocks);
97
0
  struct smbd_server_connection *sconn =
98
0
    talloc_get_type_abort(ctx->private_data,
99
0
    struct smbd_server_connection);
100
0
  siginfo_t *info = (siginfo_t *)_info;
101
0
  int fd = info->si_fd;
102
0
  files_struct *fsp;
103
104
0
  fsp = file_find_fd(sconn, fd);
105
0
  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
0
  }
109
0
  break_kernel_oplock(sconn->msg_ctx, fsp);
110
0
}
111
112
/****************************************************************************
113
 Attempt to set an kernel oplock on a file.
114
****************************************************************************/
115
116
static bool linux_set_kernel_oplock(struct kernel_oplocks *ctx,
117
            files_struct *fsp, int oplock_type)
118
0
{
119
0
  struct file_id_buf idbuf;
120
121
0
  if ( SMB_VFS_LINUX_SETLEASE(fsp, F_WRLCK) == -1) {
122
0
    DBG_NOTICE("Refused oplock on file %s, "
123
0
         "fd = %d, file_id = %s. (%s)\n",
124
0
         fsp_str_dbg(fsp),
125
0
         fsp_get_io_fd(fsp),
126
0
         file_id_str_buf(fsp->file_id, &idbuf),
127
0
         strerror(errno));
128
0
    return False;
129
0
  }
130
  
131
0
  DBG_NOTICE("got kernel oplock on file %s, "
132
0
       "file_id = %s gen_id = %"PRIu64"\n",
133
0
       fsp_str_dbg(fsp),
134
0
       file_id_str_buf(fsp->file_id, &idbuf),
135
0
       fh_get_gen_id(fsp->fh));
136
137
0
  return True;
138
0
}
139
140
/****************************************************************************
141
 Release a kernel oplock on a file.
142
****************************************************************************/
143
144
static void linux_release_kernel_oplock(struct kernel_oplocks *ctx,
145
          files_struct *fsp, int oplock_type)
146
0
{
147
0
  struct file_id_buf idbuf;
148
149
0
  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
0
      "gen_id = %"PRIu64" has kernel oplock state "
157
0
      "of %x.\n",
158
0
      fsp_str_dbg(fsp),
159
0
            file_id_str_buf(fsp->file_id, &idbuf),
160
0
      fh_get_gen_id(fsp->fh),
161
0
      state);
162
0
  }
163
164
  /*
165
   * Remove the kernel oplock on this file.
166
   */
167
0
  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
0
        "removing kernel oplock on file " );
171
0
      dbgtext("%s, file_id = %s, gen_id = %"PRIu64". "
172
0
        "Error was %s\n",
173
0
        fsp_str_dbg(fsp),
174
0
        file_id_str_buf(fsp->file_id, &idbuf),
175
0
        fh_get_gen_id(fsp->fh),
176
0
        strerror(errno));
177
0
    }
178
0
  }
179
0
}
180
181
/****************************************************************************
182
 See if the kernel supports oplocks.
183
****************************************************************************/
184
185
static bool linux_oplocks_available(void)
186
0
{
187
0
  int fd, ret;
188
0
  fd = open("/dev/null", O_RDONLY);
189
0
  if (fd == -1)
190
0
    return False; /* uggh! */
191
0
  ret = fcntl(fd, F_GETLEASE, 0);
192
0
  close(fd);
193
0
  return ret == F_UNLCK;
194
0
}
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
struct kernel_oplocks *linux_init_kernel_oplocks(struct smbd_server_connection *sconn)
206
0
{
207
0
  struct kernel_oplocks *ctx;
208
0
  struct tevent_signal *se;
209
210
0
  if (!linux_oplocks_available()) {
211
0
    DBG_NOTICE("Linux kernel oplocks not available\n");
212
0
    return NULL;
213
0
  }
214
215
0
  ctx = talloc_zero(sconn, struct kernel_oplocks);
216
0
  if (!ctx) {
217
0
    DBG_ERR("Linux Kernel oplocks talloc_Zero failed\n");
218
0
    return NULL;
219
0
  }
220
221
0
  ctx->ops = &linux_koplocks;
222
0
  ctx->private_data = sconn;
223
224
0
  se = tevent_add_signal(sconn->ev_ctx,
225
0
             ctx,
226
0
             RT_SIGNAL_LEASE, SA_SIGINFO,
227
0
             linux_oplock_signal_handler,
228
0
             ctx);
229
0
  if (!se) {
230
0
    DBG_ERR("Failed to setup RT_SIGNAL_LEASE handler\n");
231
0
    TALLOC_FREE(ctx);
232
0
    return NULL;
233
0
  }
234
235
0
  DBG_NOTICE("Linux kernel oplocks enabled\n");
236
237
0
  return ctx;
238
0
}
239
#else
240
 void oplock_linux_dummy(void);
241
242
 void oplock_linux_dummy(void) {}
243
#endif /* HAVE_KERNEL_OPLOCKS_LINUX */