4 Unix SMB/Netbios implementation.
6 kernel oplock processing for Linux
7 Copyright (C) Andrew Tridgell 2000
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #if HAVE_KERNEL_OPLOCKS_LINUX
28 extern int DEBUGLEVEL;
30 static unsigned signals_received;
31 static unsigned signals_processed;
32 static int fd_pending; /* the fd of the current pending SIGIO */
35 #define F_SETLEASE 1024
39 #define F_GETLEASE 1025
46 /****************************************************************************
47 handle a SIGIO, incrementing the signals_received and blocking SIGIO
48 ****************************************************************************/
49 static void sigio_handler(int signal, siginfo_t *info, void *unused)
51 fd_pending = info->si_fd;
53 BlockSignals(True, SIGIO);
56 /****************************************************************************
57 try to gain a linux capability
58 ****************************************************************************/
59 static void set_capability(unsigned capability)
61 #ifndef _LINUX_CAPABILITY_VERSION
62 #define _LINUX_CAPABILITY_VERSION 0x19980330
64 /* these can be removed when they are in glibc headers */
75 header.version = _LINUX_CAPABILITY_VERSION;
78 if (capget(&header, &data) == -1) {
79 DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
83 data.effective |= (1<<capability);
85 if (capset(&header, &data) == -1) {
86 DEBUG(3,("Unable to set %d capability (%s)\n",
87 capability, strerror(errno)));
92 /****************************************************************************
93 call SETLEASE. If we get EACCES then we try setting up the right capability and
95 ****************************************************************************/
96 static int linux_setlease(int fd, int leasetype)
99 ret = fcntl(fd, F_SETLEASE, leasetype);
100 if (ret == -1 && errno == EACCES) {
101 set_capability(CAP_LEASE);
102 ret = fcntl(fd, F_SETLEASE, leasetype);
109 /****************************************************************************
110 * Deal with the Linux kernel <--> smbd
111 * oplock break protocol.
112 ****************************************************************************/
113 static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
117 SMB_STRUCT_STAT sbuf;
120 if (signals_received == signals_processed) return False;
122 if (sys_fstat(fd_pending,&sbuf) == -1) {
123 DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", fd_pending));
131 DEBUG(5,("receive_local_message: kernel oplock break request received for \
132 dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
135 * Create a kernel oplock break message.
138 /* Setup the message header */
139 SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
140 SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
142 buffer += OPBRK_CMD_HEADER_LEN;
144 SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
146 memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&dev, sizeof(dev));
147 memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&inode, sizeof(inode));
150 /* now we can receive more signals */
153 BlockSignals(False, SIGIO);
159 /****************************************************************************
160 Attempt to set an kernel oplock on a file.
161 ****************************************************************************/
162 static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
164 if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
165 DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
166 inode = %.0f. (%s)\n",
167 fsp->fsp_name, fsp->fd,
168 (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
172 DEBUG(10,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f\n",
173 fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode));
179 /****************************************************************************
180 Release a kernel oplock on a file.
181 ****************************************************************************/
182 static void linux_release_kernel_oplock(files_struct *fsp)
186 * Check and print out the current kernel
187 * oplock state of this file.
189 int state = fcntl(fsp->fd, F_GETLEASE, 0);
190 dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f has kernel \
191 oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
192 (double)fsp->inode, state );
196 * Remove the kernel oplock on this file.
198 if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
200 dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " );
201 dbgtext("%s, dev = %x, inode = %.0f. Error was %s\n",
202 fsp->fsp_name, (unsigned int)fsp->dev,
203 (double)fsp->inode, strerror(errno) );
209 /****************************************************************************
210 parse a kernel oplock message
211 ****************************************************************************/
212 static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode, SMB_DEV_T *dev)
214 /* Ensure that the msg length is correct. */
215 if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
216 DEBUG(0,("process_local_message: incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, \
217 should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
221 memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
222 memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
224 DEBUG(5,("process_local_message: kernel oplock break request for \
225 file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
231 /****************************************************************************
232 see if a oplock message is waiting
233 ****************************************************************************/
234 static BOOL linux_oplock_msg_waiting(fd_set *fds)
236 return signals_processed != signals_received;
239 /****************************************************************************
240 see if the kernel supports oplocks
241 ****************************************************************************/
242 static BOOL linux_oplocks_available(void)
245 fd = open("/dev/null", O_RDONLY);
246 if (fd == -1) return False; /* uggh! */
247 ret = fcntl(fd, F_GETLEASE, 0);
249 return ret == F_UNLCK;
253 /****************************************************************************
255 ****************************************************************************/
256 struct kernel_oplocks *linux_init_kernel_oplocks(void)
258 static struct kernel_oplocks koplocks;
259 struct sigaction act;
261 if (!linux_oplocks_available()) {
262 DEBUG(3,("Linux kernel oplocks not available\n"));
266 act.sa_handler = NULL;
267 act.sa_sigaction = sigio_handler;
268 act.sa_flags = SA_SIGINFO;
269 if (sigaction(SIGIO, &act, NULL) != 0) {
270 DEBUG(0,("Failed to setup SIGIO handler\n"));
274 koplocks.receive_message = linux_oplock_receive_message;
275 koplocks.set_oplock = linux_set_kernel_oplock;
276 koplocks.release_oplock = linux_release_kernel_oplock;
277 koplocks.parse_message = linux_kernel_oplock_parse;
278 koplocks.msg_waiting = linux_oplock_msg_waiting;
279 koplocks.notification_fd = -1;
287 void oplock_linux_dummy(void) {}
288 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */