fixed Linux capabilities handling
[sfrench/samba-autobuild/.git] / source3 / 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
35 #ifndef F_SETLEASE
36 #define F_SETLEASE      1024
37 #endif
38
39 #ifndef F_GETLEASE
40 #define F_GETLEASE      1025
41 #endif
42
43 #ifndef CAP_LEASE
44 #define CAP_LEASE 28
45 #endif
46
47 /****************************************************************************
48 handle a SIGIO, incrementing the signals_received and blocking SIGIO
49 ****************************************************************************/
50 static void sigio_handler(int signal, siginfo_t *info, void *unused)
51 {
52         fd_pending = info->si_fd;
53         signals_received++;
54         BlockSignals(True, SIGIO);
55 }
56
57 /****************************************************************************
58 try to gain a linux capability
59 ****************************************************************************/
60 static void set_capability(unsigned capability)
61 {
62 #ifndef _LINUX_CAPABILITY_VERSION
63 #define _LINUX_CAPABILITY_VERSION 0x19980330
64 #endif
65         /* these can be removed when they are in glibc headers */
66         struct  {
67                 uint32 version;
68                 int pid;
69         } header;
70         struct {
71                 uint32 effective;
72                 uint32 permitted;
73                 uint32 inheritable;
74         } data;
75
76         header.version = _LINUX_CAPABILITY_VERSION;
77         header.pid = 0;
78
79         if (capget(&header, &data) == -1) {
80                 DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
81                 return;
82         }
83
84         data.effective |= (1<<capability);
85
86         if (capset(&header, &data) == -1) {
87                 DEBUG(3,("Unable to set %d capability (%s)\n", 
88                          capability, strerror(errno)));
89         }
90 }
91
92
93 /****************************************************************************
94 call SETLEASE. If we get EACCES then we try setting up the right capability and
95 try again
96 ****************************************************************************/
97 static int linux_setlease(int fd, int leasetype)
98 {
99         int ret;
100         ret = fcntl(fd, F_SETLEASE, leasetype);
101         if (ret == -1 && errno == EACCES) {
102                 set_capability(CAP_LEASE);
103                 ret = fcntl(fd, F_SETLEASE, leasetype);
104         }
105
106         return ret;
107 }
108
109
110 /****************************************************************************
111  * Deal with the Linux kernel <--> smbd
112  * oplock break protocol.
113 ****************************************************************************/
114 static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
115 {
116         SMB_DEV_T dev;
117         SMB_INO_T inode;
118         SMB_STRUCT_STAT sbuf;
119         BOOL ret;
120
121         if (signals_received == signals_processed) return False;
122
123         if (sys_fstat(fd_pending,&sbuf) == -1) {
124                 DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", fd_pending));
125                 ret = False;
126                 goto out;
127         }
128
129         dev = sbuf.st_dev;
130         inode = sbuf.st_ino;
131      
132         DEBUG(5,("receive_local_message: kernel oplock break request received for \
133 dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
134      
135         /*
136          * Create a kernel oplock break message.
137          */
138      
139         /* Setup the message header */
140         SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
141         SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
142      
143         buffer += OPBRK_CMD_HEADER_LEN;
144      
145         SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
146      
147         memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&dev, sizeof(dev));
148         memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&inode, sizeof(inode));       
149
150  out:
151         /* now we can receive more signals */
152         fd_pending = -1;
153         signals_processed++;
154         BlockSignals(False, SIGIO);
155      
156         return True;
157 }
158
159
160 /****************************************************************************
161  Attempt to set an kernel oplock on a file.
162 ****************************************************************************/
163 static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
164 {
165         if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
166                 DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
167 inode = %.0f. (%s)\n",
168                          fsp->fsp_name, fsp->fd, 
169                          (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
170                 return False;
171         }
172         
173         DEBUG(10,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f\n",
174                   fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode));
175
176         return True;
177 }
178
179
180 /****************************************************************************
181  Release a kernel oplock on a file.
182 ****************************************************************************/
183 static void linux_release_kernel_oplock(files_struct *fsp)
184 {
185         if (DEBUGLVL(10)) {
186                 /*
187                  * Check and print out the current kernel
188                  * oplock state of this file.
189                  */
190                 int state = fcntl(fsp->fd, F_GETLEASE, 0);
191                 dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f has kernel \
192 oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
193                         (double)fsp->inode, state );
194         }
195
196         /*
197          * Remove the kernel oplock on this file.
198          */
199         if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
200                 if (DEBUGLVL(0)) {
201                         dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " );
202                         dbgtext("%s, dev = %x, inode = %.0f. Error was %s\n",
203                                 fsp->fsp_name, (unsigned int)fsp->dev, 
204                                 (double)fsp->inode, strerror(errno) );
205                 }
206         }
207 }
208
209
210 /****************************************************************************
211 parse a kernel oplock message
212 ****************************************************************************/
213 static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode, SMB_DEV_T *dev)
214 {
215         /* Ensure that the msg length is correct. */
216         if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
217                 DEBUG(0,("process_local_message: incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, \
218 should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
219                 return False;
220         }
221
222         memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
223         memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
224
225         DEBUG(5,("process_local_message: kernel oplock break request for \
226 file dev = %x, inode = %.0f\n", (unsigned int)*dev, (double)*inode));
227
228         return True;
229 }
230
231
232 /****************************************************************************
233 see if a oplock message is waiting
234 ****************************************************************************/
235 static BOOL linux_oplock_msg_waiting(fd_set *fds)
236 {
237         return signals_processed != signals_received;
238 }
239
240
241 /****************************************************************************
242 setup kernel oplocks
243 ****************************************************************************/
244 struct kernel_oplocks *linux_init_kernel_oplocks(void) 
245 {
246         static struct kernel_oplocks koplocks;
247         struct sigaction act;
248
249         act.sa_handler = NULL;
250         act.sa_sigaction = sigio_handler;
251         act.sa_flags = SA_SIGINFO;
252         if (sigaction(SIGIO, &act, NULL) != 0) {
253                 DEBUG(0,("Failed to setup SIGIO handler\n"));
254                 return NULL;
255         }
256
257         koplocks.receive_message = linux_oplock_receive_message;
258         koplocks.set_oplock = linux_set_kernel_oplock;
259         koplocks.release_oplock = linux_release_kernel_oplock;
260         koplocks.parse_message = linux_kernel_oplock_parse;
261         koplocks.msg_waiting = linux_oplock_msg_waiting;
262         koplocks.notification_fd = -1;
263
264         return &koplocks;
265 }
266
267
268
269 #else
270  void oplock_linux_dummy(void) {}
271 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */
272
273 #undef OLD_NTDOMAIN
274