a first pass at Linux kernel oplocks support
[gd/samba-autobuild/.git] / source3 / smbd / oplock_linux.c
1 #define OLD_NTDOMAIN 1
2 #if HAVE_KERNEL_OPLOCKS_LINUX
3
4 /* 
5    Unix SMB/Netbios implementation.
6    Version 3.0
7    kernel oplock processing for Linux
8    Copyright (C) Andrew Tridgell 2000
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 extern int DEBUGLEVEL;
28
29 static unsigned signals_received;
30 static unsigned signals_processed;
31 static int fd_pending; /* the fd of the current pending SIGIO */
32
33 /****************************************************************************
34 handle a SIGIO, incrementing the signals_received and blocking SIGIO
35 ****************************************************************************/
36 static void sigio_handler(int signal, siginfo_t *info, void *unused)
37 {
38         fd_pending = info->si_fd;
39         signals_received++;
40         BlockSignals(True, SIGIO);
41 }
42
43 /****************************************************************************
44  * Deal with the Linux kernel <--> smbd
45  * oplock break protocol.
46 ****************************************************************************/
47 static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
48 {
49         SMB_DEV_T dev;
50         SMB_INO_T inode;
51         SMB_STRUCT_STAT sbuf;
52         BOOL ret;
53
54         if (signals_received == signals_processed) return False;
55
56         if (sys_fstat(fd_pending,&sbuf) == -1) {
57                 DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", fd_pending));
58                 ret = False;
59                 goto out;
60         }
61
62         dev = sbuf.st_dev;
63         inode = sbuf.st_ino;
64      
65         DEBUG(5,("receive_local_message: kernel oplock break request received for \
66 dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ));
67      
68         /*
69          * Create a kernel oplock break message.
70          */
71      
72         /* Setup the message header */
73         SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
74         SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
75      
76         buffer += OPBRK_CMD_HEADER_LEN;
77      
78         SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
79      
80         memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&dev, sizeof(dev));
81         memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&inode, sizeof(inode));       
82
83  out:
84         /* now we can receive more signals */
85         fd_pending = -1;
86         signals_processed++;
87         BlockSignals(False, SIGIO);
88      
89         return True;
90 }
91
92
93 /****************************************************************************
94  Attempt to set an kernel oplock on a file.
95 ****************************************************************************/
96 static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
97 {
98         if (fcntl(fsp->fd, F_SETLEASE, F_WRLCK) == -1) {
99                 DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
100 inode = %.0f.\n",
101                          fsp->fsp_name, fsp->fd, (unsigned int)fsp->dev, (double)fsp->inode));
102                 return False;
103         }
104         
105         DEBUG(10,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f\n",
106                   fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode));
107
108         return True;
109 }
110
111
112 /****************************************************************************
113  Release a kernel oplock on a file.
114 ****************************************************************************/
115 static void linux_release_kernel_oplock(files_struct *fsp)
116 {
117         if (DEBUGLVL(10)) {
118                 /*
119                  * Check and print out the current kernel
120                  * oplock state of this file.
121                  */
122                 int state = fcntl(fsp->fd, F_GETLEASE, 0);
123                 dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f has kernel \
124 oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
125                         (double)fsp->inode, state );
126         }
127
128         /*
129          * Remove the kernel oplock on this file.
130          */
131         if (fcntl(fsp->fd, F_SETLEASE, F_UNLCK) == -1) {
132                 if (DEBUGLVL(0)) {
133                         dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " );
134                         dbgtext("%s, dev = %x, inode = %.0f. Error was %s\n",
135                                 fsp->fsp_name, (unsigned int)fsp->dev, 
136                                 (double)fsp->inode, strerror(errno) );
137                 }
138         }
139 }
140
141
142 /****************************************************************************
143 parse a kernel oplock message
144 ****************************************************************************/
145 static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode, SMB_DEV_T *dev)
146 {
147         /* Ensure that the msg length is correct. */
148         if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
149                 DEBUG(0,("process_local_message: incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, \
150 should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
151                 return False;
152         }
153
154         memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
155         memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
156
157         DEBUG(5,("process_local_message: kernel oplock break request for \
158 file dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode));
159
160         return True;
161 }
162
163
164 /****************************************************************************
165 see if a oplock message is waiting
166 ****************************************************************************/
167 static BOOL linux_oplock_msg_waiting(fd_set *fds)
168 {
169         return signals_processed != signals_received;
170 }
171
172
173 /****************************************************************************
174 setup kernel oplocks
175 ****************************************************************************/
176 struct kernel_oplocks *linux_init_kernel_oplocks(void) 
177 {
178         static struct kernel_oplocks koplocks;
179         struct sigaction act;
180
181         act.sa_handler = NULL;
182         act.sa_sigaction = sigio_handler;
183         act.sa_flags = SA_SIGINFO;
184         if (sigaction(SIGIO, &act, NULL) != 0) {
185                 DEBUG(0,("Failed to setup SIGIO handler\n"));
186                 return NULL;
187         }
188
189         koplocks.receive_message = linux_oplock_receive_message;
190         koplocks.set_oplock = linux_set_kernel_oplock;
191         koplocks.release_oplock = linux_release_kernel_oplock;
192         koplocks.parse_message = linux_kernel_oplock_parse;
193         koplocks.msg_waiting = linux_oplock_msg_waiting;
194         koplocks.notification_fd = -1;
195
196         return &koplocks;
197 }
198
199
200
201 #else
202  void oplock_linux_dummy(void) {}
203 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */
204
205 #undef OLD_NTDOMAIN