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
26 #include "nfs41_ops.h"
27 #include "delegation.h"
28 #include "from_kernel.h"
29 #include "daemon_debug.h"
34 static int create_open_state(
36 IN
uint32_t open_owner_id
,
37 OUT nfs41_open_state
**state_out
)
40 nfs41_open_state
*state
;
42 state
= calloc(1, sizeof(nfs41_open_state
));
44 status
= GetLastError();
48 InitializeSRWLock(&state
->path
.lock
);
49 if (FAILED(StringCchCopyA(state
->path
.path
, NFS41_MAX_PATH_LEN
, path
))) {
50 status
= ERROR_FILENAME_EXCED_RANGE
;
53 state
->path
.len
= (unsigned short)strlen(state
->path
.path
);
54 path_fh_init(&state
->file
, &state
->path
);
55 path_fh_init(&state
->parent
, &state
->path
);
56 last_component(state
->path
.path
, state
->file
.name
.name
, &state
->parent
.name
);
58 StringCchPrintfA((LPSTR
)state
->owner
.owner
, NFS4_OPAQUE_LIMIT
, "%u",
60 state
->owner
.owner_len
= (uint32_t)strlen((const char*)state
->owner
.owner
);
62 list_init(&state
->locks
.list
);
63 list_init(&state
->client_entry
);
64 InitializeCriticalSection(&state
->locks
.lock
);
66 state
->ea
.list
= INVALID_HANDLE_VALUE
;
67 InitializeCriticalSection(&state
->ea
.lock
);
79 static void open_state_free(
80 IN nfs41_open_state
*state
)
82 struct list_entry
*entry
, *tmp
;
84 /* free associated lock state */
85 list_for_each_tmp(entry
, tmp
, &state
->locks
.list
)
86 free(list_container(entry
, nfs41_lock_state
, open_entry
));
87 if (state
->delegation
.state
)
88 nfs41_delegation_deref(state
->delegation
.state
);
89 if (state
->ea
.list
!= INVALID_HANDLE_VALUE
)
95 /* open state reference counting */
96 void nfs41_open_state_ref(
97 IN nfs41_open_state
*state
)
99 const LONG count
= InterlockedIncrement(&state
->ref_count
);
101 dprintf(2, "nfs41_open_state_ref(%s) count %d\n", state
->path
.path
, count
);
104 void nfs41_open_state_deref(
105 IN nfs41_open_state
*state
)
107 const LONG count
= InterlockedDecrement(&state
->ref_count
);
109 dprintf(2, "nfs41_open_state_deref(%s) count %d\n", state
->path
.path
, count
);
111 open_state_free(state
);
114 /* 8.2.5. Stateid Use for I/O Operations
115 * o If the client holds a delegation for the file in question, the
116 * delegation stateid SHOULD be used.
117 * o Otherwise, if the entity corresponding to the lock-owner (e.g., a
118 * process) sending the I/O has a byte-range lock stateid for the
119 * associated open file, then the byte-range lock stateid for that
120 * lock-owner and open file SHOULD be used.
121 * o If there is no byte-range lock stateid, then the OPEN stateid for
122 * the open file in question SHOULD be used.
123 * o Finally, if none of the above apply, then a special stateid SHOULD
125 void nfs41_open_stateid_arg(
126 IN nfs41_open_state
*state
,
127 OUT stateid_arg
*arg
)
130 arg
->delegation
= NULL
;
132 AcquireSRWLockShared(&state
->lock
);
134 if (state
->delegation
.state
) {
135 nfs41_delegation_state
*deleg
= state
->delegation
.state
;
136 AcquireSRWLockShared(&deleg
->lock
);
137 if (deleg
->status
== DELEGATION_GRANTED
) {
138 arg
->type
= STATEID_DELEG_FILE
;
139 memcpy(&arg
->stateid
, &deleg
->state
.stateid
, sizeof(stateid4
));
141 ReleaseSRWLockShared(&deleg
->lock
);
143 if (arg
->type
== STATEID_DELEG_FILE
)
146 dprintf(2, "delegation recalled, waiting for open stateid..\n");
148 /* wait for nfs41_delegation_to_open() to recover open stateid */
149 while (!state
->do_close
)
150 SleepConditionVariableSRW(&state
->delegation
.cond
, &state
->lock
,
151 INFINITE
, CONDITION_VARIABLE_LOCKMODE_SHARED
);
154 if (state
->locks
.stateid
.seqid
) {
155 memcpy(&arg
->stateid
, &state
->locks
.stateid
, sizeof(stateid4
));
156 arg
->type
= STATEID_LOCK
;
157 } else if (state
->do_close
) {
158 memcpy(&arg
->stateid
, &state
->stateid
, sizeof(stateid4
));
159 arg
->type
= STATEID_OPEN
;
161 memset(&arg
->stateid
, 0, sizeof(stateid4
));
162 arg
->type
= STATEID_SPECIAL
;
165 ReleaseSRWLockShared(&state
->lock
);
168 /* client list of associated open state */
169 static void client_state_add(
170 IN nfs41_open_state
*state
)
172 nfs41_client
*client
= state
->session
->client
;
174 EnterCriticalSection(&client
->state
.lock
);
175 list_add_tail(&client
->state
.opens
, &state
->client_entry
);
176 LeaveCriticalSection(&client
->state
.lock
);
179 static void client_state_remove(
180 IN nfs41_open_state
*state
)
182 nfs41_client
*client
= state
->session
->client
;
184 EnterCriticalSection(&client
->state
.lock
);
185 list_remove(&state
->client_entry
);
186 LeaveCriticalSection(&client
->state
.lock
);
190 IN OUT nfs41_open_state
*state
,
192 IN
uint32_t createhow
,
193 IN nfs41_file_info
*createattrs
,
194 IN bool_t try_recovery
,
195 OUT nfs41_file_info
*info
)
198 stateid4 open_stateid
;
199 open_delegation4 delegation
= { 0 };
200 nfs41_delegation_state
*deleg_state
= NULL
;
203 claim
.claim
= CLAIM_NULL
;
204 claim
.u
.null
.filename
= &state
->file
.name
;
206 status
= nfs41_open(state
->session
, &state
->parent
, &state
->file
,
207 &state
->owner
, &claim
, state
->share_access
, state
->share_deny
,
208 create
, createhow
, createattrs
, TRUE
, &open_stateid
,
213 /* allocate delegation state and register it with the client */
214 nfs41_delegation_granted(state
->session
, &state
->parent
,
215 &state
->file
, &delegation
, TRUE
, &deleg_state
);
217 deleg_state
->srv_open
= state
->srv_open
;
218 dprintf(1, "do_open: received delegation: saving srv_open = %x\n",
222 AcquireSRWLockExclusive(&state
->lock
);
223 /* update the stateid */
224 memcpy(&state
->stateid
, &open_stateid
, sizeof(open_stateid
));
226 state
->delegation
.state
= deleg_state
;
227 ReleaseSRWLockExclusive(&state
->lock
);
232 static int open_or_delegate(
233 IN OUT nfs41_open_state
*state
,
235 IN
uint32_t createhow
,
236 IN nfs41_file_info
*createattrs
,
237 IN bool_t try_recovery
,
238 OUT nfs41_file_info
*info
)
242 /* check for existing delegation */
243 status
= nfs41_delegate_open(state
, create
, createattrs
, info
);
245 /* get an open stateid if we have no delegation stateid */
247 status
= do_open(state
, create
, createhow
,
248 createattrs
, try_recovery
, info
);
250 state
->pnfs_last_offset
= info
->size
? info
->size
- 1 : 0;
252 /* register the client's open state on success */
253 if (status
== NFS4_OK
)
254 client_state_add(state
);
259 static int parse_abs_path(unsigned char **buffer
, uint32_t *length
, nfs41_abs_path
*path
)
261 int status
= safe_read(buffer
, length
, &path
->len
, sizeof(USHORT
));
262 if (status
) goto out
;
265 if (path
->len
>= NFS41_MAX_PATH_LEN
) {
266 status
= ERROR_BUFFER_OVERFLOW
;
269 status
= safe_read(buffer
, length
, path
->path
, path
->len
);
270 if (status
) goto out
;
271 path
->len
--; /* subtract 1 for null */
277 static int parse_open(unsigned char *buffer
, uint32_t length
, nfs41_upcall
*upcall
)
280 open_upcall_args
*args
= &upcall
->args
.open
;
282 status
= get_name(&buffer
, &length
, &args
->path
);
283 if (status
) goto out
;
284 status
= safe_read(&buffer
, &length
, &args
->access_mask
, sizeof(ULONG
));
285 if (status
) goto out
;
286 status
= safe_read(&buffer
, &length
, &args
->access_mode
, sizeof(ULONG
));
287 if (status
) goto out
;
288 status
= safe_read(&buffer
, &length
, &args
->file_attrs
, sizeof(ULONG
));
289 if (status
) goto out
;
290 status
= safe_read(&buffer
, &length
, &args
->create_opts
, sizeof(ULONG
));
291 if (status
) goto out
;
292 status
= safe_read(&buffer
, &length
, &args
->disposition
, sizeof(ULONG
));
293 if (status
) goto out
;
294 status
= safe_read(&buffer
, &length
, &args
->open_owner_id
, sizeof(LONG
));
295 if (status
) goto out
;
296 status
= safe_read(&buffer
, &length
, &args
->mode
, sizeof(DWORD
));
297 if (status
) goto out
;
298 status
= safe_read(&buffer
, &length
, &args
->srv_open
, sizeof(HANDLE
));
299 if (status
) goto out
;
300 status
= parse_abs_path(&buffer
, &length
, &args
->symlink
);
301 if (status
) goto out
;
302 status
= safe_read(&buffer
, &length
, &args
->ea
, sizeof(HANDLE
));
303 if (status
) goto out
;
305 dprintf(1, "parsing NFS41_OPEN: filename='%s' access mask=%d "
306 "access mode=%d\n\tfile attrs=0x%x create attrs=0x%x "
307 "(kernel) disposition=%d\n\topen_owner_id=%d mode=%o "
308 "srv_open=%p symlink=%s ea=%p\n", args
->path
, args
->access_mask
,
309 args
->access_mode
, args
->file_attrs
, args
->create_opts
,
310 args
->disposition
, args
->open_owner_id
, args
->mode
, args
->srv_open
,
311 args
->symlink
.path
, args
->ea
);
312 print_disposition(2, args
->disposition
);
313 print_access_mask(2, args
->access_mask
);
314 print_share_mode(2, args
->access_mode
);
315 print_create_attributes(2, args
->create_opts
);
320 static BOOLEAN
open_for_attributes(uint32_t type
, ULONG access_mask
,
321 ULONG disposition
, int status
)
323 if (type
== NF4DIR
) {
324 if (disposition
== FILE_OPEN
|| disposition
== FILE_OVERWRITE
||
325 (!status
&& (disposition
== FILE_OPEN_IF
||
326 disposition
== FILE_OVERWRITE_IF
||
327 disposition
== FILE_SUPERSEDE
))) {
328 dprintf(1, "Opening a directory\n");
331 dprintf(1, "Creating a directory\n");
336 if ((access_mask
& FILE_READ_DATA
) ||
337 (access_mask
& FILE_WRITE_DATA
) ||
338 (access_mask
& FILE_APPEND_DATA
) ||
339 (access_mask
& FILE_EXECUTE
) ||
340 disposition
== FILE_CREATE
||
341 disposition
== FILE_OVERWRITE_IF
||
342 disposition
== FILE_SUPERSEDE
||
343 disposition
== FILE_OPEN_IF
||
344 disposition
== FILE_OVERWRITE
)
347 dprintf(1, "Open call that wants to manage attributes\n");
352 static int map_disposition_2_nfsopen(ULONG disposition
, int in_status
, bool_t persistent
,
353 uint32_t *create
, uint32_t *createhowmode
,
354 uint32_t *last_error
)
356 int status
= NO_ERROR
;
357 if (disposition
== FILE_SUPERSEDE
) {
358 if (in_status
== NFS4ERR_NOENT
)
359 *last_error
= ERROR_FILE_NOT_FOUND
;
360 //remove and recreate the file
361 *create
= OPEN4_CREATE
;
362 if (persistent
) *createhowmode
= GUARDED4
;
363 else *createhowmode
= EXCLUSIVE4_1
;
364 } else if (disposition
== FILE_CREATE
) {
365 // if lookup succeeded which means the file exist, return an error
367 status
= ERROR_FILE_EXISTS
;
369 *create
= OPEN4_CREATE
;
370 if (persistent
) *createhowmode
= GUARDED4
;
371 else *createhowmode
= EXCLUSIVE4_1
;
373 } else if (disposition
== FILE_OPEN
) {
374 if (in_status
== NFS4ERR_NOENT
)
375 status
= ERROR_FILE_NOT_FOUND
;
377 *create
= OPEN4_NOCREATE
;
378 } else if (disposition
== FILE_OPEN_IF
) {
379 if (in_status
== NFS4ERR_NOENT
) {
380 dprintf(1, "creating new file\n");
381 *create
= OPEN4_CREATE
;
382 *last_error
= ERROR_FILE_NOT_FOUND
;
384 dprintf(1, "opening existing file\n");
385 *create
= OPEN4_NOCREATE
;
387 } else if (disposition
== FILE_OVERWRITE
) {
388 if (in_status
== NFS4ERR_NOENT
)
389 status
= ERROR_FILE_NOT_FOUND
;
391 *create
= OPEN4_CREATE
;
392 } else if (disposition
== FILE_OVERWRITE_IF
) {
393 if (in_status
== NFS4ERR_NOENT
)
394 *last_error
= ERROR_FILE_NOT_FOUND
;
396 *create
= OPEN4_CREATE
;
401 static void map_access_2_allowdeny(ULONG access_mask
, ULONG access_mode
,
402 ULONG disposition
, uint32_t *allow
, uint32_t *deny
)
405 (FILE_WRITE_DATA
| FILE_APPEND_DATA
| FILE_WRITE_ATTRIBUTES
)) &&
406 (access_mask
& (FILE_READ_DATA
| FILE_EXECUTE
)))
407 *allow
= OPEN4_SHARE_ACCESS_BOTH
;
408 else if (access_mask
& (FILE_READ_DATA
| FILE_EXECUTE
))
409 *allow
= OPEN4_SHARE_ACCESS_READ
;
410 else if (access_mask
&
411 (FILE_WRITE_DATA
| FILE_APPEND_DATA
| FILE_WRITE_ATTRIBUTES
))
412 *allow
= OPEN4_SHARE_ACCESS_WRITE
;
413 /* if we are creating a file and no data access is specified, then
414 * do an open and request no delegations. example open with share access 0
415 * and share deny 0 (ie deny_both).
417 if ((disposition
== FILE_CREATE
|| disposition
== FILE_OPEN_IF
||
418 disposition
== FILE_OVERWRITE_IF
|| disposition
== FILE_SUPERSEDE
||
419 disposition
== FILE_OVERWRITE
) &&
420 !(access_mask
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
|
421 FILE_WRITE_ATTRIBUTES
| FILE_READ_DATA
| FILE_EXECUTE
)))
422 *allow
= OPEN4_SHARE_ACCESS_READ
| OPEN4_SHARE_ACCESS_WANT_NO_DELEG
;
424 #define FIX_ALLOW_DENY_WIN2NFS_CONVERSION
425 #ifdef FIX_ALLOW_DENY_WIN2NFS_CONVERSION
426 if ((access_mode
& FILE_SHARE_READ
) &&
427 (access_mode
& FILE_SHARE_WRITE
))
428 *deny
= OPEN4_SHARE_DENY_NONE
;
429 else if (access_mode
& FILE_SHARE_READ
)
430 *deny
= OPEN4_SHARE_DENY_WRITE
;
431 else if (access_mode
& FILE_SHARE_WRITE
)
432 *deny
= OPEN4_SHARE_DENY_READ
;
434 *deny
= OPEN4_SHARE_DENY_BOTH
;
437 // readonly file that is being opened for reading with a
438 // share read mode given above logic translates into deny
439 // write and linux server does not allow it.
440 *deny
= OPEN4_SHARE_DENY_NONE
;
444 static int check_execute_access(nfs41_open_state
*state
)
446 uint32_t supported
, access
;
447 int status
= nfs41_access(state
->session
, &state
->file
,
448 ACCESS4_EXECUTE
| ACCESS4_READ
, &supported
, &access
);
450 eprintf("nfs41_access() failed with %s for %s\n",
451 nfs_error_string(status
), state
->path
.path
);
452 status
= ERROR_ACCESS_DENIED
;
453 } else if ((supported
& ACCESS4_EXECUTE
) == 0) {
454 /* server can't verify execute access;
455 * for now, assume that read access is good enough */
456 if ((supported
& ACCESS4_READ
) == 0 || (access
& ACCESS4_READ
) == 0) {
457 eprintf("server can't verify execute access, and user does "
458 "not have read access to file %s\n", state
->path
.path
);
459 status
= ERROR_ACCESS_DENIED
;
461 } else if ((access
& ACCESS4_EXECUTE
) == 0) {
462 dprintf(1, "user does not have execute access to file %s\n",
464 status
= ERROR_ACCESS_DENIED
;
466 dprintf(2, "user has execute access to file\n");
470 static int create_with_ea(
471 IN
uint32_t disposition
,
472 IN
uint32_t lookup_status
)
474 /* only set EAs on file creation */
475 return disposition
== FILE_SUPERSEDE
|| disposition
== FILE_CREATE
476 || disposition
== FILE_OVERWRITE
|| disposition
== FILE_OVERWRITE_IF
477 || (disposition
== FILE_OPEN_IF
&& lookup_status
== NFS4ERR_NOENT
);
480 static int handle_open(nfs41_upcall
*upcall
)
483 open_upcall_args
*args
= &upcall
->args
.open
;
484 nfs41_open_state
*state
;
485 nfs41_file_info info
= { 0 };
487 status
= create_open_state(args
->path
, args
->open_owner_id
, &state
);
489 eprintf("create_open_state(%d) failed with %d\n",
490 args
->open_owner_id
, status
);
493 state
->srv_open
= args
->srv_open
;
495 // first check if windows told us it's a directory
496 if (args
->create_opts
& FILE_DIRECTORY_FILE
)
497 state
->type
= NF4DIR
;
499 state
->type
= NF4REG
;
501 // always do a lookup
502 status
= nfs41_lookup(upcall
->root_ref
, nfs41_root_session(upcall
->root_ref
),
503 &state
->path
, &state
->parent
, &state
->file
, &info
, &state
->session
);
505 if (status
== ERROR_REPARSE
) {
507 /* one of the parent components was a symlink */
509 if (++depth
> NFS41_MAX_SYMLINK_DEPTH
) {
510 status
= ERROR_TOO_MANY_LINKS
;
514 /* replace the path with the symlink target's */
515 status
= nfs41_symlink_target(state
->session
,
516 &state
->parent
, &state
->path
);
518 /* can't do the reparse if we can't get the target */
519 eprintf("nfs41_symlink_target() failed with %d\n", status
);
523 /* redo the lookup until it doesn't return REPARSE */
524 status
= nfs41_lookup(upcall
->root_ref
, state
->session
,
525 &state
->path
, &state
->parent
, NULL
, NULL
, &state
->session
);
526 } while (status
== ERROR_REPARSE
);
528 if (status
== NO_ERROR
|| status
== ERROR_FILE_NOT_FOUND
) {
529 abs_path_copy(&args
->symlink
, &state
->path
);
531 upcall
->last_error
= ERROR_REPARSE
;
532 args
->symlink_embedded
= TRUE
;
537 // now if file/dir exists, use type returned by lookup
538 if (status
== NO_ERROR
) {
539 if (info
.type
== NF4DIR
) {
540 dprintf(2, "handle_nfs41_open: DIRECTORY\n");
541 if (args
->create_opts
& FILE_NON_DIRECTORY_FILE
) {
542 eprintf("trying to open directory %s as a file\n",
544 status
= ERROR_DIRECTORY
;
547 } else if (info
.type
== NF4REG
) {
548 dprintf(2, "handle nfs41_open: FILE\n");
549 if (args
->create_opts
& FILE_DIRECTORY_FILE
) {
550 eprintf("trying to open file %s as a directory\n",
552 status
= ERROR_BAD_FILE_TYPE
;
555 } else if (info
.type
== NF4LNK
) {
556 dprintf(2, "handle nfs41_open: SYMLINK\n");
557 if (args
->create_opts
& FILE_OPEN_REPARSE_POINT
) {
558 /* continue and open the symlink itself, but we need to
559 * know if the target is a regular file or directory */
560 nfs41_file_info target_info
;
561 int target_status
= nfs41_symlink_follow(upcall
->root_ref
,
562 state
->session
, &state
->file
, &target_info
);
563 if (target_status
== NO_ERROR
&& target_info
.type
== NF4DIR
)
564 info
.symlink_dir
= TRUE
;
566 /* replace the path with the symlink target */
567 status
= nfs41_symlink_target(state
->session
,
568 &state
->file
, &args
->symlink
);
570 eprintf("nfs41_symlink_target() for %s failed with %d\n",
573 /* tell the driver to call RxPrepareToReparseSymbolicLink() */
574 upcall
->last_error
= ERROR_REPARSE
;
575 args
->symlink_embedded
= FALSE
;
580 dprintf(2, "handle_open(): unsupported type=%d\n", info
.type
);
581 state
->type
= info
.type
;
582 } else if (status
!= ERROR_FILE_NOT_FOUND
)
585 /* XXX: this is a hard-coded check for the open arguments we see from
586 * the CreateSymbolicLink() system call. we respond to this by deferring
587 * the CREATE until we get the upcall to set the symlink. this approach
588 * is troublesome for two reasons:
589 * -an application might use these exact arguments to create a normal
590 * file, and we would return success without actually creating it
591 * -an application could create a symlink by sending the FSCTL to set
592 * the reparse point manually, and their open might be different. in
593 * this case we'd create the file on open, and need to remove it
594 * before creating the symlink */
595 if (args
->disposition
== FILE_CREATE
&&
596 args
->access_mask
== (FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
| DELETE
) &&
597 args
->access_mode
== 0 &&
598 args
->create_opts
& FILE_OPEN_REPARSE_POINT
) {
599 /* fail if the file already exists */
600 if (status
== NO_ERROR
) {
601 status
= ERROR_FILE_EXISTS
;
605 /* defer the call to CREATE until we get the symlink set upcall */
606 dprintf(1, "trying to create a symlink, deferring create\n");
608 /* because of WRITE_ATTR access, be prepared for a setattr upcall;
609 * will crash if the superblock is null, so use the parent's */
610 state
->file
.fh
.superblock
= state
->parent
.fh
.superblock
;
613 } else if (args
->symlink
.len
) {
614 /* handle cygwin symlinks */
615 nfs41_file_info createattrs
;
616 createattrs
.attrmask
.count
= 2;
617 createattrs
.attrmask
.arr
[0] = 0;
618 createattrs
.attrmask
.arr
[1] = FATTR4_WORD1_MODE
;
619 createattrs
.mode
= 0777;
621 dprintf(1, "creating cygwin symlink %s -> %s\n",
622 state
->file
.name
.name
, args
->symlink
.path
);
624 status
= nfs41_create(state
->session
, NF4LNK
, &createattrs
,
625 args
->symlink
.path
, &state
->parent
, &state
->file
, &info
);
627 eprintf("nfs41_create() for symlink=%s failed with %s\n",
628 args
->symlink
.path
, nfs_error_string(status
));
629 status
= map_symlink_errors(status
);
632 nfs_to_basic_info(&info
, &args
->basic_info
);
633 nfs_to_standard_info(&info
, &args
->std_info
);
634 args
->mode
= info
.mode
;
635 args
->changeattr
= info
.change
;
636 } else if (open_for_attributes(state
->type
, args
->access_mask
,
637 args
->disposition
, status
)) {
639 dprintf(1, "nfs41_lookup failed with %d\n", status
);
643 nfs_to_basic_info(&info
, &args
->basic_info
);
644 nfs_to_standard_info(&info
, &args
->std_info
);
645 args
->mode
= info
.mode
;
646 args
->changeattr
= info
.change
;
648 nfs41_file_info createattrs
= { 0 };
649 uint32_t create
= 0, createhowmode
= 0, lookup_status
= status
;
651 if (!lookup_status
&& (args
->disposition
== FILE_OVERWRITE
||
652 args
->disposition
== FILE_OVERWRITE_IF
||
653 args
->disposition
== FILE_SUPERSEDE
)) {
654 if ((info
.hidden
&& !(args
->file_attrs
& FILE_ATTRIBUTE_HIDDEN
)) ||
655 (info
.system
&& !(args
->file_attrs
& FILE_ATTRIBUTE_SYSTEM
))) {
656 status
= ERROR_ACCESS_DENIED
;
659 if (args
->disposition
!= FILE_SUPERSEDE
)
660 args
->mode
= info
.mode
;
662 createattrs
.attrmask
.count
= 2;
663 createattrs
.attrmask
.arr
[0] = FATTR4_WORD0_HIDDEN
| FATTR4_WORD0_ARCHIVE
;
664 createattrs
.attrmask
.arr
[1] = FATTR4_WORD1_MODE
| FATTR4_WORD1_SYSTEM
;
665 createattrs
.mode
= args
->mode
;
666 createattrs
.hidden
= args
->file_attrs
& FILE_ATTRIBUTE_HIDDEN
? 1 : 0;
667 createattrs
.system
= args
->file_attrs
& FILE_ATTRIBUTE_SYSTEM
? 1 : 0;
668 createattrs
.archive
= args
->file_attrs
& FILE_ATTRIBUTE_ARCHIVE
? 1 : 0;
670 map_access_2_allowdeny(args
->access_mask
, args
->access_mode
,
671 args
->disposition
, &state
->share_access
, &state
->share_deny
);
672 status
= map_disposition_2_nfsopen(args
->disposition
, status
,
673 state
->session
->flags
& CREATE_SESSION4_FLAG_PERSIST
,
674 &create
, &createhowmode
, &upcall
->last_error
);
678 if (args
->access_mask
& FILE_EXECUTE
&& state
->file
.fh
.len
) {
679 status
= check_execute_access(state
);
685 // XXX file exists and we have to remove it first
686 if (args
->disposition
== FILE_SUPERSEDE
&& lookup_status
== NO_ERROR
) {
687 nfs41_component
*name
= &state
->file
.name
;
688 if (!(args
->create_opts
& FILE_DIRECTORY_FILE
))
689 nfs41_delegation_return(state
->session
, &state
->file
,
690 OPEN_DELEGATE_WRITE
, TRUE
);
692 dprintf(1, "open for FILE_SUPERSEDE removing %s first\n", name
->name
);
693 status
= nfs41_remove(state
->session
, &state
->parent
,
694 name
, state
->file
.fh
.fileid
);
699 if (create
== OPEN4_CREATE
&& (args
->create_opts
& FILE_DIRECTORY_FILE
)) {
700 status
= nfs41_create(state
->session
, NF4DIR
, &createattrs
, NULL
,
701 &state
->parent
, &state
->file
, &info
);
702 args
->created
= status
== NFS4_OK
? TRUE
: FALSE
;
704 createattrs
.attrmask
.arr
[0] |= FATTR4_WORD0_SIZE
;
705 createattrs
.size
= 0;
706 dprintf(1, "creating with mod %o\n", args
->mode
);
707 status
= open_or_delegate(state
, create
, createhowmode
, &createattrs
,
709 if (status
== NFS4_OK
&& state
->delegation
.state
)
710 args
->deleg_type
= state
->delegation
.state
->state
.type
;
713 dprintf(1, "%s failed with %s\n", (create
== OPEN4_CREATE
&&
714 (args
->create_opts
& FILE_DIRECTORY_FILE
))?"nfs41_create":"nfs41_open",
715 nfs_error_string(status
));
716 if (args
->disposition
== FILE_SUPERSEDE
&& status
== NFS4ERR_EXIST
)
717 goto supersede_retry
;
718 status
= nfs_to_windows_error(status
, ERROR_FILE_NOT_FOUND
);
721 nfs_to_basic_info(&info
, &args
->basic_info
);
722 nfs_to_standard_info(&info
, &args
->std_info
);
723 args
->mode
= info
.mode
;
724 args
->changeattr
= info
.change
;
727 /* set extended attributes on file creation */
728 if (args
->ea
&& create_with_ea(args
->disposition
, lookup_status
)) {
729 status
= nfs41_ea_set(state
, args
->ea
);
730 status
= nfs_to_windows_error(status
, ERROR_FILE_NOT_FOUND
);
734 upcall
->state_ref
= state
;
735 nfs41_open_state_ref(upcall
->state_ref
);
739 nfs41_open_state_deref(state
);
743 static int marshall_open(unsigned char *buffer
, uint32_t *length
, nfs41_upcall
*upcall
)
746 open_upcall_args
*args
= &upcall
->args
.open
;
748 status
= safe_write(&buffer
, length
, &args
->basic_info
, sizeof(args
->basic_info
));
749 if (status
) goto out
;
750 status
= safe_write(&buffer
, length
, &args
->std_info
, sizeof(args
->std_info
));
751 if (status
) goto out
;
752 status
= safe_write(&buffer
, length
, &upcall
->state_ref
, sizeof(HANDLE
));
753 if (status
) goto out
;
754 status
= safe_write(&buffer
, length
, &args
->mode
, sizeof(args
->mode
));
755 if (status
) goto out
;
756 status
= safe_write(&buffer
, length
, &args
->changeattr
, sizeof(args
->changeattr
));
757 if (status
) goto out
;
758 status
= safe_write(&buffer
, length
, &args
->deleg_type
, sizeof(args
->deleg_type
));
759 if (status
) goto out
;
760 if (upcall
->last_error
== ERROR_REPARSE
) {
761 unsigned short len
= (args
->symlink
.len
+ 1) * sizeof(WCHAR
);
762 status
= safe_write(&buffer
, length
, &args
->symlink_embedded
, sizeof(BOOLEAN
));
763 if (status
) goto out
;
764 status
= safe_write(&buffer
, length
, &len
, sizeof(len
));
765 if (status
) goto out
;
766 /* convert args->symlink to wchar */
767 if (*length
<= len
|| !MultiByteToWideChar(CP_UTF8
, 0,
768 args
->symlink
.path
, args
->symlink
.len
,
769 (LPWSTR
)buffer
, len
/ sizeof(WCHAR
))) {
770 status
= ERROR_BUFFER_OVERFLOW
;
774 dprintf(2, "NFS41_OPEN: downcall open_state=0x%p mode %o changeattr 0x%llu\n",
775 upcall
->state_ref
, args
->mode
, args
->changeattr
);
780 static void cancel_open(IN nfs41_upcall
*upcall
)
782 int status
= NFS4_OK
;
783 open_upcall_args
*args
= &upcall
->args
.open
;
784 nfs41_open_state
*state
= upcall
->state_ref
;
786 dprintf(1, "--> cancel_open('%s')\n", args
->path
);
788 if (upcall
->state_ref
== NULL
||
789 upcall
->state_ref
== INVALID_HANDLE_VALUE
)
790 goto out
; /* if handle_open() failed, the state was already freed */
792 if (state
->do_close
) {
794 stateid
.open
= state
;
795 stateid
.delegation
= NULL
;
796 stateid
.type
= STATEID_OPEN
;
797 memcpy(&stateid
.stateid
, &state
->stateid
, sizeof(stateid4
));
799 status
= nfs41_close(state
->session
, &state
->file
, &stateid
);
801 dprintf(1, "cancel_open: nfs41_close() failed with %s\n",
802 nfs_error_string(status
));
804 } else if (args
->created
) {
805 const nfs41_component
*name
= &state
->file
.name
;
806 /* break any delegations and truncate before REMOVE */
807 nfs41_delegation_return(state
->session
, &state
->file
,
808 OPEN_DELEGATE_WRITE
, TRUE
);
809 status
= nfs41_remove(state
->session
, &state
->parent
,
810 name
, state
->file
.fh
.fileid
);
812 dprintf(1, "cancel_open: nfs41_remove() failed with %s\n",
813 nfs_error_string(status
));
816 /* remove from the client's list of state for recovery */
817 client_state_remove(state
);
818 nfs41_open_state_deref(state
);
820 status
= nfs_to_windows_error(status
, ERROR_INTERNAL_ERROR
);
821 dprintf(1, "<-- cancel_open() returning %d\n", status
);
826 static int parse_close(unsigned char *buffer
, uint32_t length
, nfs41_upcall
*upcall
)
829 close_upcall_args
*args
= &upcall
->args
.close
;
831 status
= safe_read(&buffer
, &length
, &args
->remove
, sizeof(BOOLEAN
));
832 if (status
) goto out
;
833 status
= safe_read(&buffer
, &length
, &args
->srv_open
, sizeof(HANDLE
));
834 if (status
) goto out
;
836 status
= get_name(&buffer
, &length
, &args
->path
);
837 if (status
) goto out
;
838 status
= safe_read(&buffer
, &length
, &args
->renamed
, sizeof(BOOLEAN
));
839 if (status
) goto out
;
842 dprintf(1, "parsing NFS41_CLOSE: remove=%d srv_open=%x renamed=%d "
843 "filename='%s'\n", args
->remove
, args
->srv_open
, args
->renamed
,
844 args
->remove
? args
->path
: "");
849 static int do_nfs41_close(nfs41_open_state
*state
)
853 stateid
.open
= state
;
854 stateid
.delegation
= NULL
;
855 stateid
.type
= STATEID_OPEN
;
856 memcpy(&stateid
.stateid
, &state
->stateid
, sizeof(stateid4
));
858 status
= nfs41_close(state
->session
, &state
->file
, &stateid
);
860 dprintf(1, "nfs41_close() failed with error %s.\n",
861 nfs_error_string(status
));
862 status
= nfs_to_windows_error(status
, ERROR_INTERNAL_ERROR
);
868 static int handle_close(nfs41_upcall
*upcall
)
870 int status
= NFS4_OK
, rm_status
= NFS4_OK
;
871 close_upcall_args
*args
= &upcall
->args
.close
;
872 nfs41_open_state
*state
= upcall
->state_ref
;
874 /* return associated file layouts if necessary */
875 if (state
->type
== NF4REG
)
876 pnfs_layout_state_close(state
->session
, state
, args
->remove
);
878 if (state
->srv_open
== args
->srv_open
)
879 nfs41_delegation_remove_srvopen(state
->session
, &state
->file
);
882 nfs41_component
*name
= &state
->file
.name
;
885 dprintf(1, "removing a renamed file %s\n", name
->name
);
886 create_silly_rename(&state
->path
, &state
->file
.fh
, name
);
887 status
= do_nfs41_close(state
);
894 /* break any delegations and truncate before REMOVE */
895 nfs41_delegation_return(state
->session
, &state
->file
,
896 OPEN_DELEGATE_WRITE
, TRUE
);
898 dprintf(1, "calling nfs41_remove for %s\n", name
->name
);
900 rm_status
= nfs41_remove(state
->session
, &state
->parent
,
901 name
, state
->file
.fh
.fileid
);
903 if (rm_status
== NFS4ERR_FILE_OPEN
) {
904 status
= do_nfs41_close(state
);
910 dprintf(1, "nfs41_remove() failed with error %s.\n",
911 nfs_error_string(rm_status
));
912 rm_status
= nfs_to_windows_error(rm_status
, ERROR_INTERNAL_ERROR
);
916 if (state
->do_close
) {
917 status
= do_nfs41_close(state
);
920 /* remove from the client's list of state for recovery */
921 client_state_remove(state
);
923 if (status
|| !rm_status
)
929 static void cleanup_close(nfs41_upcall
*upcall
)
931 /* release the initial reference from create_open_state() */
932 nfs41_open_state_deref(upcall
->state_ref
);
936 const nfs41_upcall_op nfs41_op_open
= {
942 const nfs41_upcall_op nfs41_op_close
= {