/src/samba/source3/smbd/oplock_linux.c
Line | Count | Source (jump to first uncovered 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 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 | 0 | #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 | 0 | #define RT_SIGNAL_LEASE (SIGRTMIN+1) |
30 | | #endif |
31 | | |
32 | | /* |
33 | | * Call to set the kernel lease signal handler |
34 | | */ |
35 | | int linux_set_lease_sighandler(int fd) |
36 | 0 | { |
37 | 0 | 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 | 0 | } |
41 | | |
42 | 0 | return 0; |
43 | 0 | } |
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 | | int linux_setlease(int fd, int leasetype) |
52 | 0 | { |
53 | 0 | int ret; |
54 | 0 | 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 | 0 | become_root(); |
62 | | |
63 | | /* First set the signal handler. */ |
64 | 0 | if (linux_set_lease_sighandler(fd) == -1) { |
65 | 0 | saved_errno = errno; |
66 | 0 | ret = -1; |
67 | 0 | goto out; |
68 | 0 | } |
69 | 0 | ret = fcntl(fd, F_SETLEASE, leasetype); |
70 | 0 | if (ret == -1) { |
71 | 0 | saved_errno = errno; |
72 | 0 | } |
73 | |
|
74 | 0 | out: |
75 | |
|
76 | 0 | unbecome_root(); |
77 | |
|
78 | 0 | if (ret == -1) { |
79 | 0 | errno = saved_errno; |
80 | 0 | } |
81 | 0 | return ret; |
82 | 0 | } |
83 | | |
84 | | /**************************************************************************** |
85 | | * Deal with the Linux kernel <--> smbd |
86 | | * oplock break protocol. |
87 | | ****************************************************************************/ |
88 | | |
89 | | 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 | 0 | { |
94 | 0 | struct kernel_oplocks *ctx = |
95 | 0 | talloc_get_type_abort(private_data, |
96 | 0 | struct kernel_oplocks); |
97 | 0 | struct smbd_server_connection *sconn = |
98 | 0 | talloc_get_type_abort(ctx->private_data, |
99 | 0 | struct smbd_server_connection); |
100 | 0 | siginfo_t *info = (siginfo_t *)_info; |
101 | 0 | int fd = info->si_fd; |
102 | 0 | files_struct *fsp; |
103 | |
|
104 | 0 | fsp = file_find_fd(sconn, fd); |
105 | 0 | 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 | 0 | } |
109 | 0 | break_kernel_oplock(sconn->msg_ctx, fsp); |
110 | 0 | } |
111 | | |
112 | | /**************************************************************************** |
113 | | Attempt to set an kernel oplock on a file. |
114 | | ****************************************************************************/ |
115 | | |
116 | | static bool linux_set_kernel_oplock(struct kernel_oplocks *ctx, |
117 | | files_struct *fsp, int oplock_type) |
118 | 0 | { |
119 | 0 | struct file_id_buf idbuf; |
120 | |
|
121 | 0 | if ( SMB_VFS_LINUX_SETLEASE(fsp, F_WRLCK) == -1) { |
122 | 0 | DBG_NOTICE("Refused oplock on file %s, " |
123 | 0 | "fd = %d, file_id = %s. (%s)\n", |
124 | 0 | fsp_str_dbg(fsp), |
125 | 0 | fsp_get_io_fd(fsp), |
126 | 0 | file_id_str_buf(fsp->file_id, &idbuf), |
127 | 0 | strerror(errno)); |
128 | 0 | return False; |
129 | 0 | } |
130 | | |
131 | 0 | DBG_NOTICE("got kernel oplock on file %s, " |
132 | 0 | "file_id = %s gen_id = %"PRIu64"\n", |
133 | 0 | fsp_str_dbg(fsp), |
134 | 0 | file_id_str_buf(fsp->file_id, &idbuf), |
135 | 0 | fh_get_gen_id(fsp->fh)); |
136 | |
|
137 | 0 | return True; |
138 | 0 | } |
139 | | |
140 | | /**************************************************************************** |
141 | | Release a kernel oplock on a file. |
142 | | ****************************************************************************/ |
143 | | |
144 | | static void linux_release_kernel_oplock(struct kernel_oplocks *ctx, |
145 | | files_struct *fsp, int oplock_type) |
146 | 0 | { |
147 | 0 | struct file_id_buf idbuf; |
148 | |
|
149 | 0 | 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 | 0 | "gen_id = %"PRIu64" has kernel oplock state " |
157 | 0 | "of %x.\n", |
158 | 0 | fsp_str_dbg(fsp), |
159 | 0 | file_id_str_buf(fsp->file_id, &idbuf), |
160 | 0 | fh_get_gen_id(fsp->fh), |
161 | 0 | state); |
162 | 0 | } |
163 | | |
164 | | /* |
165 | | * Remove the kernel oplock on this file. |
166 | | */ |
167 | 0 | 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 | 0 | "removing kernel oplock on file " ); |
171 | 0 | dbgtext("%s, file_id = %s, gen_id = %"PRIu64". " |
172 | 0 | "Error was %s\n", |
173 | 0 | fsp_str_dbg(fsp), |
174 | 0 | file_id_str_buf(fsp->file_id, &idbuf), |
175 | 0 | fh_get_gen_id(fsp->fh), |
176 | 0 | strerror(errno)); |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | /**************************************************************************** |
182 | | See if the kernel supports oplocks. |
183 | | ****************************************************************************/ |
184 | | |
185 | | static bool linux_oplocks_available(void) |
186 | 0 | { |
187 | 0 | int fd, ret; |
188 | 0 | fd = open("/dev/null", O_RDONLY); |
189 | 0 | if (fd == -1) |
190 | 0 | return False; /* uggh! */ |
191 | 0 | ret = fcntl(fd, F_GETLEASE, 0); |
192 | 0 | close(fd); |
193 | 0 | return ret == F_UNLCK; |
194 | 0 | } |
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 | | struct kernel_oplocks *linux_init_kernel_oplocks(struct smbd_server_connection *sconn) |
206 | 0 | { |
207 | 0 | struct kernel_oplocks *ctx; |
208 | 0 | struct tevent_signal *se; |
209 | |
|
210 | 0 | if (!linux_oplocks_available()) { |
211 | 0 | DBG_NOTICE("Linux kernel oplocks not available\n"); |
212 | 0 | return NULL; |
213 | 0 | } |
214 | | |
215 | 0 | ctx = talloc_zero(sconn, struct kernel_oplocks); |
216 | 0 | if (!ctx) { |
217 | 0 | DBG_ERR("Linux Kernel oplocks talloc_Zero failed\n"); |
218 | 0 | return NULL; |
219 | 0 | } |
220 | | |
221 | 0 | ctx->ops = &linux_koplocks; |
222 | 0 | ctx->private_data = sconn; |
223 | |
|
224 | 0 | se = tevent_add_signal(sconn->ev_ctx, |
225 | 0 | ctx, |
226 | 0 | RT_SIGNAL_LEASE, SA_SIGINFO, |
227 | 0 | linux_oplock_signal_handler, |
228 | 0 | ctx); |
229 | 0 | if (!se) { |
230 | 0 | DBG_ERR("Failed to setup RT_SIGNAL_LEASE handler\n"); |
231 | 0 | TALLOC_FREE(ctx); |
232 | 0 | return NULL; |
233 | 0 | } |
234 | | |
235 | 0 | DBG_NOTICE("Linux kernel oplocks enabled\n"); |
236 | |
|
237 | 0 | return ctx; |
238 | 0 | } |
239 | | #else |
240 | | void oplock_linux_dummy(void); |
241 | | |
242 | | void oplock_linux_dummy(void) {} |
243 | | #endif /* HAVE_KERNEL_OPLOCKS_LINUX */ |