2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - locking
6 Copyright (C) Andrew Tridgell 2004
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 2 of the License, or
11 (at your option) any later version.
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.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "include/includes.h"
24 #include "vfs_posix.h"
28 check if we can perform IO on a range that might be locked
30 NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs,
33 uint64_t offset, uint64_t count,
36 if (!(pvfs->flags & PVFS_FLAG_STRICT_LOCKING)) {
40 return brl_locktest(pvfs->brl_context,
47 /* this state structure holds information about a lock we are waiting on */
48 struct pending_state {
49 struct pvfs_state *pvfs;
52 struct smbsrv_request *req;
60 a secondary attempt to setup a lock has failed - back out
61 the locks we did get and send an error
63 static void pvfs_lock_async_failed(struct pvfs_state *pvfs,
64 struct smbsrv_request *req,
66 struct smb_lock_entry *locks,
70 /* undo the locks we just did */
71 for (i=i-1;i>=0;i--) {
72 brl_unlock(pvfs->brl_context,
79 req->async.status = status;
80 req->async.send_fn(req);
85 called when we receive a pending lock notification. It means that
86 either our lock timed out or somoene else has unlocked a overlapping
87 range, so we should try the lock again. Note that on timeout we
88 do retry the lock, giving it a last chance.
90 static void pvfs_pending_lock_continue(void *private, BOOL timed_out)
92 struct pending_state *pending = private;
93 struct pvfs_state *pvfs = pending->pvfs;
94 struct pvfs_file *f = pending->f;
95 struct smbsrv_request *req = pending->req;
96 union smb_lock *lck = pending->lck;
97 struct smb_lock_entry *locks;
102 locks = lck->lockx.in.locks + lck->lockx.in.ulock_cnt;
104 if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
110 status = brl_lock(pvfs->brl_context,
114 locks[pending->pending_lock].offset,
115 locks[pending->pending_lock].count,
118 /* if we have failed and timed out, or succeeded, then we
119 don't need the pending lock any more */
120 if (NT_STATUS_IS_OK(status) || timed_out) {
122 status2 = brl_remove_pending(pvfs->brl_context, &f->locking_key, pending);
123 if (!NT_STATUS_IS_OK(status2)) {
124 DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2)));
126 talloc_free(pending->wait_handle);
129 if (!NT_STATUS_IS_OK(status)) {
131 /* no more chances */
132 pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status);
134 /* we can try again */
138 /* if we haven't timed out yet, then we can do more pending locks */
142 if (rw == READ_LOCK) {
143 rw = PENDING_READ_LOCK;
145 rw = PENDING_WRITE_LOCK;
149 /* we've now got the pending lock. try and get the rest, which might
150 lead to more pending locks */
151 for (i=pending->pending_lock;i<lck->lockx.in.lock_cnt;i++) {
153 pending->pending_lock = i;
156 status = brl_lock(pvfs->brl_context,
163 if (!NT_STATUS_IS_OK(status)) {
165 /* a timed lock failed - setup a wait message to handle
166 the pending lock notification or a timeout */
167 pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
169 pvfs_pending_lock_continue,
171 if (pending->wait_handle == NULL) {
172 pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY);
176 pvfs_lock_async_failed(pvfs, req, f, locks, i, status);
181 brl_unlock(pvfs->brl_context,
188 /* we've managed to get all the locks. Tell the client */
189 req->async.status = NT_STATUS_OK;
190 req->async.send_fn(req);
195 lock or unlock a byte range
197 NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
198 struct smbsrv_request *req, union smb_lock *lck)
200 struct pvfs_state *pvfs = ntvfs->private_data;
202 struct smb_lock_entry *locks;
205 struct pending_state *pending = NULL;
207 f = pvfs_find_fd(pvfs, req, lck->generic.in.fnum);
209 return NT_STATUS_INVALID_HANDLE;
212 switch (lck->generic.level) {
214 return brl_lock(pvfs->brl_context,
222 case RAW_LOCK_UNLOCK:
223 return brl_unlock(pvfs->brl_context,
230 case RAW_LOCK_GENERIC:
231 return NT_STATUS_INVALID_LEVEL;
234 /* fall through to the most complex case */
238 /* now the lockingX case, most common and also most complex */
239 if (lck->lockx.in.timeout != 0) {
240 pending = talloc_p(req, struct pending_state);
241 if (pending == NULL) {
242 return NT_STATUS_NO_MEMORY;
245 pending->pvfs = pvfs;
250 /* round up to the nearest second */
251 pending->end_time = time(NULL) + ((lck->lockx.in.timeout+999)/1000);
254 if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
255 rw = pending? PENDING_READ_LOCK : READ_LOCK;
257 rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK;
260 if (lck->lockx.in.mode &
261 (LOCKING_ANDX_OPLOCK_RELEASE |
262 LOCKING_ANDX_CHANGE_LOCKTYPE |
263 LOCKING_ANDX_CANCEL_LOCK)) {
264 /* todo: need to add support for these */
265 return NT_STATUS_NOT_IMPLEMENTED;
269 /* the unlocks happen first */
270 locks = lck->lockx.in.locks;
272 for (i=0;i<lck->lockx.in.ulock_cnt;i++) {
274 status = brl_unlock(pvfs->brl_context,
280 if (!NT_STATUS_IS_OK(status)) {
287 for (i=0;i<lck->lockx.in.lock_cnt;i++) {
291 pending->pending_lock = i;
294 status = brl_lock(pvfs->brl_context,
301 if (!NT_STATUS_IS_OK(status)) {
303 /* a timed lock failed - setup a wait message to handle
304 the pending lock notification or a timeout */
305 pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
307 pvfs_pending_lock_continue,
309 if (pending->wait_handle == NULL) {
310 return NT_STATUS_NO_MEMORY;
314 /* undo the locks we just did */
315 for (i=i-1;i>=0;i--) {
316 brl_unlock(pvfs->brl_context,