if we are adding a new sambaAccount, make sure that we add a
[Samba.git] / source / smbd / oplock_linux.c
blobf54c6c83e861d1bf8f256c86f761b8e82d512474
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 kernel oplock processing for Linux
5 Copyright (C) Andrew Tridgell 2000
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
24 #if HAVE_KERNEL_OPLOCKS_LINUX
26 static SIG_ATOMIC_T signals_received;
27 #define FD_PENDING_SIZE 100
28 static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
30 #ifndef F_SETLEASE
31 #define F_SETLEASE 1024
32 #endif
34 #ifndef F_GETLEASE
35 #define F_GETLEASE 1025
36 #endif
38 #ifndef CAP_LEASE
39 #define CAP_LEASE 28
40 #endif
42 #ifndef RT_SIGNAL_LEASE
43 #define RT_SIGNAL_LEASE 33
44 #endif
46 #ifndef F_SETSIG
47 #define F_SETSIG 10
48 #endif
50 /****************************************************************************
51 Handle a LEASE signal, incrementing the signals_received and blocking the signal.
52 ****************************************************************************/
54 static void signal_handler(int sig, siginfo_t *info, void *unused)
56 if (signals_received < FD_PENDING_SIZE - 1) {
57 fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd;
58 signals_received++;
59 } /* Else signal is lost. */
60 sys_select_signal();
63 /****************************************************************************
64 Try to gain a linux capability.
65 ****************************************************************************/
67 static void set_capability(unsigned capability)
69 #ifndef _LINUX_CAPABILITY_VERSION
70 #define _LINUX_CAPABILITY_VERSION 0x19980330
71 #endif
72 /* these can be removed when they are in glibc headers */
73 struct {
74 uint32 version;
75 int pid;
76 } header;
77 struct {
78 uint32 effective;
79 uint32 permitted;
80 uint32 inheritable;
81 } data;
83 header.version = _LINUX_CAPABILITY_VERSION;
84 header.pid = 0;
86 if (capget(&header, &data) == -1) {
87 DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
88 return;
91 data.effective |= (1<<capability);
93 if (capset(&header, &data) == -1) {
94 DEBUG(3,("Unable to set %d capability (%s)\n",
95 capability, strerror(errno)));
99 /****************************************************************************
100 Call SETLEASE. If we get EACCES then we try setting up the right capability and
101 try again
102 ****************************************************************************/
104 static int linux_setlease(int fd, int leasetype)
106 int ret;
108 if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
109 DEBUG(3,("Failed to set signal handler for kernel lease\n"));
110 return -1;
113 ret = fcntl(fd, F_SETLEASE, leasetype);
114 if (ret == -1 && errno == EACCES) {
115 set_capability(CAP_LEASE);
116 ret = fcntl(fd, F_SETLEASE, leasetype);
119 return ret;
122 /****************************************************************************
123 * Deal with the Linux kernel <--> smbd
124 * oplock break protocol.
125 ****************************************************************************/
127 static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
129 int fd;
130 struct files_struct *fsp;
132 BlockSignals(True, RT_SIGNAL_LEASE);
133 fd = fd_pending_array[0];
134 fsp = file_find_fd(fd);
135 fd_pending_array[0] = (SIG_ATOMIC_T)-1;
136 if (signals_received > 1)
137 memmove((void *)&fd_pending_array[0], (void *)&fd_pending_array[1],
138 sizeof(SIG_ATOMIC_T)*(signals_received-1));
139 signals_received--;
140 /* now we can receive more signals */
141 BlockSignals(False, RT_SIGNAL_LEASE);
143 if (fsp == NULL) {
144 DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd));
145 return False;
148 DEBUG(3,("linux_oplock_receive_message: kernel oplock break request received for \
149 dev = %x, inode = %.0f fd = %d, fileid = %lu \n", (unsigned int)fsp->dev, (double)fsp->inode,
150 fd, fsp->file_id));
153 * Create a kernel oplock break message.
156 /* Setup the message header */
157 SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
158 SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
160 buffer += OPBRK_CMD_HEADER_LEN;
162 SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
164 memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
165 memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
166 memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
168 return True;
171 /****************************************************************************
172 Attempt to set an kernel oplock on a file.
173 ****************************************************************************/
175 static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
177 if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
178 DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
179 inode = %.0f. (%s)\n",
180 fsp->fsp_name, fsp->fd,
181 (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
182 return False;
185 DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %lu\n",
186 fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
188 return True;
191 /****************************************************************************
192 Release a kernel oplock on a file.
193 ****************************************************************************/
195 static void linux_release_kernel_oplock(files_struct *fsp)
197 if (DEBUGLVL(10)) {
199 * Check and print out the current kernel
200 * oplock state of this file.
202 int state = fcntl(fsp->fd, F_GETLEASE, 0);
203 dbgtext("linux_release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %lu has kernel \
204 oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
205 (double)fsp->inode, fsp->file_id, state );
209 * Remove the kernel oplock on this file.
211 if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
212 if (DEBUGLVL(0)) {
213 dbgtext("linux_release_kernel_oplock: Error when removing kernel oplock on file " );
214 dbgtext("%s, dev = %x, inode = %.0f, file_id = %lu. Error was %s\n",
215 fsp->fsp_name, (unsigned int)fsp->dev,
216 (double)fsp->inode, fsp->file_id, strerror(errno) );
221 /****************************************************************************
222 Parse a kernel oplock message.
223 ****************************************************************************/
225 static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode,
226 SMB_DEV_T *dev, unsigned long *file_id)
228 /* Ensure that the msg length is correct. */
229 if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
230 DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
231 msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
232 return False;
235 memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
236 memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
237 memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
239 DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %lu\n",
240 (unsigned int)*dev, (double)*inode, *file_id));
242 return True;
245 /****************************************************************************
246 See if a oplock message is waiting.
247 ****************************************************************************/
249 static BOOL linux_oplock_msg_waiting(fd_set *fds)
251 return signals_received != 0;
254 /****************************************************************************
255 See if the kernel supports oplocks.
256 ****************************************************************************/
258 static BOOL linux_oplocks_available(void)
260 int fd, ret;
261 fd = open("/dev/null", O_RDONLY);
262 if (fd == -1)
263 return False; /* uggh! */
264 ret = fcntl(fd, F_GETLEASE, 0);
265 close(fd);
266 return ret == F_UNLCK;
269 /****************************************************************************
270 Setup kernel oplocks.
271 ****************************************************************************/
273 struct kernel_oplocks *linux_init_kernel_oplocks(void)
275 static struct kernel_oplocks koplocks;
276 struct sigaction act;
278 if (!linux_oplocks_available()) {
279 DEBUG(3,("Linux kernel oplocks not available\n"));
280 return NULL;
283 act.sa_handler = NULL;
284 act.sa_sigaction = signal_handler;
285 act.sa_flags = SA_SIGINFO;
286 if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
287 DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
288 return NULL;
291 koplocks.receive_message = linux_oplock_receive_message;
292 koplocks.set_oplock = linux_set_kernel_oplock;
293 koplocks.release_oplock = linux_release_kernel_oplock;
294 koplocks.parse_message = linux_kernel_oplock_parse;
295 koplocks.msg_waiting = linux_oplock_msg_waiting;
296 koplocks.notification_fd = -1;
298 DEBUG(3,("Linux kernel oplocks enabled\n"));
300 return &koplocks;
302 #else
303 void oplock_linux_dummy(void) {}
304 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */