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 
pvfs_oplock_release_internal(struct pvfs_file_handle * h,uint8_t oplock_break)38 static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
39 					     uint8_t oplock_break)
40 {
41 	struct odb_lock *olck;
42 	NTSTATUS status;
43 
44 	if (h->fd == -1) {
45 		return NT_STATUS_FILE_IS_A_DIRECTORY;
46 	}
47 
48 	if (!h->have_opendb_entry) {
49 		return NT_STATUS_FOOBAR;
50 	}
51 
52 	if (!h->oplock) {
53 		return NT_STATUS_FOOBAR;
54 	}
55 
56 	olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
57 	if (olck == NULL) {
58 		DEBUG(0,("Unable to lock opendb for oplock update\n"));
59 		return NT_STATUS_FOOBAR;
60 	}
61 
62 	if (oplock_break == OPLOCK_BREAK_TO_NONE) {
63 		h->oplock->level = OPLOCK_NONE;
64 	} else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
65 		h->oplock->level = OPLOCK_LEVEL_II;
66 	} else {
67 		/* fallback to level II in case of a invalid value */
68 		DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
69 		h->oplock->level = OPLOCK_LEVEL_II;
70 	}
71 	status = odb_update_oplock(olck, h, h->oplock->level);
72 	if (!NT_STATUS_IS_OK(status)) {
73 		DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
74 			 h->name->full_name, nt_errstr(status)));
75 		talloc_free(olck);
76 		return status;
77 	}
78 
79 	talloc_free(olck);
80 
81 	/* after a break to none, we no longer have an oplock attached */
82 	if (h->oplock->level == OPLOCK_NONE) {
83 		talloc_free(h->oplock);
84 		h->oplock = NULL;
85 	}
86 
87 	return NT_STATUS_OK;
88 }
89 
90 /*
91   receive oplock breaks and forward them to the client
92 */
pvfs_oplock_break(struct pvfs_oplock * opl,uint8_t level)93 static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
94 {
95 	NTSTATUS status;
96 	struct pvfs_file *f = opl->file;
97 	struct pvfs_file_handle *h = opl->handle;
98 	struct pvfs_state *pvfs = h->pvfs;
99 	struct timeval cur = timeval_current();
100 	struct timeval *last = NULL;
101 	struct timeval end;
102 
103 	switch (level) {
104 	case OPLOCK_BREAK_TO_LEVEL_II:
105 		last = &opl->break_to_level_II;
106 		break;
107 	case OPLOCK_BREAK_TO_NONE:
108 		last = &opl->break_to_none;
109 		break;
110 	}
111 
112 	if (!last) {
113 		DEBUG(0,("%s: got unexpected level[0x%02X]\n",
114 			__FUNCTION__, level));
115 		return;
116 	}
117 
118 	if (timeval_is_zero(last)) {
119 		/*
120 		 * this is the first break we for this level
121 		 * remember the time
122 		 */
123 		*last = cur;
124 
125 		DEBUG(5,("%s: sending oplock break level %d for '%s' %p\n",
126 			__FUNCTION__, level, h->name->original_name, h));
127 		status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
128 		if (!NT_STATUS_IS_OK(status)) {
129 			DEBUG(0,("%s: sending oplock break failed: %s\n",
130 				__FUNCTION__, nt_errstr(status)));
131 		}
132 		return;
133 	}
134 
135 	end = timeval_add(last, pvfs->oplock_break_timeout, 0);
136 
137 	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 		DEBUG(0,("%s: do not resend oplock break level %d for '%s' %p\n",
143 			__FUNCTION__, level, h->name->original_name, h));
144 		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 	DEBUG(0,("%s: auto release oplock level %d for '%s' %p\n",
153 		__FUNCTION__, level, h->name->original_name, h));
154 	status = pvfs_oplock_release_internal(h, level);
155 	if (!NT_STATUS_IS_OK(status)) {
156 		DEBUG(0,("%s: failed to auto release the oplock[0x%02X]: %s\n",
157 			__FUNCTION__, level, nt_errstr(status)));
158 	}
159 }
160 
pvfs_oplock_break_dispatch(struct imessaging_context * msg,void * private_data,uint32_t msg_type,struct server_id src,size_t num_fds,int * fds,DATA_BLOB * data)161 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 	struct pvfs_oplock *opl = talloc_get_type(private_data,
170 						  struct pvfs_oplock);
171 	struct opendb_oplock_break opb;
172 
173 	if (num_fds != 0) {
174 		DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
175 		return;
176 	}
177 
178 	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 	if (data->length == sizeof(struct opendb_oplock_break)) {
184 		struct opendb_oplock_break *p;
185 		p = (struct opendb_oplock_break *)data->data;
186 		opb = *p;
187 	} else {
188 		DEBUG(0,("%s: ignore oplock break with length[%u]\n",
189 			 __location__, (unsigned)data->length));
190 		return;
191 	}
192 	if (opb.file_handle != opl->handle) {
193 		return;
194 	}
195 
196 	/*
197 	 * maybe we should use ntvfs_setup_async()
198 	 */
199 	pvfs_oplock_break(opl, opb.level);
200 }
201 
pvfs_oplock_destructor(struct pvfs_oplock * opl)202 static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
203 {
204 	imessaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
205 	return 0;
206 }
207 
pvfs_setup_oplock(struct pvfs_file * f,uint32_t oplock_granted)208 NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
209 {
210 	NTSTATUS status;
211 	struct pvfs_oplock *opl;
212 	uint32_t level = OPLOCK_NONE;
213 
214 	f->handle->oplock = NULL;
215 
216 	switch (oplock_granted) {
217 	case EXCLUSIVE_OPLOCK_RETURN:
218 		level = OPLOCK_EXCLUSIVE;
219 		break;
220 	case BATCH_OPLOCK_RETURN:
221 		level = OPLOCK_BATCH;
222 		break;
223 	case LEVEL_II_OPLOCK_RETURN:
224 		level = OPLOCK_LEVEL_II;
225 		break;
226 	}
227 
228 	if (level == OPLOCK_NONE) {
229 		return NT_STATUS_OK;
230 	}
231 
232 	opl = talloc_zero(f->handle, struct pvfs_oplock);
233 	NT_STATUS_HAVE_NO_MEMORY(opl);
234 
235 	opl->handle	= f->handle;
236 	opl->file	= f;
237 	opl->level	= level;
238 	opl->msg_ctx	= f->pvfs->ntvfs->ctx->msg_ctx;
239 
240 	status = imessaging_register(opl->msg_ctx,
241 				    opl,
242 				    MSG_NTVFS_OPLOCK_BREAK,
243 				    pvfs_oplock_break_dispatch);
244 	NT_STATUS_NOT_OK_RETURN(status);
245 
246 	/* destructor */
247 	talloc_set_destructor(opl, pvfs_oplock_destructor);
248 
249 	f->handle->oplock = opl;
250 
251 	return NT_STATUS_OK;
252 }
253 
pvfs_oplock_release(struct ntvfs_module_context * ntvfs,struct ntvfs_request * req,union smb_lock * lck)254 NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
255 			     struct ntvfs_request *req, union smb_lock *lck)
256 {
257 	struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
258 				  struct pvfs_state);
259 	struct pvfs_file *f;
260 	uint8_t oplock_break;
261 	NTSTATUS status;
262 
263 	f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
264 	if (!f) {
265 		return NT_STATUS_INVALID_HANDLE;
266 	}
267 
268 	oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
269 
270 	status = pvfs_oplock_release_internal(f->handle, oplock_break);
271 	if (!NT_STATUS_IS_OK(status)) {
272 		DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
273 			__FUNCTION__, oplock_break, nt_errstr(status)));
274 		return status;
275 	}
276 
277 	return NT_STATUS_OK;
278 }
279 
pvfs_break_level2_oplocks(struct pvfs_file * f)280 NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
281 {
282 	struct pvfs_file_handle *h = f->handle;
283 	struct odb_lock *olck;
284 	NTSTATUS status;
285 
286 	if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
287 		return NT_STATUS_OK;
288 	}
289 
290 	olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
291 	if (olck == NULL) {
292 		DEBUG(0,("Unable to lock opendb for oplock update\n"));
293 		return NT_STATUS_FOOBAR;
294 	}
295 
296 	status = odb_break_oplocks(olck);
297 	if (!NT_STATUS_IS_OK(status)) {
298 		DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
299 			 h->name->full_name, nt_errstr(status)));
300 		talloc_free(olck);
301 		return status;
302 	}
303 
304 	talloc_free(olck);
305 
306 	return NT_STATUS_OK;
307 }
308