1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or (at
10 * your option) any later version.
12 * This library is distributed in the hope that it will be useful, but
13 * without any warranty; without even the implied warranty of merchantability
14 * or fitness for a particular purpose. See the GNU Lesser General Public
15 * License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include "daemon_debug.h"
26 #include "delegation.h"
27 #include "nfs41_ops.h"
32 #define LKLVL 2 /* dprintf level for lock logging */
35 static void lock_stateid_arg(
36 IN nfs41_open_state
*state
,
40 arg
->delegation
= NULL
;
42 AcquireSRWLockShared(&state
->lock
);
43 if (state
->locks
.stateid
.seqid
) {
44 memcpy(&arg
->stateid
, &state
->locks
.stateid
, sizeof(stateid4
));
45 arg
->type
= STATEID_LOCK
;
46 } else if (state
->do_close
) {
47 memcpy(&arg
->stateid
, &state
->stateid
, sizeof(stateid4
));
48 arg
->type
= STATEID_OPEN
;
50 memset(&arg
->stateid
, 0, sizeof(stateid4
));
51 arg
->type
= STATEID_SPECIAL
;
53 ReleaseSRWLockShared(&state
->lock
);
56 /* expects the caller to hold an exclusive lock on nfs41_open_state.lock */
57 static void lock_stateid_update(
58 OUT nfs41_open_state
*state
,
59 IN
const stateid4
*stateid
)
61 if (state
->locks
.stateid
.seqid
== 0) {
62 /* if it's a new lock stateid, copy it in */
63 memcpy(&state
->locks
.stateid
, stateid
, sizeof(stateid4
));
64 } else if (stateid
->seqid
> state
->locks
.stateid
.seqid
) {
65 /* update the seqid if it's more recent */
66 state
->locks
.stateid
.seqid
= stateid
->seqid
;
70 static void open_lock_add(
71 IN nfs41_open_state
*open
,
72 IN
const stateid_arg
*stateid
,
73 IN nfs41_lock_state
*lock
)
75 AcquireSRWLockExclusive(&open
->lock
);
77 if (stateid
->type
== STATEID_LOCK
)
78 lock_stateid_update(open
, &stateid
->stateid
);
80 lock
->id
= open
->locks
.counter
++;
81 list_add_tail(&open
->locks
.list
, &lock
->open_entry
);
83 ReleaseSRWLockExclusive(&open
->lock
);
86 static bool_t
open_lock_delegate(
87 IN nfs41_open_state
*open
,
88 IN nfs41_lock_state
*lock
)
90 bool_t delegated
= FALSE
;
92 AcquireSRWLockExclusive(&open
->lock
);
93 if (open
->delegation
.state
) {
94 nfs41_delegation_state
*deleg
= open
->delegation
.state
;
95 AcquireSRWLockShared(&deleg
->lock
);
96 if (deleg
->state
.type
== OPEN_DELEGATE_WRITE
97 && deleg
->status
== DELEGATION_GRANTED
) {
99 lock
->id
= open
->locks
.counter
++;
100 list_add_tail(&open
->locks
.list
, &lock
->open_entry
);
103 ReleaseSRWLockShared(&deleg
->lock
);
105 ReleaseSRWLockExclusive(&open
->lock
);
110 #define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry)
112 static int lock_range_cmp(const struct list_entry
*entry
, const void *value
)
114 const nfs41_lock_state
*lhs
= lock_entry(entry
);
115 const nfs41_lock_state
*rhs
= (const nfs41_lock_state
*)value
;
116 if (lhs
->offset
!= rhs
->offset
) return -1;
117 if (lhs
->length
!= rhs
->length
) return -1;
121 static int open_unlock_delegate(
122 IN nfs41_open_state
*open
,
123 IN
const nfs41_lock_state
*input
)
125 struct list_entry
*entry
;
126 int status
= ERROR_NOT_LOCKED
;
128 AcquireSRWLockExclusive(&open
->lock
);
130 /* find lock state that matches this range */
131 entry
= list_search(&open
->locks
.list
, input
, lock_range_cmp
);
133 nfs41_lock_state
*lock
= lock_entry(entry
);
134 if (lock
->delegated
) {
135 /* if the lock was delegated, remove/free it and return success */
140 status
= ERROR_LOCKED
;
143 ReleaseSRWLockExclusive(&open
->lock
);
147 static void open_unlock_remove(
148 IN nfs41_open_state
*open
,
149 IN
const stateid_arg
*stateid
,
150 IN
const nfs41_lock_state
*input
)
152 struct list_entry
*entry
;
154 AcquireSRWLockExclusive(&open
->lock
);
155 if (stateid
->type
== STATEID_LOCK
)
156 lock_stateid_update(open
, &stateid
->stateid
);
158 /* find and remove the unlocked range */
159 entry
= list_search(&open
->locks
.list
, input
, lock_range_cmp
);
162 free(lock_entry(entry
));
164 ReleaseSRWLockExclusive(&open
->lock
);
169 static int parse_lock(unsigned char *buffer
, uint32_t length
, nfs41_upcall
*upcall
)
172 lock_upcall_args
*args
= &upcall
->args
.lock
;
174 status
= safe_read(&buffer
, &length
, &args
->offset
, sizeof(LONGLONG
));
175 if (status
) goto out
;
176 status
= safe_read(&buffer
, &length
, &args
->length
, sizeof(LONGLONG
));
177 if (status
) goto out
;
178 status
= safe_read(&buffer
, &length
, &args
->exclusive
, sizeof(BOOLEAN
));
179 if (status
) goto out
;
180 status
= safe_read(&buffer
, &length
, &args
->blocking
, sizeof(BOOLEAN
));
181 if (status
) goto out
;
183 dprintf(1, "parsing NFS41_LOCK: offset=0x%llx length=0x%llx exclusive=%u "
184 "blocking=%u\n", args
->offset
, args
->length
, args
->exclusive
,
190 static __inline
uint32_t get_lock_type(BOOLEAN exclusive
, BOOLEAN blocking
)
193 ? ( exclusive
== 0 ? READ_LT
: WRITE_LT
)
194 : ( exclusive
== 0 ? READW_LT
: WRITEW_LT
);
197 static int handle_lock(nfs41_upcall
*upcall
)
200 lock_upcall_args
*args
= &upcall
->args
.lock
;
201 nfs41_open_state
*state
= upcall
->state_ref
;
202 nfs41_lock_state
*lock
;
203 const uint32_t type
= get_lock_type(args
->exclusive
, args
->blocking
);
204 int status
= NO_ERROR
;
206 /* 18.10.3. Operation 12: LOCK - Create Lock
207 * "To lock the file from a specific offset through the end-of-file
208 * (no matter how long the file actually is) use a length field equal
209 * to NFS4_UINT64_MAX." */
210 if (args
->length
>= NFS4_UINT64_MAX
- args
->offset
)
211 args
->length
= NFS4_UINT64_MAX
;
213 /* allocate the lock state */
214 lock
= calloc(1, sizeof(nfs41_lock_state
));
216 status
= GetLastError();
219 lock
->offset
= args
->offset
;
220 lock
->length
= args
->length
;
221 lock
->exclusive
= args
->exclusive
;
223 /* if we hold a write delegation, handle the lock locally */
224 if (open_lock_delegate(state
, lock
)) {
225 dprintf(LKLVL
, "delegated lock { %llu, %llu }\n",
226 lock
->offset
, lock
->length
);
227 args
->acquired
= TRUE
; /* for cancel_lock() */
231 /* open_to_lock_owner4 requires an open stateid; if we
232 * have a delegation, convert it to an open stateid */
233 status
= nfs41_delegation_to_open(state
, TRUE
);
235 status
= ERROR_FILE_INVALID
;
239 EnterCriticalSection(&state
->locks
.lock
);
241 lock_stateid_arg(state
, &stateid
);
243 status
= nfs41_lock(state
->session
, &state
->file
, &state
->owner
,
244 type
, lock
->offset
, lock
->length
, FALSE
, TRUE
, &stateid
);
246 dprintf(LKLVL
, "nfs41_lock failed with %s\n",
247 nfs_error_string(status
));
248 status
= nfs_to_windows_error(status
, ERROR_BAD_NET_RESP
);
249 LeaveCriticalSection(&state
->locks
.lock
);
253 /* save lock state with the open */
254 open_lock_add(state
, &stateid
, lock
);
255 LeaveCriticalSection(&state
->locks
.lock
);
257 args
->acquired
= TRUE
; /* for cancel_lock() */
266 static void cancel_lock(IN nfs41_upcall
*upcall
)
269 nfs41_lock_state input
;
270 lock_upcall_args
*args
= &upcall
->args
.lock
;
271 nfs41_open_state
*state
= upcall
->state_ref
;
272 int status
= NO_ERROR
;
274 dprintf(1, "--> cancel_lock()\n");
276 /* can't do 'if (upcall->status)' here, because a handle_lock() success
277 * could be overwritten by upcall_marshall() or allocation failure */
281 input
.offset
= args
->offset
;
282 input
.length
= args
->length
;
284 /* search for the range to unlock, and remove if delegated */
285 status
= open_unlock_delegate(state
, &input
);
286 if (status
!= ERROR_LOCKED
)
289 EnterCriticalSection(&state
->locks
.lock
);
290 lock_stateid_arg(state
, &stateid
);
292 status
= nfs41_unlock(state
->session
, &state
->file
,
293 args
->offset
, args
->length
, &stateid
);
295 open_unlock_remove(state
, &stateid
, &input
);
296 LeaveCriticalSection(&state
->locks
.lock
);
298 status
= nfs_to_windows_error(status
, ERROR_BAD_NET_RESP
);
300 dprintf(1, "<-- cancel_lock() returning %d\n", status
);
305 static int parse_unlock(unsigned char *buffer
, uint32_t length
, nfs41_upcall
*upcall
)
308 unlock_upcall_args
*args
= &upcall
->args
.unlock
;
310 status
= safe_read(&buffer
, &length
, &args
->count
, sizeof(ULONG
));
311 if (status
) goto out
;
314 args
->buf_len
= length
;
316 dprintf(1, "parsing NFS41_UNLOCK: count=%u\n", args
->count
);
321 static int handle_unlock(nfs41_upcall
*upcall
)
323 nfs41_lock_state input
;
325 unlock_upcall_args
*args
= &upcall
->args
.unlock
;
326 nfs41_open_state
*state
= upcall
->state_ref
;
327 unsigned char *buf
= args
->buf
;
328 uint32_t buf_len
= args
->buf_len
;
330 int status
= NO_ERROR
;
332 for (i
= 0; i
< args
->count
; i
++) {
333 if (safe_read(&buf
, &buf_len
, &input
.offset
, sizeof(LONGLONG
))) break;
334 if (safe_read(&buf
, &buf_len
, &input
.length
, sizeof(LONGLONG
))) break;
336 /* do the same translation as LOCK, or the ranges won't match */
337 if (input
.length
>= NFS4_UINT64_MAX
- input
.offset
)
338 input
.length
= NFS4_UINT64_MAX
;
340 /* search for the range to unlock, and remove if delegated */
341 status
= open_unlock_delegate(state
, &input
);
342 if (status
!= ERROR_LOCKED
)
345 EnterCriticalSection(&state
->locks
.lock
);
346 lock_stateid_arg(state
, &stateid
);
348 status
= nfs41_unlock(state
->session
, &state
->file
,
349 input
.offset
, input
.length
, &stateid
);
351 open_unlock_remove(state
, &stateid
, &input
);
352 LeaveCriticalSection(&state
->locks
.lock
);
354 status
= nfs_to_windows_error(status
, ERROR_BAD_NET_RESP
);
360 const nfs41_upcall_op nfs41_op_lock
= {
366 const nfs41_upcall_op nfs41_op_unlock
= {