1 /* NFSv4.1 client for Windows
2 * Copyright � 2012 The Regents of the University of Michigan
3 *
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include <windows.h>
23 #include <stdio.h>
24 #include <strsafe.h>
25
26 #include "nfs41_ops.h"
27 #include "delegation.h"
28 #include "from_kernel.h"
29 #include "daemon_debug.h"
30 #include "upcall.h"
31 #include "util.h"
32
33
create_open_state(IN const char * path,IN uint32_t open_owner_id,OUT nfs41_open_state ** state_out)34 static int create_open_state(
35 IN const char *path,
36 IN uint32_t open_owner_id,
37 OUT nfs41_open_state **state_out)
38 {
39 int status;
40 nfs41_open_state *state;
41
42 state = calloc(1, sizeof(nfs41_open_state));
43 if (state == NULL) {
44 status = GetLastError();
45 goto out;
46 }
47
48 InitializeSRWLock(&state->path.lock);
49 if (FAILED(StringCchCopyA(state->path.path, NFS41_MAX_PATH_LEN, path))) {
50 status = ERROR_FILENAME_EXCED_RANGE;
51 goto out_free;
52 }
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);
57
58 StringCchPrintfA((LPSTR)state->owner.owner, NFS4_OPAQUE_LIMIT, "%u",
59 open_owner_id);
60 state->owner.owner_len = (uint32_t)strlen((const char*)state->owner.owner);
61 state->ref_count = 1;
62 list_init(&state->locks.list);
63 list_init(&state->client_entry);
64 InitializeCriticalSection(&state->locks.lock);
65
66 state->ea.list = INVALID_HANDLE_VALUE;
67 InitializeCriticalSection(&state->ea.lock);
68
69 *state_out = state;
70 status = NO_ERROR;
71 out:
72 return status;
73
74 out_free:
75 free(state);
76 goto out;
77 }
78
open_state_free(IN nfs41_open_state * state)79 static void open_state_free(
80 IN nfs41_open_state *state)
81 {
82 struct list_entry *entry, *tmp;
83
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)
90 free(state->ea.list);
91 free(state);
92 }
93
94
95 /* open state reference counting */
nfs41_open_state_ref(IN nfs41_open_state * state)96 void nfs41_open_state_ref(
97 IN nfs41_open_state *state)
98 {
99 const LONG count = InterlockedIncrement(&state->ref_count);
100
101 dprintf(2, "nfs41_open_state_ref(%s) count %d\n", state->path.path, count);
102 }
103
nfs41_open_state_deref(IN nfs41_open_state * state)104 void nfs41_open_state_deref(
105 IN nfs41_open_state *state)
106 {
107 const LONG count = InterlockedDecrement(&state->ref_count);
108
109 dprintf(2, "nfs41_open_state_deref(%s) count %d\n", state->path.path, count);
110 if (count == 0)
111 open_state_free(state);
112 }
113
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
124 * be used. */
nfs41_open_stateid_arg(IN nfs41_open_state * state,OUT stateid_arg * arg)125 void nfs41_open_stateid_arg(
126 IN nfs41_open_state *state,
127 OUT stateid_arg *arg)
128 {
129 arg->open = state;
130 arg->delegation = NULL;
131
132 AcquireSRWLockShared(&state->lock);
133
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));
140 }
141 ReleaseSRWLockShared(&deleg->lock);
142
143 if (arg->type == STATEID_DELEG_FILE)
144 goto out;
145
146 dprintf(2, "delegation recalled, waiting for open stateid..\n");
147
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);
152 }
153
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;
160 } else {
161 memset(&arg->stateid, 0, sizeof(stateid4));
162 arg->type = STATEID_SPECIAL;
163 }
164 out:
165 ReleaseSRWLockShared(&state->lock);
166 }
167
168 /* client list of associated open state */
client_state_add(IN nfs41_open_state * state)169 static void client_state_add(
170 IN nfs41_open_state *state)
171 {
172 nfs41_client *client = state->session->client;
173
174 EnterCriticalSection(&client->state.lock);
175 list_add_tail(&client->state.opens, &state->client_entry);
176 LeaveCriticalSection(&client->state.lock);
177 }
178
client_state_remove(IN nfs41_open_state * state)179 static void client_state_remove(
180 IN nfs41_open_state *state)
181 {
182 nfs41_client *client = state->session->client;
183
184 EnterCriticalSection(&client->state.lock);
185 list_remove(&state->client_entry);
186 LeaveCriticalSection(&client->state.lock);
187 }
188
do_open(IN OUT nfs41_open_state * state,IN uint32_t create,IN uint32_t createhow,IN nfs41_file_info * createattrs,IN bool_t try_recovery,OUT nfs41_file_info * info)189 static int do_open(
190 IN OUT nfs41_open_state *state,
191 IN uint32_t create,
192 IN uint32_t createhow,
193 IN nfs41_file_info *createattrs,
194 IN bool_t try_recovery,
195 OUT nfs41_file_info *info)
196 {
197 open_claim4 claim;
198 stateid4 open_stateid;
199 open_delegation4 delegation = { 0 };
200 nfs41_delegation_state *deleg_state = NULL;
201 int status;
202
203 claim.claim = CLAIM_NULL;
204 claim.u.null.filename = &state->file.name;
205
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,
209 &delegation, info);
210 if (status)
211 goto out;
212
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);
216 if (deleg_state) {
217 deleg_state->srv_open = state->srv_open;
218 dprintf(1, "do_open: received delegation: saving srv_open = %x\n",
219 state->srv_open);
220 }
221
222 AcquireSRWLockExclusive(&state->lock);
223 /* update the stateid */
224 memcpy(&state->stateid, &open_stateid, sizeof(open_stateid));
225 state->do_close = 1;
226 state->delegation.state = deleg_state;
227 ReleaseSRWLockExclusive(&state->lock);
228 out:
229 return status;
230 }
231
open_or_delegate(IN OUT nfs41_open_state * state,IN uint32_t create,IN uint32_t createhow,IN nfs41_file_info * createattrs,IN bool_t try_recovery,OUT nfs41_file_info * info)232 static int open_or_delegate(
233 IN OUT nfs41_open_state *state,
234 IN uint32_t create,
235 IN uint32_t createhow,
236 IN nfs41_file_info *createattrs,
237 IN bool_t try_recovery,
238 OUT nfs41_file_info *info)
239 {
240 int status;
241
242 /* check for existing delegation */
243 status = nfs41_delegate_open(state, create, createattrs, info);
244
245 /* get an open stateid if we have no delegation stateid */
246 if (status)
247 status = do_open(state, create, createhow,
248 createattrs, try_recovery, info);
249
250 state->pnfs_last_offset = info->size ? info->size - 1 : 0;
251
252 /* register the client's open state on success */
253 if (status == NFS4_OK)
254 client_state_add(state);
255 return status;
256 }
257
258
parse_abs_path(unsigned char ** buffer,uint32_t * length,nfs41_abs_path * path)259 static int parse_abs_path(unsigned char **buffer, uint32_t *length, nfs41_abs_path *path)
260 {
261 int status = safe_read(buffer, length, &path->len, sizeof(USHORT));
262 if (status) goto out;
263 if (path->len == 0)
264 goto out;
265 if (path->len >= NFS41_MAX_PATH_LEN) {
266 status = ERROR_BUFFER_OVERFLOW;
267 goto out;
268 }
269 status = safe_read(buffer, length, path->path, path->len);
270 if (status) goto out;
271 path->len--; /* subtract 1 for null */
272 out:
273 return status;
274 }
275
276 /* NFS41_OPEN */
parse_open(unsigned char * buffer,uint32_t length,nfs41_upcall * upcall)277 static int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
278 {
279 int status;
280 open_upcall_args *args = &upcall->args.open;
281
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;
304
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);
316 out:
317 return status;
318 }
319
open_for_attributes(uint32_t type,ULONG access_mask,ULONG disposition,int status)320 static BOOLEAN open_for_attributes(uint32_t type, ULONG access_mask,
321 ULONG disposition, int status)
322 {
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");
329 return TRUE;
330 } else {
331 dprintf(1, "Creating a directory\n");
332 return FALSE;
333 }
334 }
335
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)
345 return FALSE;
346 else {
347 dprintf(1, "Open call that wants to manage attributes\n");
348 return TRUE;
349 }
350 }
351
map_disposition_2_nfsopen(ULONG disposition,int in_status,bool_t persistent,uint32_t * create,uint32_t * createhowmode,uint32_t * last_error)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)
355 {
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
366 if (!in_status)
367 status = ERROR_FILE_EXISTS;
368 else {
369 *create = OPEN4_CREATE;
370 if (persistent) *createhowmode = GUARDED4;
371 else *createhowmode = EXCLUSIVE4_1;
372 }
373 } else if (disposition == FILE_OPEN) {
374 if (in_status == NFS4ERR_NOENT)
375 status = ERROR_FILE_NOT_FOUND;
376 else
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;
383 } else {
384 dprintf(1, "opening existing file\n");
385 *create = OPEN4_NOCREATE;
386 }
387 } else if (disposition == FILE_OVERWRITE) {
388 if (in_status == NFS4ERR_NOENT)
389 status = ERROR_FILE_NOT_FOUND;
390 //truncate file
391 *create = OPEN4_CREATE;
392 } else if (disposition == FILE_OVERWRITE_IF) {
393 if (in_status == NFS4ERR_NOENT)
394 *last_error = ERROR_FILE_NOT_FOUND;
395 //truncate file
396 *create = OPEN4_CREATE;
397 }
398 return status;
399 }
400
map_access_2_allowdeny(ULONG access_mask,ULONG access_mode,ULONG disposition,uint32_t * allow,uint32_t * deny)401 static void map_access_2_allowdeny(ULONG access_mask, ULONG access_mode,
402 ULONG disposition, uint32_t *allow, uint32_t *deny)
403 {
404 if ((access_mask &
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).
416 */
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;
423
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;
433 else
434 *deny = OPEN4_SHARE_DENY_BOTH;
435 #else
436 // AGLO: 11/13/2009.
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;
441 #endif
442 }
443
check_execute_access(nfs41_open_state * state)444 static int check_execute_access(nfs41_open_state *state)
445 {
446 uint32_t supported, access;
447 int status = nfs41_access(state->session, &state->file,
448 ACCESS4_EXECUTE | ACCESS4_READ, &supported, &access);
449 if (status) {
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;
460 }
461 } else if ((access & ACCESS4_EXECUTE) == 0) {
462 dprintf(1, "user does not have execute access to file %s\n",
463 state->path.path);
464 status = ERROR_ACCESS_DENIED;
465 } else
466 dprintf(2, "user has execute access to file\n");
467 return status;
468 }
469
create_with_ea(IN uint32_t disposition,IN uint32_t lookup_status)470 static int create_with_ea(
471 IN uint32_t disposition,
472 IN uint32_t lookup_status)
473 {
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);
478 }
479
handle_open(nfs41_upcall * upcall)480 static int handle_open(nfs41_upcall *upcall)
481 {
482 int status = 0;
483 open_upcall_args *args = &upcall->args.open;
484 nfs41_open_state *state;
485 nfs41_file_info info = { 0 };
486
487 status = create_open_state(args->path, args->open_owner_id, &state);
488 if (status) {
489 eprintf("create_open_state(%d) failed with %d\n",
490 args->open_owner_id, status);
491 goto out;
492 }
493 state->srv_open = args->srv_open;
494
495 // first check if windows told us it's a directory
496 if (args->create_opts & FILE_DIRECTORY_FILE)
497 state->type = NF4DIR;
498 else
499 state->type = NF4REG;
500
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);
504
505 if (status == ERROR_REPARSE) {
506 uint32_t depth = 0;
507 /* one of the parent components was a symlink */
508 do {
509 if (++depth > NFS41_MAX_SYMLINK_DEPTH) {
510 status = ERROR_TOO_MANY_LINKS;
511 goto out_free_state;
512 }
513
514 /* replace the path with the symlink target's */
515 status = nfs41_symlink_target(state->session,
516 &state->parent, &state->path);
517 if (status) {
518 /* can't do the reparse if we can't get the target */
519 eprintf("nfs41_symlink_target() failed with %d\n", status);
520 goto out_free_state;
521 }
522
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);
527
528 if (status == NO_ERROR || status == ERROR_FILE_NOT_FOUND) {
529 abs_path_copy(&args->symlink, &state->path);
530 status = NO_ERROR;
531 upcall->last_error = ERROR_REPARSE;
532 args->symlink_embedded = TRUE;
533 }
534 goto out_free_state;
535 }
536
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",
543 state->path.path);
544 status = ERROR_DIRECTORY;
545 goto out_free_state;
546 }
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",
551 state->path.path);
552 status = ERROR_BAD_FILE_TYPE;
553 goto out_free_state;
554 }
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;
565 } else {
566 /* replace the path with the symlink target */
567 status = nfs41_symlink_target(state->session,
568 &state->file, &args->symlink);
569 if (status) {
570 eprintf("nfs41_symlink_target() for %s failed with %d\n",
571 args->path, status);
572 } else {
573 /* tell the driver to call RxPrepareToReparseSymbolicLink() */
574 upcall->last_error = ERROR_REPARSE;
575 args->symlink_embedded = FALSE;
576 }
577 goto out_free_state;
578 }
579 } else
580 dprintf(2, "handle_open(): unsupported type=%d\n", info.type);
581 state->type = info.type;
582 } else if (status != ERROR_FILE_NOT_FOUND)
583 goto out_free_state;
584
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;
602 goto out_free_state;
603 }
604
605 /* defer the call to CREATE until we get the symlink set upcall */
606 dprintf(1, "trying to create a symlink, deferring create\n");
607
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;
611
612 status = NO_ERROR;
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;
620
621 dprintf(1, "creating cygwin symlink %s -> %s\n",
622 state->file.name.name, args->symlink.path);
623
624 status = nfs41_create(state->session, NF4LNK, &createattrs,
625 args->symlink.path, &state->parent, &state->file, &info);
626 if (status) {
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);
630 goto out_free_state;
631 }
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)) {
638 if (status) {
639 dprintf(1, "nfs41_lookup failed with %d\n", status);
640 goto out_free_state;
641 }
642
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;
647 } else {
648 nfs41_file_info createattrs = { 0 };
649 uint32_t create = 0, createhowmode = 0, lookup_status = status;
650
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;
657 goto out_free_state;
658 }
659 if (args->disposition != FILE_SUPERSEDE)
660 args->mode = info.mode;
661 }
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;
669
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);
675 if (status)
676 goto out_free_state;
677
678 if (args->access_mask & FILE_EXECUTE && state->file.fh.len) {
679 status = check_execute_access(state);
680 if (status)
681 goto out_free_state;
682 }
683
684 supersede_retry:
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);
691
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);
695 if (status)
696 goto out_free_state;
697 }
698
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;
703 } else {
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,
708 TRUE, &info);
709 if (status == NFS4_OK && state->delegation.state)
710 args->deleg_type = state->delegation.state->state.type;
711 }
712 if (status) {
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);
719 goto out_free_state;
720 } else {
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;
725 }
726
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);
731 }
732 }
733
734 upcall->state_ref = state;
735 nfs41_open_state_ref(upcall->state_ref);
736 out:
737 return status;
738 out_free_state:
739 nfs41_open_state_deref(state);
740 goto out;
741 }
742
marshall_open(unsigned char * buffer,uint32_t * length,nfs41_upcall * upcall)743 static int marshall_open(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
744 {
745 int status;
746 open_upcall_args *args = &upcall->args.open;
747
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;
771 goto out;
772 }
773 }
774 dprintf(2, "NFS41_OPEN: downcall open_state=0x%p mode %o changeattr 0x%llu\n",
775 upcall->state_ref, args->mode, args->changeattr);
776 out:
777 return status;
778 }
779
cancel_open(IN nfs41_upcall * upcall)780 static void cancel_open(IN nfs41_upcall *upcall)
781 {
782 int status = NFS4_OK;
783 open_upcall_args *args = &upcall->args.open;
784 nfs41_open_state *state = upcall->state_ref;
785
786 dprintf(1, "--> cancel_open('%s')\n", args->path);
787
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 */
791
792 if (state->do_close) {
793 stateid_arg stateid;
794 stateid.open = state;
795 stateid.delegation = NULL;
796 stateid.type = STATEID_OPEN;
797 memcpy(&stateid.stateid, &state->stateid, sizeof(stateid4));
798
799 status = nfs41_close(state->session, &state->file, &stateid);
800 if (status)
801 dprintf(1, "cancel_open: nfs41_close() failed with %s\n",
802 nfs_error_string(status));
803
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);
811 if (status)
812 dprintf(1, "cancel_open: nfs41_remove() failed with %s\n",
813 nfs_error_string(status));
814 }
815
816 /* remove from the client's list of state for recovery */
817 client_state_remove(state);
818 nfs41_open_state_deref(state);
819 out:
820 status = nfs_to_windows_error(status, ERROR_INTERNAL_ERROR);
821 dprintf(1, "<-- cancel_open() returning %d\n", status);
822 }
823
824
825 /* NFS41_CLOSE */
parse_close(unsigned char * buffer,uint32_t length,nfs41_upcall * upcall)826 static int parse_close(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
827 {
828 int status;
829 close_upcall_args *args = &upcall->args.close;
830
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;
835 if (args->remove) {
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;
840 }
841
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 : "");
845 out:
846 return status;
847 }
848
do_nfs41_close(nfs41_open_state * state)849 static int do_nfs41_close(nfs41_open_state *state)
850 {
851 int status;
852 stateid_arg stateid;
853 stateid.open = state;
854 stateid.delegation = NULL;
855 stateid.type = STATEID_OPEN;
856 memcpy(&stateid.stateid, &state->stateid, sizeof(stateid4));
857
858 status = nfs41_close(state->session, &state->file, &stateid);
859 if (status) {
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);
863 }
864
865 return status;
866 }
867
handle_close(nfs41_upcall * upcall)868 static int handle_close(nfs41_upcall *upcall)
869 {
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;
873
874 /* return associated file layouts if necessary */
875 if (state->type == NF4REG)
876 pnfs_layout_state_close(state->session, state, args->remove);
877
878 if (state->srv_open == args->srv_open)
879 nfs41_delegation_remove_srvopen(state->session, &state->file);
880
881 if (args->remove) {
882 nfs41_component *name = &state->file.name;
883
884 if (args->renamed) {
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);
888 if (status)
889 goto out;
890 else
891 state->do_close = 0;
892 }
893
894 /* break any delegations and truncate before REMOVE */
895 nfs41_delegation_return(state->session, &state->file,
896 OPEN_DELEGATE_WRITE, TRUE);
897
898 dprintf(1, "calling nfs41_remove for %s\n", name->name);
899 retry_delete:
900 rm_status = nfs41_remove(state->session, &state->parent,
901 name, state->file.fh.fileid);
902 if (rm_status) {
903 if (rm_status == NFS4ERR_FILE_OPEN) {
904 status = do_nfs41_close(state);
905 if (!status) {
906 state->do_close = 0;
907 goto retry_delete;
908 } else goto out;
909 }
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);
913 }
914 }
915
916 if (state->do_close) {
917 status = do_nfs41_close(state);
918 }
919 out:
920 /* remove from the client's list of state for recovery */
921 client_state_remove(state);
922
923 if (status || !rm_status)
924 return status;
925 else
926 return rm_status;
927 }
928
cleanup_close(nfs41_upcall * upcall)929 static void cleanup_close(nfs41_upcall *upcall)
930 {
931 /* release the initial reference from create_open_state() */
932 nfs41_open_state_deref(upcall->state_ref);
933 }
934
935
936 const nfs41_upcall_op nfs41_op_open = {
937 parse_open,
938 handle_open,
939 marshall_open,
940 cancel_open
941 };
942 const nfs41_upcall_op nfs41_op_close = {
943 parse_close,
944 handle_close,
945 NULL,
946 NULL,
947 cleanup_close
948 };
949