2 Unix SMB/CIFS implementation.
3 kernel oplock processing for Linux
4 Copyright (C) Andrew Tridgell 2000
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.
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.
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/>.
20 #define DBGC_CLASS DBGC_LOCKING
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
26 #ifdef HAVE_KERNEL_OPLOCKS_LINUX
28 #ifndef RT_SIGNAL_LEASE
29 #define RT_SIGNAL_LEASE (SIGRTMIN+1)
33 * Call to set the kernel lease signal handler
35 int linux_set_lease_sighandler(int fd
)
37 if (fcntl(fd
, F_SETSIG
, RT_SIGNAL_LEASE
) == -1) {
38 DBG_NOTICE("Failed to set signal handler for kernel lease\n");
45 /****************************************************************************
46 Call SETLEASE. If we get EACCES then we try setting up the right capability and
48 Use the SMB_VFS_LINUX_SETLEASE instead of this call directly.
49 ****************************************************************************/
51 int linux_setlease(int fd
, int leasetype
)
57 * Ensure the lease owner is root to allow
58 * correct delivery of lease-break signals.
63 /* First set the signal handler. */
64 if (linux_set_lease_sighandler(fd
) == -1) {
69 ret
= fcntl(fd
, F_SETLEASE
, leasetype
);
84 /****************************************************************************
85 * Deal with the Linux kernel <--> smbd
86 * oplock break protocol.
87 ****************************************************************************/
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
)
94 struct kernel_oplocks
*ctx
=
95 talloc_get_type_abort(private_data
,
96 struct kernel_oplocks
);
97 struct smbd_server_connection
*sconn
=
98 talloc_get_type_abort(ctx
->private_data
,
99 struct smbd_server_connection
);
100 siginfo_t
*info
= (siginfo_t
*)_info
;
101 int fd
= info
->si_fd
;
104 fsp
= file_find_fd(sconn
, fd
);
106 DBG_ERR("linux_oplock_signal_handler: failed to find fsp for file fd=%d (file was closed ?)\n", fd
);
109 break_kernel_oplock(sconn
->msg_ctx
, fsp
);
112 /****************************************************************************
113 Attempt to set an kernel oplock on a file.
114 ****************************************************************************/
116 static bool linux_set_kernel_oplock(struct kernel_oplocks
*ctx
,
117 files_struct
*fsp
, int oplock_type
)
119 struct file_id_buf idbuf
;
121 if ( SMB_VFS_LINUX_SETLEASE(fsp
, F_WRLCK
) == -1) {
122 DBG_NOTICE("Refused oplock on file %s, "
123 "fd = %d, file_id = %s. (%s)\n",
126 file_id_str_buf(fsp
->file_id
, &idbuf
),
131 DBG_NOTICE("got kernel oplock on file %s, "
132 "file_id = %s gen_id = %"PRIu64
"\n",
134 file_id_str_buf(fsp
->file_id
, &idbuf
),
135 fh_get_gen_id(fsp
->fh
));
140 /****************************************************************************
141 Release a kernel oplock on a file.
142 ****************************************************************************/
144 static void linux_release_kernel_oplock(struct kernel_oplocks
*ctx
,
145 files_struct
*fsp
, int oplock_type
)
147 struct file_id_buf idbuf
;
149 if (DEBUGLVL(DBGLVL_DEBUG
)) {
151 * Check and print out the current kernel
152 * oplock state of this file.
154 int state
= fcntl(fsp_get_io_fd(fsp
), F_GETLEASE
, 0);
155 dbgtext("linux_release_kernel_oplock: file %s, file_id = %s "
156 "gen_id = %"PRIu64
" has kernel oplock state "
159 file_id_str_buf(fsp
->file_id
, &idbuf
),
160 fh_get_gen_id(fsp
->fh
),
165 * Remove the kernel oplock on this file.
167 if ( SMB_VFS_LINUX_SETLEASE(fsp
, F_UNLCK
) == -1) {
168 if (DEBUGLVL(DBGLVL_ERR
)) {
169 dbgtext("linux_release_kernel_oplock: Error when "
170 "removing kernel oplock on file " );
171 dbgtext("%s, file_id = %s, gen_id = %"PRIu64
". "
174 file_id_str_buf(fsp
->file_id
, &idbuf
),
175 fh_get_gen_id(fsp
->fh
),
181 /****************************************************************************
182 See if the kernel supports oplocks.
183 ****************************************************************************/
185 static bool linux_oplocks_available(void)
188 fd
= open("/dev/null", O_RDONLY
);
190 return False
; /* uggh! */
191 ret
= fcntl(fd
, F_GETLEASE
, 0);
193 return ret
== F_UNLCK
;
196 /****************************************************************************
197 Setup kernel oplocks.
198 ****************************************************************************/
200 static const struct kernel_oplocks_ops linux_koplocks
= {
201 .set_oplock
= linux_set_kernel_oplock
,
202 .release_oplock
= linux_release_kernel_oplock
,
205 struct kernel_oplocks
*linux_init_kernel_oplocks(struct smbd_server_connection
*sconn
)
207 struct kernel_oplocks
*ctx
;
208 struct tevent_signal
*se
;
210 if (!linux_oplocks_available()) {
211 DBG_NOTICE("Linux kernel oplocks not available\n");
215 ctx
= talloc_zero(sconn
, struct kernel_oplocks
);
217 DBG_ERR("Linux Kernel oplocks talloc_Zero failed\n");
221 ctx
->ops
= &linux_koplocks
;
222 ctx
->private_data
= sconn
;
224 se
= tevent_add_signal(sconn
->ev_ctx
,
226 RT_SIGNAL_LEASE
, SA_SIGINFO
,
227 linux_oplock_signal_handler
,
230 DBG_ERR("Failed to setup RT_SIGNAL_LEASE handler\n");
235 DBG_NOTICE("Linux kernel oplocks enabled\n");
240 void oplock_linux_dummy(void);
242 void oplock_linux_dummy(void) {}
243 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */