xref: /reactos/base/services/nfsd/open.c (revision c2c66aff)
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