source: branches/samba-3.0/source/smbd/oplock_linux.c

Last change on this file was 140, checked in by Paul Smedley, 17 years ago

Update branch to 3.0.31 release

File size: 7.3 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 kernel oplock processing for Linux
4 Copyright (C) Andrew Tridgell 2000
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#define DBGC_CLASS DBGC_LOCKING
22#include "includes.h"
23
24#if HAVE_KERNEL_OPLOCKS_LINUX
25
26static SIG_ATOMIC_T signals_received;
27#define FD_PENDING_SIZE 100
28static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
29
30#ifndef F_SETLEASE
31#define F_SETLEASE 1024
32#endif
33
34#ifndef F_GETLEASE
35#define F_GETLEASE 1025
36#endif
37
38#ifndef CAP_LEASE
39#define CAP_LEASE 28
40#endif
41
42#ifndef RT_SIGNAL_LEASE
43#define RT_SIGNAL_LEASE (SIGRTMIN+1)
44#endif
45
46#ifndef F_SETSIG
47#define F_SETSIG 10
48#endif
49
50/****************************************************************************
51 Handle a LEASE signal, incrementing the signals_received and blocking the signal.
52****************************************************************************/
53
54static void signal_handler(int sig, siginfo_t *info, void *unused)
55{
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(RT_SIGNAL_LEASE);
61}
62
63/*
64 Call to set the kernel lease signal handler
65*/
66int linux_set_lease_sighandler(int fd)
67{
68 if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
69 DEBUG(3,("Failed to set signal handler for kernel lease\n"));
70 return -1;
71 }
72
73 return 0;
74}
75
76/****************************************************************************
77 Call SETLEASE. If we get EACCES then we try setting up the right capability and
78 try again.
79 Use the SMB_VFS_LINUX_SETLEASE instead of this call directly.
80****************************************************************************/
81
82int linux_setlease(int fd, int leasetype)
83{
84 int ret;
85
86 ret = fcntl(fd, F_SETLEASE, leasetype);
87 if (ret == -1 && errno == EACCES) {
88 set_effective_capability(LEASE_CAPABILITY);
89 ret = fcntl(fd, F_SETLEASE, leasetype);
90 }
91
92 return ret;
93}
94
95/****************************************************************************
96 * Deal with the Linux kernel <--> smbd
97 * oplock break protocol.
98****************************************************************************/
99
100static files_struct *linux_oplock_receive_message(fd_set *fds)
101{
102 int fd;
103 files_struct *fsp;
104
105 BlockSignals(True, RT_SIGNAL_LEASE);
106 fd = fd_pending_array[0];
107 fsp = file_find_fd(fd);
108 fd_pending_array[0] = (SIG_ATOMIC_T)-1;
109 if (signals_received > 1)
110 memmove(CONST_DISCARD(void *, &fd_pending_array[0]),
111 CONST_DISCARD(void *, &fd_pending_array[1]),
112 sizeof(SIG_ATOMIC_T)*(signals_received-1));
113 signals_received--;
114 /* now we can receive more signals */
115 BlockSignals(False, RT_SIGNAL_LEASE);
116
117 return fsp;
118}
119
120/****************************************************************************
121 Attempt to set an kernel oplock on a file.
122****************************************************************************/
123
124static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
125{
126 if ( SMB_VFS_LINUX_SETLEASE(fsp,fsp->fh->fd, F_WRLCK) == -1) {
127 DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, "
128 "fd = %d, dev = %x, inode = %.0f. (%s)\n",
129 fsp->fsp_name, fsp->fh->fd,
130 (unsigned int)fsp->dev, (double)fsp->inode,
131 strerror(errno)));
132 return False;
133 }
134
135 DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, "
136 "dev = %x, inode = %.0f, file_id = %lu\n",
137 fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode,
138 fsp->fh->file_id));
139
140 return True;
141}
142
143/****************************************************************************
144 Release a kernel oplock on a file.
145****************************************************************************/
146
147static void linux_release_kernel_oplock(files_struct *fsp)
148{
149 if (DEBUGLVL(10)) {
150 /*
151 * Check and print out the current kernel
152 * oplock state of this file.
153 */
154 int state = fcntl(fsp->fh->fd, F_GETLEASE, 0);
155 dbgtext("linux_release_kernel_oplock: file %s, dev = %x, "
156 "inode = %.0f file_id = %lu has kernel oplock state "
157 "of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
158 (double)fsp->inode, fsp->fh->file_id, state );
159 }
160
161 /*
162 * Remove the kernel oplock on this file.
163 */
164 if ( SMB_VFS_LINUX_SETLEASE(fsp,fsp->fh->fd, F_UNLCK) == -1) {
165 if (DEBUGLVL(0)) {
166 dbgtext("linux_release_kernel_oplock: Error when "
167 "removing kernel oplock on file " );
168 dbgtext("%s, dev = %x, inode = %.0f, file_id = %lu. "
169 "Error was %s\n", fsp->fsp_name,
170 (unsigned int)fsp->dev, (double)fsp->inode,
171 fsp->fh->file_id, strerror(errno) );
172 }
173 }
174}
175
176/****************************************************************************
177 See if a oplock message is waiting.
178****************************************************************************/
179
180static BOOL linux_oplock_msg_waiting(fd_set *fds)
181{
182 return signals_received != 0;
183}
184
185/****************************************************************************
186 See if the kernel supports oplocks.
187****************************************************************************/
188
189static BOOL linux_oplocks_available(void)
190{
191 int fd, ret;
192 fd = open("/dev/null", O_RDONLY);
193 if (fd == -1)
194 return False; /* uggh! */
195 ret = fcntl(fd, F_GETLEASE, 0);
196 close(fd);
197 return ret == F_UNLCK;
198}
199
200/****************************************************************************
201 Setup kernel oplocks.
202****************************************************************************/
203
204struct kernel_oplocks *linux_init_kernel_oplocks(void)
205{
206 static struct kernel_oplocks koplocks;
207 struct sigaction act;
208
209 if (!linux_oplocks_available()) {
210 DEBUG(3,("Linux kernel oplocks not available\n"));
211 return NULL;
212 }
213
214 ZERO_STRUCT(act);
215
216 act.sa_handler = NULL;
217 act.sa_sigaction = signal_handler;
218 act.sa_flags = SA_SIGINFO;
219 sigemptyset( &act.sa_mask );
220 if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
221 DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
222 return NULL;
223 }
224
225 koplocks.receive_message = linux_oplock_receive_message;
226 koplocks.set_oplock = linux_set_kernel_oplock;
227 koplocks.release_oplock = linux_release_kernel_oplock;
228 koplocks.msg_waiting = linux_oplock_msg_waiting;
229 koplocks.notification_fd = -1;
230
231 /* the signal can start off blocked due to a bug in bash */
232 BlockSignals(False, RT_SIGNAL_LEASE);
233
234 DEBUG(3,("Linux kernel oplocks enabled\n"));
235
236 return &koplocks;
237}
238#else
239 void oplock_linux_dummy(void);
240
241 void oplock_linux_dummy(void) {}
242#endif /* HAVE_KERNEL_OPLOCKS_LINUX */
Note: See TracBrowser for help on using the repository browser.