2 Unix SMB/CIFS implementation.
3 kernel oplock processing for Linux
4 Copyright (C) Andrew Tridgell 2000
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 3 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #define DBGC_CLASS DBGC_LOCKING
23 #if HAVE_KERNEL_OPLOCKS_LINUX
25 static SIG_ATOMIC_T signals_received
;
26 #define FD_PENDING_SIZE 100
27 static SIG_ATOMIC_T fd_pending_array
[FD_PENDING_SIZE
];
30 #define F_SETLEASE 1024
34 #define F_GETLEASE 1025
41 #ifndef RT_SIGNAL_LEASE
42 #define RT_SIGNAL_LEASE (SIGRTMIN+1)
49 /****************************************************************************
50 Handle a LEASE signal, incrementing the signals_received and blocking the signal.
51 ****************************************************************************/
53 static void signal_handler(int sig
, siginfo_t
*info
, void *unused
)
55 if (signals_received
< FD_PENDING_SIZE
- 1) {
56 fd_pending_array
[signals_received
] = (SIG_ATOMIC_T
)info
->si_fd
;
58 } /* Else signal is lost. */
59 sys_select_signal(RT_SIGNAL_LEASE
);
63 * public function to get linux lease capability. Needed by some VFS modules (eg. gpfs.c)
65 void linux_set_lease_capability(void)
67 set_effective_capability(LEASE_CAPABILITY
);
71 * Call to set the kernel lease signal handler
73 int linux_set_lease_sighandler(int fd
)
75 if (fcntl(fd
, F_SETSIG
, RT_SIGNAL_LEASE
) == -1) {
76 DEBUG(3,("Failed to set signal handler for kernel lease\n"));
83 /****************************************************************************
84 Call SETLEASE. If we get EACCES then we try setting up the right capability and
86 Use the SMB_VFS_LINUX_SETLEASE instead of this call directly.
87 ****************************************************************************/
89 int linux_setlease(int fd
, int leasetype
)
93 ret
= fcntl(fd
, F_SETLEASE
, leasetype
);
94 if (ret
== -1 && errno
== EACCES
) {
95 set_effective_capability(LEASE_CAPABILITY
);
96 ret
= fcntl(fd
, F_SETLEASE
, leasetype
);
102 /****************************************************************************
103 * Deal with the Linux kernel <--> smbd
104 * oplock break protocol.
105 ****************************************************************************/
107 static files_struct
*linux_oplock_receive_message(fd_set
*fds
)
112 BlockSignals(True
, RT_SIGNAL_LEASE
);
113 fd
= fd_pending_array
[0];
114 fsp
= file_find_fd(fd
);
115 fd_pending_array
[0] = (SIG_ATOMIC_T
)-1;
116 if (signals_received
> 1)
117 memmove(CONST_DISCARD(void *, &fd_pending_array
[0]),
118 CONST_DISCARD(void *, &fd_pending_array
[1]),
119 sizeof(SIG_ATOMIC_T
)*(signals_received
-1));
121 /* now we can receive more signals */
122 BlockSignals(False
, RT_SIGNAL_LEASE
);
127 /****************************************************************************
128 Attempt to set an kernel oplock on a file.
129 ****************************************************************************/
131 static bool linux_set_kernel_oplock(files_struct
*fsp
, int oplock_type
)
133 if ( SMB_VFS_LINUX_SETLEASE(fsp
, F_WRLCK
) == -1) {
134 DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, "
135 "fd = %d, file_id = %s. (%s)\n",
136 fsp
->fsp_name
, fsp
->fh
->fd
,
137 file_id_string_tos(&fsp
->file_id
),
142 DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, "
143 "file_id = %s gen_id = %lu\n",
144 fsp
->fsp_name
, file_id_string_tos(&fsp
->file_id
),
150 /****************************************************************************
151 Release a kernel oplock on a file.
152 ****************************************************************************/
154 static void linux_release_kernel_oplock(files_struct
*fsp
)
158 * Check and print out the current kernel
159 * oplock state of this file.
161 int state
= fcntl(fsp
->fh
->fd
, F_GETLEASE
, 0);
162 dbgtext("linux_release_kernel_oplock: file %s, file_id = %s "
163 "gen_id = %lu has kernel oplock state "
164 "of %x.\n", fsp
->fsp_name
, file_id_string_tos(&fsp
->file_id
),
165 fsp
->fh
->gen_id
, state
);
169 * Remove the kernel oplock on this file.
171 if ( SMB_VFS_LINUX_SETLEASE(fsp
, F_UNLCK
) == -1) {
173 dbgtext("linux_release_kernel_oplock: Error when "
174 "removing kernel oplock on file " );
175 dbgtext("%s, file_id = %s, gen_id = %lu. "
176 "Error was %s\n", fsp
->fsp_name
,
177 file_id_string_tos(&fsp
->file_id
),
178 fsp
->fh
->gen_id
, strerror(errno
) );
183 /****************************************************************************
184 See if a oplock message is waiting.
185 ****************************************************************************/
187 static bool linux_oplock_msg_waiting(fd_set
*fds
)
189 return signals_received
!= 0;
192 /****************************************************************************
193 See if the kernel supports oplocks.
194 ****************************************************************************/
196 static bool linux_oplocks_available(void)
199 fd
= open("/dev/null", O_RDONLY
);
201 return False
; /* uggh! */
202 ret
= fcntl(fd
, F_GETLEASE
, 0);
204 return ret
== F_UNLCK
;
207 /****************************************************************************
208 Setup kernel oplocks.
209 ****************************************************************************/
211 struct kernel_oplocks
*linux_init_kernel_oplocks(void)
213 static struct kernel_oplocks koplocks
;
214 struct sigaction act
;
216 if (!linux_oplocks_available()) {
217 DEBUG(3,("Linux kernel oplocks not available\n"));
223 act
.sa_handler
= NULL
;
224 act
.sa_sigaction
= signal_handler
;
225 act
.sa_flags
= SA_SIGINFO
;
226 sigemptyset( &act
.sa_mask
);
227 if (sigaction(RT_SIGNAL_LEASE
, &act
, NULL
) != 0) {
228 DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
232 koplocks
.receive_message
= linux_oplock_receive_message
;
233 koplocks
.set_oplock
= linux_set_kernel_oplock
;
234 koplocks
.release_oplock
= linux_release_kernel_oplock
;
235 koplocks
.msg_waiting
= linux_oplock_msg_waiting
;
236 koplocks
.notification_fd
= -1;
238 /* the signal can start off blocked due to a bug in bash */
239 BlockSignals(False
, RT_SIGNAL_LEASE
);
241 DEBUG(3,("Linux kernel oplocks enabled\n"));
246 void oplock_linux_dummy(void);
248 void oplock_linux_dummy(void) {}
249 #endif /* HAVE_KERNEL_OPLOCKS_LINUX */