Line data Source code
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 3 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, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #define DBGC_CLASS DBGC_LOCKING
21 : #include "includes.h"
22 : #include "system/filesys.h"
23 : #include "smbd/smbd.h"
24 : #include "smbd/globals.h"
25 :
26 : #ifdef HAVE_KERNEL_OPLOCKS_LINUX
27 :
28 : #ifndef RT_SIGNAL_LEASE
29 : #define RT_SIGNAL_LEASE (SIGRTMIN+1)
30 : #endif
31 :
32 : /*
33 : * Call to set the kernel lease signal handler
34 : */
35 12 : int linux_set_lease_sighandler(int fd)
36 : {
37 12 : if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
38 0 : DBG_NOTICE("Failed to set signal handler for kernel lease\n");
39 0 : return -1;
40 : }
41 :
42 12 : return 0;
43 : }
44 :
45 : /****************************************************************************
46 : Call SETLEASE. If we get EACCES then we try setting up the right capability and
47 : try again.
48 : Use the SMB_VFS_LINUX_SETLEASE instead of this call directly.
49 : ****************************************************************************/
50 :
51 12 : int linux_setlease(int fd, int leasetype)
52 : {
53 0 : int ret;
54 12 : int saved_errno = 0;
55 :
56 : /*
57 : * Ensure the lease owner is root to allow
58 : * correct delivery of lease-break signals.
59 : */
60 :
61 12 : become_root();
62 :
63 : /* First set the signal handler. */
64 12 : if (linux_set_lease_sighandler(fd) == -1) {
65 0 : saved_errno = errno;
66 0 : ret = -1;
67 0 : goto out;
68 : }
69 12 : ret = fcntl(fd, F_SETLEASE, leasetype);
70 12 : if (ret == -1) {
71 0 : saved_errno = errno;
72 : }
73 :
74 12 : out:
75 :
76 12 : unbecome_root();
77 :
78 12 : if (ret == -1) {
79 0 : errno = saved_errno;
80 : }
81 12 : return ret;
82 : }
83 :
84 : /****************************************************************************
85 : * Deal with the Linux kernel <--> smbd
86 : * oplock break protocol.
87 : ****************************************************************************/
88 :
89 4 : static void linux_oplock_signal_handler(struct tevent_context *ev_ctx,
90 : struct tevent_signal *se,
91 : int signum, int count,
92 : void *_info, void *private_data)
93 : {
94 0 : struct kernel_oplocks *ctx =
95 4 : talloc_get_type_abort(private_data,
96 : struct kernel_oplocks);
97 0 : struct smbd_server_connection *sconn =
98 4 : talloc_get_type_abort(ctx->private_data,
99 : struct smbd_server_connection);
100 4 : siginfo_t *info = (siginfo_t *)_info;
101 4 : int fd = info->si_fd;
102 0 : files_struct *fsp;
103 :
104 4 : fsp = file_find_fd(sconn, fd);
105 4 : if (fsp == NULL) {
106 0 : DBG_ERR("linux_oplock_signal_handler: failed to find fsp for file fd=%d (file was closed ?)\n", fd );
107 0 : return;
108 : }
109 4 : break_kernel_oplock(sconn->msg_ctx, fsp);
110 : }
111 :
112 : /****************************************************************************
113 : Attempt to set an kernel oplock on a file.
114 : ****************************************************************************/
115 :
116 7 : static bool linux_set_kernel_oplock(struct kernel_oplocks *ctx,
117 : files_struct *fsp, int oplock_type)
118 : {
119 0 : struct file_id_buf idbuf;
120 :
121 7 : if ( SMB_VFS_LINUX_SETLEASE(fsp, F_WRLCK) == -1) {
122 0 : DBG_NOTICE("Refused oplock on file %s, "
123 : "fd = %d, file_id = %s. (%s)\n",
124 : fsp_str_dbg(fsp),
125 : fsp_get_io_fd(fsp),
126 : file_id_str_buf(fsp->file_id, &idbuf),
127 : strerror(errno));
128 0 : return False;
129 : }
130 :
131 7 : DBG_NOTICE("got kernel oplock on file %s, "
132 : "file_id = %s gen_id = %"PRIu64"\n",
133 : fsp_str_dbg(fsp),
134 : file_id_str_buf(fsp->file_id, &idbuf),
135 : fh_get_gen_id(fsp->fh));
136 :
137 7 : return True;
138 : }
139 :
140 : /****************************************************************************
141 : Release a kernel oplock on a file.
142 : ****************************************************************************/
143 :
144 7 : static void linux_release_kernel_oplock(struct kernel_oplocks *ctx,
145 : files_struct *fsp, int oplock_type)
146 : {
147 0 : struct file_id_buf idbuf;
148 :
149 7 : if (DEBUGLVL(DBGLVL_DEBUG)) {
150 : /*
151 : * Check and print out the current kernel
152 : * oplock state of this file.
153 : */
154 0 : int state = fcntl(fsp_get_io_fd(fsp), F_GETLEASE, 0);
155 0 : dbgtext("linux_release_kernel_oplock: file %s, file_id = %s "
156 : "gen_id = %"PRIu64" has kernel oplock state "
157 : "of %x.\n",
158 : fsp_str_dbg(fsp),
159 : file_id_str_buf(fsp->file_id, &idbuf),
160 : fh_get_gen_id(fsp->fh),
161 : state);
162 : }
163 :
164 : /*
165 : * Remove the kernel oplock on this file.
166 : */
167 7 : if ( SMB_VFS_LINUX_SETLEASE(fsp, F_UNLCK) == -1) {
168 0 : if (DEBUGLVL(DBGLVL_ERR)) {
169 0 : dbgtext("linux_release_kernel_oplock: Error when "
170 : "removing kernel oplock on file " );
171 0 : dbgtext("%s, file_id = %s, gen_id = %"PRIu64". "
172 : "Error was %s\n",
173 : fsp_str_dbg(fsp),
174 : file_id_str_buf(fsp->file_id, &idbuf),
175 : fh_get_gen_id(fsp->fh),
176 0 : strerror(errno));
177 : }
178 : }
179 7 : }
180 :
181 : /****************************************************************************
182 : See if the kernel supports oplocks.
183 : ****************************************************************************/
184 :
185 17 : static bool linux_oplocks_available(void)
186 : {
187 0 : int fd, ret;
188 17 : fd = open("/dev/null", O_RDONLY);
189 17 : if (fd == -1)
190 0 : return False; /* uggh! */
191 17 : ret = fcntl(fd, F_GETLEASE, 0);
192 17 : close(fd);
193 17 : return ret == F_UNLCK;
194 : }
195 :
196 : /****************************************************************************
197 : Setup kernel oplocks.
198 : ****************************************************************************/
199 :
200 : static const struct kernel_oplocks_ops linux_koplocks = {
201 : .set_oplock = linux_set_kernel_oplock,
202 : .release_oplock = linux_release_kernel_oplock,
203 : };
204 :
205 17 : struct kernel_oplocks *linux_init_kernel_oplocks(struct smbd_server_connection *sconn)
206 : {
207 0 : struct kernel_oplocks *ctx;
208 0 : struct tevent_signal *se;
209 :
210 17 : if (!linux_oplocks_available()) {
211 0 : DBG_NOTICE("Linux kernel oplocks not available\n");
212 0 : return NULL;
213 : }
214 :
215 17 : ctx = talloc_zero(sconn, struct kernel_oplocks);
216 17 : if (!ctx) {
217 0 : DBG_ERR("Linux Kernel oplocks talloc_Zero failed\n");
218 0 : return NULL;
219 : }
220 :
221 17 : ctx->ops = &linux_koplocks;
222 17 : ctx->private_data = sconn;
223 :
224 17 : se = tevent_add_signal(sconn->ev_ctx,
225 : ctx,
226 : RT_SIGNAL_LEASE, SA_SIGINFO,
227 : linux_oplock_signal_handler,
228 : ctx);
229 17 : if (!se) {
230 0 : DBG_ERR("Failed to setup RT_SIGNAL_LEASE handler\n");
231 0 : TALLOC_FREE(ctx);
232 0 : return NULL;
233 : }
234 :
235 17 : DBG_NOTICE("Linux kernel oplocks enabled\n");
236 :
237 17 : return ctx;
238 : }
239 : #else
240 : void oplock_linux_dummy(void);
241 :
242 : void oplock_linux_dummy(void) {}
243 : #endif /* HAVE_KERNEL_OPLOCKS_LINUX */
|