a better test for oplocks being enabled in this kernel
[tprouty/samba.git] / source / smbd / oplock_linux.c
1 #define OLD_NTDOMAIN 1
2
3 /* 
4    Unix SMB/Netbios implementation.
5    Version 3.0
6    kernel oplock processing for Linux
7    Copyright (C) Andrew Tridgell 2000
8    
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.
13    
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.
18    
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.
22 */
23
24 #include "includes.h"
25
26 #if HAVE_KERNEL_OPLOCKS_LINUX
27
28 extern int DEBUGLEVEL;
29
30 static unsigned signals_received;
31 static unsigned signals_processed;
32 static int fd_pending; /* the fd of the current pending SIGIO */
33
34 #ifndef F_SETLEASE
35 #define F_SETLEASE      1024
36 #endif
37
38 #ifndef F_GETLEASE
39 #define F_GETLEASE      1025
40 #endif
41
42 #ifndef CAP_LEASE
43 #define CAP_LEASE 28
44 #endif
45
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)
50 {
51         fd_pending = info->si_fd;
52         signals_received++;
53         BlockSignals(True, SIGIO);
54 }
55
56 /****************************************************************************
57 try to gain a linux capability
58 ****************************************************************************/
59 static void set_capability(unsigned capability)
60 {
61 #ifndef _LINUX_CAPABILITY_VERSION
62 #define _LINUX_CAPABILITY_VERSION 0x19980330
63 #endif
64         /* these can be removed when they are in glibc headers */
65         struct  {
66                 uint32 version;
67                 int pid;
68         } header;
69         struct {
70                 uint32 effective;
71                 uint32 permitted;
72                 uint32 inheritable;
73         } data;
74
75         header.version = _LINUX_CAPABILITY_VERSION;
76         header.pid = 0;
77
78         if (capget(&header, &data) == -1) {
79                 DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
80                 return;
81         }
82
83         data.effective |= (1<<capability);
84
85         if (capset(&header, &data) == -1) {
86                 DEBUG(3,("Unable to set %d capability (%s)\n", 
87                          capability, strerror(errno)));
88         }
89 }
90
91
92 /****************************************************************************
93 call SETLEASE. If we get EACCES then we try setting up the right capability and
94 try again
95 ****************************************************************************/
96 static int linux_setlease(int fd, int leasetype)
97 {
98         int ret;
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);
103         }
104
105         return ret;
106 }
107
108
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)
114 {
115         SMB_DEV_T dev;
116         SMB_INO_T inode;
117         SMB_STRUCT_STAT sbuf;
118         BOOL ret;
119
120         if (signals_received == signals_processed) return False;
121
122         if (sys_fstat(fd_pending,&sbuf) == -1) {
123                 DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", fd_pending));
124                 ret = False;
125                 goto out;
126         }
127
128         dev = sbuf.st_dev;
129         inode = sbuf.st_ino;
130      
131         DEBUG(5,("receive_local_message: kernel oplock break request received for \
132 dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
133      
134         /*
135          * Create a kernel oplock break message.
136          */
137      
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);
141      
142         buffer += OPBRK_CMD_HEADER_LEN;
143      
144         SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
145      
146         memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&dev, sizeof(dev));
147         memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&inode, sizeof(inode));       
148
149  out:
150         /* now we can receive more signals */
151         fd_pending = -1;
152         signals_processed++;
153         BlockSignals(False, SIGIO);
154      
155         return True;
156 }
157
158
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)
163 {
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)));
169                 return False;
170         }
171         
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));
174
175         return True;
176 }
177
178
179 /****************************************************************************
180  Release a kernel oplock on a file.
181 ****************************************************************************/
182 static void linux_release_kernel_oplock(files_struct *fsp)
183 {
184         if (DEBUGLVL(10)) {
185                 /*
186                  * Check and print out the current kernel
187                  * oplock state of this file.
188                  */
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 );
193         }
194
195         /*
196          * Remove the kernel oplock on this file.
197          */
198         if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
199                 if (DEBUGLVL(0)) {
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) );
204                 }
205         }
206 }
207
208
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)
213 {
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));
218                 return False;
219         }
220
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));
223
224         DEBUG(5,("process_local_message: kernel oplock break request for \
225 file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
226
227         return True;
228 }
229
230
231 /****************************************************************************
232 see if a oplock message is waiting
233 ****************************************************************************/
234 static BOOL linux_oplock_msg_waiting(fd_set *fds)
235 {
236         return signals_processed != signals_received;
237 }
238
239 /****************************************************************************
240 see if the kernel supports oplocks
241 ****************************************************************************/
242 static BOOL linux_oplocks_available(void)
243 {
244         int fd, ret;
245         fd = open("/dev/null", O_RDONLY);
246         if (fd == -1) return False; /* uggh! */
247         ret = fcntl(fd, F_GETLEASE, 0);
248         close(fd);
249         return ret == F_UNLCK;
250 }
251
252
253 /****************************************************************************
254 setup kernel oplocks
255 ****************************************************************************/
256 struct kernel_oplocks *linux_init_kernel_oplocks(void) 
257 {
258         static struct kernel_oplocks koplocks;
259         struct sigaction act;
260
261         if (!linux_oplocks_available()) {
262                 DEBUG(3,("Linux kernel oplocks not available\n"));
263                 return NULL;
264         }
265
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"));
271                 return NULL;
272         }
273
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;
280
281         return &koplocks;
282 }
283
284
285
286 #else
287  void oplock_linux_dummy(void) {}
288 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */
289
290 #undef OLD_NTDOMAIN
291