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 "smbd/globals.h" 23 24#if HAVE_KERNEL_OPLOCKS_LINUX 25 26#ifndef F_SETLEASE 27#define F_SETLEASE 1024 28#endif 29 30#ifndef F_GETLEASE 31#define F_GETLEASE 1025 32#endif 33 34#ifndef CAP_LEASE 35#define CAP_LEASE 28 36#endif 37 38#ifndef RT_SIGNAL_LEASE 39#define RT_SIGNAL_LEASE (SIGRTMIN+1) 40#endif 41 42#ifndef F_SETSIG 43#define F_SETSIG 10 44#endif 45 46/* 47 * public function to get linux lease capability. Needed by some VFS modules (eg. gpfs.c) 48 */ 49void linux_set_lease_capability(void) 50{ 51 set_effective_capability(LEASE_CAPABILITY); 52} 53 54/* 55 * Call to set the kernel lease signal handler 56 */ 57int linux_set_lease_sighandler(int fd) 58{ 59 if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) { 60 DEBUG(3,("Failed to set signal handler for kernel lease\n")); 61 return -1; 62 } 63 64 return 0; 65} 66 67/**************************************************************************** 68 Call SETLEASE. If we get EACCES then we try setting up the right capability and 69 try again. 70 Use the SMB_VFS_LINUX_SETLEASE instead of this call directly. 71****************************************************************************/ 72 73int linux_setlease(int fd, int leasetype) 74{ 75 int ret; 76 77 ret = fcntl(fd, F_SETLEASE, leasetype); 78 if (ret == -1 && errno == EACCES) { 79 set_effective_capability(LEASE_CAPABILITY); 80 ret = fcntl(fd, F_SETLEASE, leasetype); 81 } 82 83 return ret; 84} 85 86/**************************************************************************** 87 * Deal with the Linux kernel <--> smbd 88 * oplock break protocol. 89****************************************************************************/ 90 91static void linux_oplock_signal_handler(struct tevent_context *ev_ctx, 92 struct tevent_signal *se, 93 int signum, int count, 94 void *_info, void *private_data) 95{ 96 siginfo_t *info = (siginfo_t *)_info; 97 int fd = info->si_fd; 98 files_struct *fsp; 99 100 fsp = file_find_fd(fd); 101 if (fsp == NULL) { 102 DEBUG(0,("linux_oplock_signal_handler: failed to find fsp for file fd=%d (file was closed ?)\n", fd )); 103 return; 104 } 105 break_kernel_oplock(smbd_messaging_context(), fsp); 106} 107 108/**************************************************************************** 109 Attempt to set an kernel oplock on a file. 110****************************************************************************/ 111 112static bool linux_set_kernel_oplock(struct kernel_oplocks *ctx, 113 files_struct *fsp, int oplock_type) 114{ 115 if ( SMB_VFS_LINUX_SETLEASE(fsp, F_WRLCK) == -1) { 116 DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, " 117 "fd = %d, file_id = %s. (%s)\n", 118 fsp_str_dbg(fsp), fsp->fh->fd, 119 file_id_string_tos(&fsp->file_id), 120 strerror(errno))); 121 return False; 122 } 123 124 DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, " 125 "file_id = %s gen_id = %lu\n", 126 fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), 127 fsp->fh->gen_id)); 128 129 return True; 130} 131 132/**************************************************************************** 133 Release a kernel oplock on a file. 134****************************************************************************/ 135 136static void linux_release_kernel_oplock(struct kernel_oplocks *ctx, 137 files_struct *fsp, int oplock_type) 138{ 139 if (DEBUGLVL(10)) { 140 /* 141 * Check and print out the current kernel 142 * oplock state of this file. 143 */ 144 int state = fcntl(fsp->fh->fd, F_GETLEASE, 0); 145 dbgtext("linux_release_kernel_oplock: file %s, file_id = %s " 146 "gen_id = %lu has kernel oplock state " 147 "of %x.\n", fsp_str_dbg(fsp), 148 file_id_string_tos(&fsp->file_id), 149 fsp->fh->gen_id, state ); 150 } 151 152 /* 153 * Remove the kernel oplock on this file. 154 */ 155 if ( SMB_VFS_LINUX_SETLEASE(fsp, F_UNLCK) == -1) { 156 if (DEBUGLVL(0)) { 157 dbgtext("linux_release_kernel_oplock: Error when " 158 "removing kernel oplock on file " ); 159 dbgtext("%s, file_id = %s, gen_id = %lu. " 160 "Error was %s\n", fsp_str_dbg(fsp), 161 file_id_string_tos(&fsp->file_id), 162 fsp->fh->gen_id, strerror(errno) ); 163 } 164 } 165} 166 167/**************************************************************************** 168 See if the kernel supports oplocks. 169****************************************************************************/ 170 171static bool linux_oplocks_available(void) 172{ 173 int fd, ret; 174 fd = open("/dev/null", O_RDONLY); 175 if (fd == -1) 176 return False; /* uggh! */ 177 ret = fcntl(fd, F_GETLEASE, 0); 178 close(fd); 179 return ret == F_UNLCK; 180} 181 182/**************************************************************************** 183 Setup kernel oplocks. 184****************************************************************************/ 185 186static const struct kernel_oplocks_ops linux_koplocks = { 187 .set_oplock = linux_set_kernel_oplock, 188 .release_oplock = linux_release_kernel_oplock, 189 .contend_level2_oplocks_begin = NULL, 190 .contend_level2_oplocks_end = NULL, 191}; 192 193struct kernel_oplocks *linux_init_kernel_oplocks(TALLOC_CTX *mem_ctx) 194{ 195 struct kernel_oplocks *ctx; 196 struct tevent_signal *se; 197 198 if (!linux_oplocks_available()) { 199 DEBUG(3,("Linux kernel oplocks not available\n")); 200 return NULL; 201 } 202 203 ctx = talloc_zero(mem_ctx, struct kernel_oplocks); 204 if (!ctx) { 205 DEBUG(0,("Linux Kernel oplocks talloc_Zero failed\n")); 206 return NULL; 207 } 208 209 ctx->ops = &linux_koplocks; 210 211 se = tevent_add_signal(smbd_event_context(), 212 ctx, 213 RT_SIGNAL_LEASE, SA_SIGINFO, 214 linux_oplock_signal_handler, 215 ctx); 216 if (!se) { 217 DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler")); 218 TALLOC_FREE(ctx); 219 return NULL; 220 } 221 222 ctx->private_data = se; 223 224 DEBUG(3,("Linux kernel oplocks enabled\n")); 225 226 return ctx; 227} 228#else 229 void oplock_linux_dummy(void); 230 231 void oplock_linux_dummy(void) {} 232#endif /* HAVE_KERNEL_OPLOCKS_LINUX */ 233