• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/ntvfs/posix/
1/*
2   Unix SMB/CIFS implementation.
3
4   POSIX NTVFS backend - locking
5
6   Copyright (C) Andrew Tridgell 2004
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 "vfs_posix.h"
24#include "system/time.h"
25#include "../lib/util/dlinklist.h"
26#include "messaging/messaging.h"
27
28
29/*
30  check if we can perform IO on a range that might be locked
31*/
32NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs,
33			 struct pvfs_file *f,
34			 uint32_t smbpid,
35			 uint64_t offset, uint64_t count,
36			 enum brl_type rw)
37{
38	if (!(pvfs->flags & PVFS_FLAG_STRICT_LOCKING)) {
39		return NT_STATUS_OK;
40	}
41
42	return brl_locktest(pvfs->brl_context,
43			    f->brl_handle,
44			    smbpid,
45			    offset, count, rw);
46}
47
48/* this state structure holds information about a lock we are waiting on */
49struct pvfs_pending_lock {
50	struct pvfs_pending_lock *next, *prev;
51	struct pvfs_state *pvfs;
52	union smb_lock *lck;
53	struct pvfs_file *f;
54	struct ntvfs_request *req;
55	int pending_lock;
56	struct pvfs_wait *wait_handle;
57	struct timeval end_time;
58};
59
60/*
61  a secondary attempt to setup a lock has failed - back out
62  the locks we did get and send an error
63*/
64static void pvfs_lock_async_failed(struct pvfs_state *pvfs,
65				   struct ntvfs_request *req,
66				   struct pvfs_file *f,
67				   struct smb_lock_entry *locks,
68				   int i,
69				   NTSTATUS status)
70{
71	/* undo the locks we just did */
72	for (i--;i>=0;i--) {
73		brl_unlock(pvfs->brl_context,
74			   f->brl_handle,
75			   locks[i].pid,
76			   locks[i].offset,
77			   locks[i].count);
78		f->lock_count--;
79	}
80	req->async_states->status = status;
81	req->async_states->send_fn(req);
82}
83
84
85/*
86  called when we receive a pending lock notification. It means that
87  either our lock timed out or someone else has unlocked a overlapping
88  range, so we should try the lock again. Note that on timeout we
89  do retry the lock, giving it a last chance.
90*/
91static void pvfs_pending_lock_continue(void *private_data, enum pvfs_wait_notice reason)
92{
93	struct pvfs_pending_lock *pending = talloc_get_type(private_data,
94					    struct pvfs_pending_lock);
95	struct pvfs_state *pvfs = pending->pvfs;
96	struct pvfs_file *f = pending->f;
97	struct ntvfs_request *req = pending->req;
98	union smb_lock *lck = pending->lck;
99	struct smb_lock_entry *locks;
100	enum brl_type rw;
101	NTSTATUS status;
102	int i;
103	bool timed_out;
104
105	timed_out = (reason != PVFS_WAIT_EVENT);
106
107	locks = lck->lockx.in.locks + lck->lockx.in.ulock_cnt;
108
109	if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
110		rw = READ_LOCK;
111	} else {
112		rw = WRITE_LOCK;
113	}
114
115	DLIST_REMOVE(f->pending_list, pending);
116
117	/* we don't retry on a cancel */
118	if (reason == PVFS_WAIT_CANCEL) {
119		status = NT_STATUS_FILE_LOCK_CONFLICT;
120	} else {
121		/*
122		 * here it's important to pass the pending pointer
123		 * because with this we'll get the correct error code
124		 * FILE_LOCK_CONFLICT in the error case
125		 */
126		status = brl_lock(pvfs->brl_context,
127				  f->brl_handle,
128				  locks[pending->pending_lock].pid,
129				  locks[pending->pending_lock].offset,
130				  locks[pending->pending_lock].count,
131				  rw, pending);
132	}
133	if (NT_STATUS_IS_OK(status)) {
134		f->lock_count++;
135		timed_out = false;
136	}
137
138	/* if we have failed and timed out, or succeeded, then we
139	   don't need the pending lock any more */
140	if (NT_STATUS_IS_OK(status) || timed_out) {
141		NTSTATUS status2;
142		status2 = brl_remove_pending(pvfs->brl_context,
143					     f->brl_handle, pending);
144		if (!NT_STATUS_IS_OK(status2)) {
145			DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2)));
146		}
147		talloc_free(pending->wait_handle);
148	}
149
150	if (!NT_STATUS_IS_OK(status)) {
151		if (timed_out) {
152			/* no more chances */
153			pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status);
154			talloc_free(pending);
155		} else {
156			/* we can try again */
157			DLIST_ADD(f->pending_list, pending);
158		}
159		return;
160	}
161
162	/* if we haven't timed out yet, then we can do more pending locks */
163	if (rw == READ_LOCK) {
164		rw = PENDING_READ_LOCK;
165	} else {
166		rw = PENDING_WRITE_LOCK;
167	}
168
169	/* we've now got the pending lock. try and get the rest, which might
170	   lead to more pending locks */
171	for (i=pending->pending_lock+1;i<lck->lockx.in.lock_cnt;i++) {
172		if (pending) {
173			pending->pending_lock = i;
174		}
175
176		status = brl_lock(pvfs->brl_context,
177				  f->brl_handle,
178				  locks[i].pid,
179				  locks[i].offset,
180				  locks[i].count,
181				  rw, pending);
182		if (!NT_STATUS_IS_OK(status)) {
183			if (pending) {
184				/* a timed lock failed - setup a wait message to handle
185				   the pending lock notification or a timeout */
186				pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
187									 pending->end_time,
188									 pvfs_pending_lock_continue,
189									 pending);
190				if (pending->wait_handle == NULL) {
191					pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY);
192					talloc_free(pending);
193				} else {
194					talloc_steal(pending, pending->wait_handle);
195					DLIST_ADD(f->pending_list, pending);
196				}
197				return;
198			}
199			pvfs_lock_async_failed(pvfs, req, f, locks, i, status);
200			talloc_free(pending);
201			return;
202		}
203
204		f->lock_count++;
205	}
206
207	/* we've managed to get all the locks. Tell the client */
208	req->async_states->status = NT_STATUS_OK;
209	req->async_states->send_fn(req);
210	talloc_free(pending);
211}
212
213
214/*
215  called when we close a file that might have locks
216*/
217void pvfs_lock_close(struct pvfs_state *pvfs, struct pvfs_file *f)
218{
219	struct pvfs_pending_lock *p, *next;
220
221	if (f->lock_count || f->pending_list) {
222		DEBUG(5,("pvfs_lock: removing %.0f locks on close\n",
223			 (double)f->lock_count));
224		brl_close(f->pvfs->brl_context, f->brl_handle);
225		f->lock_count = 0;
226	}
227
228	/* reply to all the pending lock requests, telling them the
229	   lock failed */
230	for (p=f->pending_list;p;p=next) {
231		next = p->next;
232		DLIST_REMOVE(f->pending_list, p);
233		p->req->async_states->status = NT_STATUS_RANGE_NOT_LOCKED;
234		p->req->async_states->send_fn(p->req);
235	}
236}
237
238
239/*
240  cancel a set of locks
241*/
242static NTSTATUS pvfs_lock_cancel(struct pvfs_state *pvfs, struct ntvfs_request *req, union smb_lock *lck,
243				 struct pvfs_file *f)
244{
245	struct pvfs_pending_lock *p;
246
247	for (p=f->pending_list;p;p=p->next) {
248		/* check if the lock request matches exactly - you can only cancel with exact matches */
249		if (p->lck->lockx.in.ulock_cnt == lck->lockx.in.ulock_cnt &&
250		    p->lck->lockx.in.lock_cnt  == lck->lockx.in.lock_cnt &&
251		    p->lck->lockx.in.file.ntvfs== lck->lockx.in.file.ntvfs &&
252		    p->lck->lockx.in.mode      == (lck->lockx.in.mode & ~LOCKING_ANDX_CANCEL_LOCK)) {
253			int i;
254
255			for (i=0;i<lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;i++) {
256				if (p->lck->lockx.in.locks[i].pid != lck->lockx.in.locks[i].pid ||
257				    p->lck->lockx.in.locks[i].offset != lck->lockx.in.locks[i].offset ||
258				    p->lck->lockx.in.locks[i].count != lck->lockx.in.locks[i].count) {
259					break;
260				}
261			}
262			if (i < lck->lockx.in.ulock_cnt) continue;
263
264			/* an exact match! we can cancel it, which is equivalent
265			   to triggering the timeout early */
266			pvfs_pending_lock_continue(p, PVFS_WAIT_TIMEOUT);
267			return NT_STATUS_OK;
268		}
269	}
270
271	return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
272}
273
274
275/*
276  lock or unlock a byte range
277*/
278NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
279		   struct ntvfs_request *req, union smb_lock *lck)
280{
281	struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
282				  struct pvfs_state);
283	struct pvfs_file *f;
284	struct smb_lock_entry *locks;
285	int i;
286	enum brl_type rw;
287	struct pvfs_pending_lock *pending = NULL;
288	NTSTATUS status;
289
290	if (lck->generic.level != RAW_LOCK_GENERIC) {
291		return ntvfs_map_lock(ntvfs, req, lck);
292	}
293
294	if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
295		return pvfs_oplock_release(ntvfs, req, lck);
296	}
297
298	f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
299	if (!f) {
300		return NT_STATUS_INVALID_HANDLE;
301	}
302
303	if (f->handle->fd == -1) {
304		return NT_STATUS_FILE_IS_A_DIRECTORY;
305	}
306
307	status = pvfs_break_level2_oplocks(f);
308	NT_STATUS_NOT_OK_RETURN(status);
309
310	if (lck->lockx.in.timeout != 0 &&
311	    (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
312		pending = talloc(f, struct pvfs_pending_lock);
313		if (pending == NULL) {
314			return NT_STATUS_NO_MEMORY;
315		}
316
317		pending->pvfs = pvfs;
318		pending->lck = lck;
319		pending->f = f;
320		pending->req = req;
321
322		pending->end_time =
323			timeval_current_ofs(lck->lockx.in.timeout/1000,
324					    1000*(lck->lockx.in.timeout%1000));
325	}
326
327	if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
328		rw = pending? PENDING_READ_LOCK : READ_LOCK;
329	} else {
330		rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK;
331	}
332
333	if (lck->lockx.in.mode & LOCKING_ANDX_CANCEL_LOCK) {
334		talloc_free(pending);
335		return pvfs_lock_cancel(pvfs, req, lck, f);
336	}
337
338	if (lck->lockx.in.mode & LOCKING_ANDX_CHANGE_LOCKTYPE) {
339		/* this seems to not be supported by any windows server,
340		   or used by any clients */
341		talloc_free(pending);
342		return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
343	}
344
345	/* the unlocks happen first */
346	locks = lck->lockx.in.locks;
347
348	for (i=0;i<lck->lockx.in.ulock_cnt;i++) {
349		status = brl_unlock(pvfs->brl_context,
350				    f->brl_handle,
351				    locks[i].pid,
352				    locks[i].offset,
353				    locks[i].count);
354		if (!NT_STATUS_IS_OK(status)) {
355			talloc_free(pending);
356			return status;
357		}
358		f->lock_count--;
359	}
360
361	locks += i;
362
363	for (i=0;i<lck->lockx.in.lock_cnt;i++) {
364		if (pending) {
365			pending->pending_lock = i;
366		}
367
368		status = brl_lock(pvfs->brl_context,
369				  f->brl_handle,
370				  locks[i].pid,
371				  locks[i].offset,
372				  locks[i].count,
373				  rw, pending);
374		if (!NT_STATUS_IS_OK(status)) {
375			if (pending) {
376				/* a timed lock failed - setup a wait message to handle
377				   the pending lock notification or a timeout */
378				pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
379									 pending->end_time,
380									 pvfs_pending_lock_continue,
381									 pending);
382				if (pending->wait_handle == NULL) {
383					talloc_free(pending);
384					return NT_STATUS_NO_MEMORY;
385				}
386				talloc_steal(pending, pending->wait_handle);
387				DLIST_ADD(f->pending_list, pending);
388				return NT_STATUS_OK;
389			}
390
391			/* undo the locks we just did */
392			for (i--;i>=0;i--) {
393				brl_unlock(pvfs->brl_context,
394					   f->brl_handle,
395					   locks[i].pid,
396					   locks[i].offset,
397					   locks[i].count);
398				f->lock_count--;
399			}
400			talloc_free(pending);
401			return status;
402		}
403		f->lock_count++;
404	}
405
406	talloc_free(pending);
407	return NT_STATUS_OK;
408}
409