Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : POSIX NTVFS backend - oplock handling
5 :
6 : Copyright (C) Stefan Metzmacher 2008
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "lib/messaging/messaging.h"
24 : #include "lib/messaging/irpc.h"
25 : #include "system/time.h"
26 : #include "vfs_posix.h"
27 :
28 :
29 : struct pvfs_oplock {
30 : struct pvfs_file_handle *handle;
31 : struct pvfs_file *file;
32 : uint32_t level;
33 : struct timeval break_to_level_II;
34 : struct timeval break_to_none;
35 : struct imessaging_context *msg_ctx;
36 : };
37 :
38 86 : static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
39 : uint8_t oplock_break)
40 : {
41 0 : struct odb_lock *olck;
42 0 : NTSTATUS status;
43 :
44 86 : if (h->fd == -1) {
45 0 : return NT_STATUS_FILE_IS_A_DIRECTORY;
46 : }
47 :
48 86 : if (!h->have_opendb_entry) {
49 0 : return NT_STATUS_FOOBAR;
50 : }
51 :
52 86 : if (!h->oplock) {
53 0 : return NT_STATUS_FOOBAR;
54 : }
55 :
56 86 : olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
57 86 : if (olck == NULL) {
58 0 : DEBUG(0,("Unable to lock opendb for oplock update\n"));
59 0 : return NT_STATUS_FOOBAR;
60 : }
61 :
62 86 : if (oplock_break == OPLOCK_BREAK_TO_NONE) {
63 18 : h->oplock->level = OPLOCK_NONE;
64 68 : } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
65 68 : h->oplock->level = OPLOCK_LEVEL_II;
66 : } else {
67 : /* fallback to level II in case of a invalid value */
68 0 : DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
69 0 : h->oplock->level = OPLOCK_LEVEL_II;
70 : }
71 86 : status = odb_update_oplock(olck, h, h->oplock->level);
72 86 : if (!NT_STATUS_IS_OK(status)) {
73 0 : DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
74 : h->name->full_name, nt_errstr(status)));
75 0 : talloc_free(olck);
76 0 : return status;
77 : }
78 :
79 86 : talloc_free(olck);
80 :
81 : /* after a break to none, we no longer have an oplock attached */
82 86 : if (h->oplock->level == OPLOCK_NONE) {
83 18 : talloc_free(h->oplock);
84 18 : h->oplock = NULL;
85 : }
86 :
87 86 : return NT_STATUS_OK;
88 : }
89 :
90 : /*
91 : receive oplock breaks and forward them to the client
92 : */
93 108 : static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
94 : {
95 0 : NTSTATUS status;
96 108 : struct pvfs_file *f = opl->file;
97 108 : struct pvfs_file_handle *h = opl->handle;
98 108 : struct pvfs_state *pvfs = h->pvfs;
99 108 : struct timeval cur = timeval_current();
100 108 : struct timeval *last = NULL;
101 0 : struct timeval end;
102 :
103 108 : switch (level) {
104 75 : case OPLOCK_BREAK_TO_LEVEL_II:
105 75 : last = &opl->break_to_level_II;
106 75 : break;
107 33 : case OPLOCK_BREAK_TO_NONE:
108 33 : last = &opl->break_to_none;
109 33 : break;
110 : }
111 :
112 108 : if (!last) {
113 0 : DEBUG(0,("%s: got unexpected level[0x%02X]\n",
114 : __FUNCTION__, level));
115 107 : return;
116 : }
117 :
118 108 : if (timeval_is_zero(last)) {
119 : /*
120 : * this is the first break we for this level
121 : * remember the time
122 : */
123 104 : *last = cur;
124 :
125 104 : DEBUG(5,("%s: sending oplock break level %d for '%s' %p\n",
126 : __FUNCTION__, level, h->name->original_name, h));
127 104 : status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
128 104 : if (!NT_STATUS_IS_OK(status)) {
129 0 : DEBUG(0,("%s: sending oplock break failed: %s\n",
130 : __FUNCTION__, nt_errstr(status)));
131 : }
132 104 : return;
133 : }
134 :
135 4 : end = timeval_add(last, pvfs->oplock_break_timeout, 0);
136 :
137 4 : if (timeval_compare(&cur, &end) < 0) {
138 : /*
139 : * If it's not expired just ignore the break
140 : * as we already sent the break request to the client
141 : */
142 3 : DEBUG(0,("%s: do not resend oplock break level %d for '%s' %p\n",
143 : __FUNCTION__, level, h->name->original_name, h));
144 3 : return;
145 : }
146 :
147 : /*
148 : * If the client did not send a release within the
149 : * oplock break timeout time frame we auto release
150 : * the oplock
151 : */
152 1 : DEBUG(0,("%s: auto release oplock level %d for '%s' %p\n",
153 : __FUNCTION__, level, h->name->original_name, h));
154 1 : status = pvfs_oplock_release_internal(h, level);
155 1 : if (!NT_STATUS_IS_OK(status)) {
156 0 : DEBUG(0,("%s: failed to auto release the oplock[0x%02X]: %s\n",
157 : __FUNCTION__, level, nt_errstr(status)));
158 : }
159 : }
160 :
161 108 : static void pvfs_oplock_break_dispatch(struct imessaging_context *msg,
162 : void *private_data,
163 : uint32_t msg_type,
164 : struct server_id src,
165 : size_t num_fds,
166 : int *fds,
167 : DATA_BLOB *data)
168 : {
169 108 : struct pvfs_oplock *opl = talloc_get_type(private_data,
170 : struct pvfs_oplock);
171 0 : struct opendb_oplock_break opb;
172 :
173 108 : if (num_fds != 0) {
174 0 : DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
175 0 : return;
176 : }
177 :
178 108 : ZERO_STRUCT(opb);
179 :
180 : /* we need to check that this one is for us. See
181 : imessaging_send_ptr() for the other side of this.
182 : */
183 108 : if (data->length == sizeof(struct opendb_oplock_break)) {
184 0 : struct opendb_oplock_break *p;
185 108 : p = (struct opendb_oplock_break *)data->data;
186 108 : opb = *p;
187 : } else {
188 0 : DEBUG(0,("%s: ignore oplock break with length[%u]\n",
189 : __location__, (unsigned)data->length));
190 0 : return;
191 : }
192 108 : if (opb.file_handle != opl->handle) {
193 0 : return;
194 : }
195 :
196 : /*
197 : * maybe we should use ntvfs_setup_async()
198 : */
199 108 : pvfs_oplock_break(opl, opb.level);
200 : }
201 :
202 164 : static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
203 : {
204 164 : imessaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
205 164 : return 0;
206 : }
207 :
208 164 : NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
209 : {
210 0 : NTSTATUS status;
211 0 : struct pvfs_oplock *opl;
212 164 : uint32_t level = OPLOCK_NONE;
213 :
214 164 : f->handle->oplock = NULL;
215 :
216 164 : switch (oplock_granted) {
217 31 : case EXCLUSIVE_OPLOCK_RETURN:
218 31 : level = OPLOCK_EXCLUSIVE;
219 31 : break;
220 86 : case BATCH_OPLOCK_RETURN:
221 86 : level = OPLOCK_BATCH;
222 86 : break;
223 47 : case LEVEL_II_OPLOCK_RETURN:
224 47 : level = OPLOCK_LEVEL_II;
225 47 : break;
226 : }
227 :
228 164 : if (level == OPLOCK_NONE) {
229 0 : return NT_STATUS_OK;
230 : }
231 :
232 164 : opl = talloc_zero(f->handle, struct pvfs_oplock);
233 164 : NT_STATUS_HAVE_NO_MEMORY(opl);
234 :
235 164 : opl->handle = f->handle;
236 164 : opl->file = f;
237 164 : opl->level = level;
238 164 : opl->msg_ctx = f->pvfs->ntvfs->ctx->msg_ctx;
239 :
240 164 : status = imessaging_register(opl->msg_ctx,
241 : opl,
242 : MSG_NTVFS_OPLOCK_BREAK,
243 : pvfs_oplock_break_dispatch);
244 164 : NT_STATUS_NOT_OK_RETURN(status);
245 :
246 : /* destructor */
247 164 : talloc_set_destructor(opl, pvfs_oplock_destructor);
248 :
249 164 : f->handle->oplock = opl;
250 :
251 164 : return NT_STATUS_OK;
252 : }
253 :
254 85 : NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
255 : struct ntvfs_request *req, union smb_lock *lck)
256 : {
257 85 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
258 : struct pvfs_state);
259 0 : struct pvfs_file *f;
260 0 : uint8_t oplock_break;
261 0 : NTSTATUS status;
262 :
263 85 : f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
264 85 : if (!f) {
265 0 : return NT_STATUS_INVALID_HANDLE;
266 : }
267 :
268 85 : oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
269 :
270 85 : status = pvfs_oplock_release_internal(f->handle, oplock_break);
271 85 : if (!NT_STATUS_IS_OK(status)) {
272 0 : DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
273 : __FUNCTION__, oplock_break, nt_errstr(status)));
274 0 : return status;
275 : }
276 :
277 85 : return NT_STATUS_OK;
278 : }
279 :
280 31552 : NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
281 : {
282 31552 : struct pvfs_file_handle *h = f->handle;
283 0 : struct odb_lock *olck;
284 0 : NTSTATUS status;
285 :
286 31552 : if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
287 14 : return NT_STATUS_OK;
288 : }
289 :
290 31538 : olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
291 31538 : if (olck == NULL) {
292 0 : DEBUG(0,("Unable to lock opendb for oplock update\n"));
293 0 : return NT_STATUS_FOOBAR;
294 : }
295 :
296 31538 : status = odb_break_oplocks(olck);
297 31538 : if (!NT_STATUS_IS_OK(status)) {
298 0 : DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
299 : h->name->full_name, nt_errstr(status)));
300 0 : talloc_free(olck);
301 0 : return status;
302 : }
303 :
304 31538 : talloc_free(olck);
305 :
306 31538 : return NT_STATUS_OK;
307 : }
|