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 #define MINIRDR__NAME "Value is ignored, only fact of definition"
23 #include <rx.h>
24 #include <windef.h>
25 #include <winerror.h>
26 
27 #include <ntstrsafe.h>
28 
29 #ifdef __REACTOS__
30 #include <pseh/pseh2.h>
31 #endif
32 
33 #include "nfs41_driver.h"
34 #include "nfs41_np.h"
35 #include "nfs41_debug.h"
36 
37 #if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
38 NTSTATUS NTAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
39                                  ULONG *utf8_bytes_written,
40                                  const WCHAR *uni_src, ULONG uni_bytes);
41 NTSTATUS NTAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
42                                  ULONG *uni_bytes_written,
43                                  const CHAR *utf8_src, ULONG utf8_bytes);
44 #endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
45 
46 #define USE_MOUNT_SEC_CONTEXT
47 
48 /* debugging printout defines */
49 //#define DEBUG_FSDDISPATCH
50 #define DEBUG_MARSHAL_HEADER
51 #define DEBUG_MARSHAL_DETAIL
52 //#define DEBUG_OPEN
53 //#define DEBUG_CLOSE
54 //#define DEBUG_CACHE
55 #define DEBUG_INVALIDATE_CACHE
56 //#define DEBUG_READ
57 //#define DEBUG_WRITE
58 //#define DEBUG_DIR_QUERY
59 //#define DEBUG_FILE_QUERY
60 //#define DEBUG_FILE_SET
61 //#define DEBUG_ACL_QUERY
62 //#define DEBUG_ACL_SET
63 //#define DEBUG_EA_QUERY
64 //#define DEBUG_EA_SET
65 //#define DEBUG_LOCK
66 //#define DEBUG_MISC
67 #define DEBUG_TIME_BASED_COHERENCY
68 #define DEBUG_MOUNT
69 //#define DEBUG_VOLUME_QUERY
70 
71 //#define ENABLE_TIMINGS
72 //#define ENABLE_INDV_TIMINGS
73 #ifdef ENABLE_TIMINGS
74 typedef struct __nfs41_timings {
75     LONG tops, sops;
76     LONGLONG ticks, size;
77 } nfs41_timings;
78 
79 nfs41_timings lookup, readdir, open, close, getattr, setattr, getacl, setacl, volume,
80     read, write, lock, unlock, setexattr, getexattr;
81 #endif
82 DRIVER_INITIALIZE DriverEntry;
83 DRIVER_UNLOAD nfs41_driver_unload;
84 DRIVER_DISPATCH ( nfs41_FsdDispatch );
85 
86 struct _MINIRDR_DISPATCH nfs41_ops;
87 PRDBSS_DEVICE_OBJECT nfs41_dev;
88 
89 #define DISABLE_CACHING 0
90 #define ENABLE_READ_CACHING 1
91 #define ENABLE_WRITE_CACHING 2
92 #define ENABLE_READWRITE_CACHING 3
93 
94 #define NFS41_MM_POOLTAG        ('nfs4')
95 #define NFS41_MM_POOLTAG_ACL    ('acls')
96 #define NFS41_MM_POOLTAG_MOUNT  ('mnts')
97 #define NFS41_MM_POOLTAG_OPEN   ('open')
98 #define NFS41_MM_POOLTAG_UP     ('upca')
99 #define NFS41_MM_POOLTAG_DOWN   ('down')
100 
101 KEVENT upcallEvent;
102 FAST_MUTEX upcallLock, downcallLock, fcblistLock;
103 FAST_MUTEX xidLock;
104 FAST_MUTEX openOwnerLock;
105 
106 LONGLONG xid = 0;
107 LONG open_owner_id = 1;
108 
109 #define DECLARE_CONST_ANSI_STRING(_var, _string) \
110     const CHAR _var ## _buffer[] = _string; \
111     const ANSI_STRING _var = { sizeof(_string) - sizeof(CHAR), \
112         sizeof(_string), (PCH) _var ## _buffer }
113 #define RELATIVE(wait) (-(wait))
114 #define NANOSECONDS(nanos) (((signed __int64)(nanos)) / 100L)
115 #define MICROSECONDS(micros) (((signed __int64)(micros)) * NANOSECONDS(1000L))
116 #define MILLISECONDS(milli) (((signed __int64)(milli)) * MICROSECONDS(1000L))
117 #define SECONDS(seconds) (((signed __int64)(seconds)) * MILLISECONDS(1000L))
118 
119 DECLARE_CONST_ANSI_STRING(NfsV3Attributes, "NfsV3Attributes");
120 DECLARE_CONST_ANSI_STRING(NfsSymlinkTargetName, "NfsSymlinkTargetName");
121 DECLARE_CONST_ANSI_STRING(NfsActOnLink, "NfsActOnLink");
122 
123 INLINE BOOL AnsiStrEq(
124     IN const ANSI_STRING *lhs,
125     IN const CHAR *rhs,
126     IN const UCHAR rhs_len)
127 {
128     return lhs->Length == rhs_len &&
129         RtlCompareMemory(lhs->Buffer, rhs, rhs_len) == rhs_len;
130 }
131 
132 typedef struct _nfs3_attrs {
133     DWORD type, mode, nlink, uid, gid, filler1;
134     LARGE_INTEGER size, used;
135     struct {
136         DWORD specdata1;
137         DWORD specdata2;
138     } rdev;
139     LONGLONG fsid, fileid;
140     LONGLONG atime, mtime, ctime;
141 } nfs3_attrs;
142 LARGE_INTEGER unix_time_diff; //needed to convert windows time to unix
143 
144 enum ftype3 {
145     NF3REG = 1,
146     NF3DIR,
147     NF3BLK,
148     NF3CHR,
149     NF3LNK,
150     NF3SOCK,
151     NF3FIFO
152 };
153 
154 typedef enum _nfs41_updowncall_state {
155     NFS41_WAITING_FOR_UPCALL,
156     NFS41_WAITING_FOR_DOWNCALL,
157     NFS41_DONE_PROCESSING,
158     NFS41_NOT_WAITING
159 } nfs41_updowncall_state;
160 
161 #ifdef __REACTOS__
162 #undef _errno
163 #undef errno
164 #endif
165 
166 typedef struct _updowncall_entry {
167     DWORD version;
168     LONGLONG xid;
169     DWORD opcode;
170     NTSTATUS status;
171     nfs41_updowncall_state state;
172     FAST_MUTEX lock;
173     LIST_ENTRY next;
174     KEVENT cond;
175     DWORD errno;
176     BOOLEAN async_op;
177     SECURITY_CLIENT_CONTEXT sec_ctx;
178     PSECURITY_CLIENT_CONTEXT psec_ctx;
179     HANDLE open_state;
180     HANDLE session;
181     PUNICODE_STRING filename;
182     PVOID buf;
183     ULONG buf_len;
184     ULONGLONG ChangeTime;
185     union {
186         struct {
187             PUNICODE_STRING srv_name;
188             PUNICODE_STRING root;
189             PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs;
190             DWORD sec_flavor;
191             DWORD rsize;
192             DWORD wsize;
193             DWORD lease_time;
194         } Mount;
195         struct {
196             PMDL MdlAddress;
197             ULONGLONG offset;
198             PRX_CONTEXT rxcontext;
199         } ReadWrite;
200         struct {
201             LONGLONG offset;
202             LONGLONG length;
203             BOOLEAN exclusive;
204             BOOLEAN blocking;
205         } Lock;
206         struct {
207             ULONG count;
208             LOWIO_LOCK_LIST locks;
209         } Unlock;
210         struct {
211             FILE_BASIC_INFORMATION binfo;
212             FILE_STANDARD_INFORMATION sinfo;
213             UNICODE_STRING symlink;
214             ULONG access_mask;
215             ULONG access_mode;
216             ULONG attrs;
217             ULONG copts;
218             ULONG disp;
219             ULONG cattrs;
220             LONG open_owner_id;
221             DWORD mode;
222             HANDLE srv_open;
223             DWORD deleg_type;
224             BOOLEAN symlink_embedded;
225             PMDL EaMdl;
226             PVOID EaBuffer;
227         } Open;
228         struct {
229             HANDLE srv_open;
230             BOOLEAN remove;
231             BOOLEAN renamed;
232         } Close;
233         struct {
234             PUNICODE_STRING filter;
235             FILE_INFORMATION_CLASS InfoClass;
236             BOOLEAN restart_scan;
237             BOOLEAN return_single;
238             BOOLEAN initial_query;
239             PMDL mdl;
240             PVOID mdl_buf;
241         } QueryFile;
242         struct {
243             FILE_INFORMATION_CLASS InfoClass;
244         } SetFile;
245         struct {
246             DWORD mode;
247         } SetEa;
248         struct {
249             PVOID EaList;
250             ULONG EaListLength;
251             ULONG Overflow;
252             ULONG EaIndex;
253             BOOLEAN ReturnSingleEntry;
254             BOOLEAN RestartScan;
255         } QueryEa;
256         struct {
257             PUNICODE_STRING target;
258             BOOLEAN set;
259         } Symlink;
260         struct {
261             FS_INFORMATION_CLASS query;
262         } Volume;
263         struct {
264             SECURITY_INFORMATION query;
265         } Acl;
266     } u;
267 
268 } nfs41_updowncall_entry;
269 
270 typedef struct _updowncall_list {
271     LIST_ENTRY head;
272 } nfs41_updowncall_list;
273 nfs41_updowncall_list upcall, downcall;
274 
275 typedef struct _nfs41_mount_entry {
276     LIST_ENTRY next;
277     LUID login_id;
278     HANDLE authsys_session;
279     HANDLE gss_session;
280     HANDLE gssi_session;
281     HANDLE gssp_session;
282 } nfs41_mount_entry;
283 
284 typedef struct _nfs41_mount_list {
285     LIST_ENTRY head;
286 } nfs41_mount_list;
287 
288 #define nfs41_AddEntry(lock,list,pEntry)                    \
289             ExAcquireFastMutex(&lock);                      \
290             InsertTailList(&(list).head, &(pEntry)->next);  \
291             ExReleaseFastMutex(&lock);
292 #define nfs41_RemoveFirst(lock,list,pEntry)                 \
293             ExAcquireFastMutex(&lock);                      \
294             pEntry = (IsListEmpty(&(list).head)             \
295             ? NULL                                          \
296             : RemoveHeadList(&(list).head));                \
297             ExReleaseFastMutex(&lock);
298 #define nfs41_RemoveEntry(lock,pEntry)                      \
299             ExAcquireFastMutex(&lock);                      \
300             RemoveEntryList(&pEntry->next);                 \
301             ExReleaseFastMutex(&lock);
302 #define nfs41_IsListEmpty(lock,list,flag)                   \
303             ExAcquireFastMutex(&lock);                      \
304             flag = IsListEmpty(&(list).head);               \
305             ExReleaseFastMutex(&lock);
306 #define nfs41_GetFirstEntry(lock,list,pEntry)               \
307             ExAcquireFastMutex(&lock);                      \
308             pEntry = (IsListEmpty(&(list).head)             \
309              ? NULL                                         \
310              : (nfs41_updowncall_entry *)                   \
311                (CONTAINING_RECORD((list).head.Flink,        \
312                                   nfs41_updowncall_entry,   \
313                                   next)));                  \
314             ExReleaseFastMutex(&lock);
315 #define nfs41_GetFirstMountEntry(lock,list,pEntry)          \
316             ExAcquireFastMutex(&lock);                      \
317             pEntry = (IsListEmpty(&(list).head)             \
318              ? NULL                                         \
319              : (nfs41_mount_entry *)                        \
320                (CONTAINING_RECORD((list).head.Flink,        \
321                                   nfs41_mount_entry,        \
322                                   next)));                  \
323             ExReleaseFastMutex(&lock);
324 
325 /* In order to cooperate with other network providers,
326  * we only claim paths of the format '\\server\nfs4\path' */
327 DECLARE_CONST_UNICODE_STRING(NfsPrefix, L"\\nfs4");
328 DECLARE_CONST_UNICODE_STRING(AUTH_SYS_NAME, L"sys");
329 DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5_NAME, L"krb5");
330 DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5I_NAME, L"krb5i");
331 DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5P_NAME, L"krb5p");
332 DECLARE_CONST_UNICODE_STRING(SLASH, L"\\");
333 DECLARE_CONST_UNICODE_STRING(EMPTY_STRING, L"");
334 
335 #define SERVER_NAME_BUFFER_SIZE         1024
336 #define MOUNT_CONFIG_RW_SIZE_MIN        1024
337 #define MOUNT_CONFIG_RW_SIZE_DEFAULT    1048576
338 #define MOUNT_CONFIG_RW_SIZE_MAX        1048576
339 #define MAX_SEC_FLAVOR_LEN              12
340 #define UPCALL_TIMEOUT_DEFAULT          50  /* in seconds */
341 
342 typedef struct _NFS41_MOUNT_CONFIG {
343     DWORD ReadSize;
344     DWORD WriteSize;
345     BOOLEAN ReadOnly;
346     BOOLEAN write_thru;
347     BOOLEAN nocache;
348     WCHAR srv_buffer[SERVER_NAME_BUFFER_SIZE];
349     UNICODE_STRING SrvName;
350     WCHAR mntpt_buffer[MAX_PATH];
351     UNICODE_STRING MntPt;
352     WCHAR sec_flavor[MAX_SEC_FLAVOR_LEN];
353     UNICODE_STRING SecFlavor;
354     DWORD timeout;
355 } NFS41_MOUNT_CONFIG, *PNFS41_MOUNT_CONFIG;
356 
357 typedef struct _NFS41_NETROOT_EXTENSION {
358     NODE_TYPE_CODE          NodeTypeCode;
359     NODE_BYTE_SIZE          NodeByteSize;
360     DWORD                   nfs41d_version;
361     BOOLEAN                 mounts_init;
362     FAST_MUTEX              mountLock;
363     nfs41_mount_list        mounts;
364 } NFS41_NETROOT_EXTENSION, *PNFS41_NETROOT_EXTENSION;
365 #define NFS41GetNetRootExtension(pNetRoot)      \
366         (((pNetRoot) == NULL) ? NULL :          \
367         (PNFS41_NETROOT_EXTENSION)((pNetRoot)->Context))
368 
369 /* FileSystemName as reported by FileFsAttributeInfo query */
370 #define FS_NAME     L"NFS"
371 #define FS_NAME_LEN (sizeof(FS_NAME) - sizeof(WCHAR))
372 #define FS_ATTR_LEN (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + FS_NAME_LEN)
373 
374 /* FileSystemName as reported by FileFsAttributeInfo query */
375 #define VOL_NAME     L"PnfsVolume"
376 #define VOL_NAME_LEN (sizeof(VOL_NAME) - sizeof(WCHAR))
377 #define VOL_ATTR_LEN (sizeof(FILE_FS_VOLUME_INFORMATION) + VOL_NAME_LEN)
378 
379 typedef struct _NFS41_V_NET_ROOT_EXTENSION {
380     NODE_TYPE_CODE          NodeTypeCode;
381     NODE_BYTE_SIZE          NodeByteSize;
382     HANDLE                  session;
383     FILE_FS_ATTRIBUTE_INFORMATION FsAttrs;
384     DWORD                   sec_flavor;
385     DWORD                   timeout;
386     USHORT                  MountPathLen;
387     BOOLEAN                 read_only;
388     BOOLEAN                 write_thru;
389     BOOLEAN                 nocache;
390 #define STORE_MOUNT_SEC_CONTEXT
391 #ifdef STORE_MOUNT_SEC_CONTEXT
392     SECURITY_CLIENT_CONTEXT mount_sec_ctx;
393 #endif
394 } NFS41_V_NET_ROOT_EXTENSION, *PNFS41_V_NET_ROOT_EXTENSION;
395 #define NFS41GetVNetRootExtension(pVNetRoot)      \
396         (((pVNetRoot) == NULL) ? NULL :           \
397         (PNFS41_V_NET_ROOT_EXTENSION)((pVNetRoot)->Context))
398 
399 typedef struct _NFS41_FCB {
400     NODE_TYPE_CODE          NodeTypeCode;
401     NODE_BYTE_SIZE          NodeByteSize;
402     FILE_BASIC_INFORMATION  BasicInfo;
403     FILE_STANDARD_INFORMATION StandardInfo;
404     BOOLEAN                 Renamed;
405     BOOLEAN                 DeletePending;
406     DWORD                   mode;
407     ULONGLONG               changeattr;
408 } NFS41_FCB, *PNFS41_FCB;
409 #define NFS41GetFcbExtension(pFcb)      \
410         (((pFcb) == NULL) ? NULL : (PNFS41_FCB)((pFcb)->Context))
411 
412 typedef struct _NFS41_FOBX {
413     NODE_TYPE_CODE          NodeTypeCode;
414     NODE_BYTE_SIZE          NodeByteSize;
415 
416     HANDLE nfs41_open_state;
417     SECURITY_CLIENT_CONTEXT sec_ctx;
418     PVOID acl;
419     DWORD acl_len;
420     LARGE_INTEGER time;
421     DWORD deleg_type;
422     BOOLEAN write_thru;
423     BOOLEAN nocache;
424 } NFS41_FOBX, *PNFS41_FOBX;
425 #define NFS41GetFobxExtension(pFobx)  \
426         (((pFobx) == NULL) ? NULL : (PNFS41_FOBX)((pFobx)->Context))
427 
428 typedef struct _NFS41_SERVER_ENTRY {
429     PMRX_SRV_CALL                 pRdbssSrvCall;
430     WCHAR                         NameBuffer[SERVER_NAME_BUFFER_SIZE];
431     UNICODE_STRING                Name;             // the server name.
432 } NFS41_SERVER_ENTRY, *PNFS41_SERVER_ENTRY;
433 
434 typedef struct _NFS41_DEVICE_EXTENSION {
435     NODE_TYPE_CODE          NodeTypeCode;
436     NODE_BYTE_SIZE          NodeByteSize;
437     PRDBSS_DEVICE_OBJECT    DeviceObject;
438     ULONG                   ActiveNodes;
439     HANDLE                  SharedMemorySection;
440     DWORD                   nfs41d_version;
441     BYTE                    VolAttrs[VOL_ATTR_LEN];
442     DWORD                   VolAttrsLen;
443     HANDLE                  openlistHandle;
444 } NFS41_DEVICE_EXTENSION, *PNFS41_DEVICE_EXTENSION;
445 
446 #define NFS41GetDeviceExtension(RxContext,pExt)        \
447         PNFS41_DEVICE_EXTENSION pExt = (PNFS41_DEVICE_EXTENSION) \
448         ((PBYTE)(RxContext->RxDeviceObject) + sizeof(RDBSS_DEVICE_OBJECT))
449 
450 typedef struct _nfs41_fcb_list_entry {
451     LIST_ENTRY next;
452     PMRX_FCB fcb;
453     HANDLE session;
454     PNFS41_FOBX nfs41_fobx;
455     ULONGLONG ChangeTime;
456     BOOLEAN skip;
457 } nfs41_fcb_list_entry;
458 
459 typedef struct _nfs41_fcb_list {
460     LIST_ENTRY head;
461 } nfs41_fcb_list;
462 nfs41_fcb_list openlist;
463 
464 typedef enum _NULMRX_STORAGE_TYPE_CODES {
465     NTC_NFS41_DEVICE_EXTENSION      =   (NODE_TYPE_CODE)0xFC00,
466 } NFS41_STORAGE_TYPE_CODES;
467 #define RxDefineNode( node, type )          \
468         node->NodeTypeCode = NTC_##type;    \
469         node->NodeByteSize = sizeof(type);
470 
471 #define RDR_NULL_STATE  0
472 #define RDR_UNLOADED    1
473 #define RDR_UNLOADING   2
474 #define RDR_LOADING     3
475 #define RDR_LOADED      4
476 #define RDR_STOPPED     5
477 #define RDR_STOPPING    6
478 #define RDR_STARTING    7
479 #define RDR_STARTED     8
480 
481 nfs41_init_driver_state nfs41_init_state = NFS41_INIT_DRIVER_STARTABLE;
482 nfs41_start_driver_state nfs41_start_state = NFS41_START_DRIVER_STARTABLE;
483 
484 NTSTATUS map_readwrite_errors(DWORD status);
485 
486 void print_debug_header(
487     PRX_CONTEXT RxContext)
488 {
489 
490     PIO_STACK_LOCATION IrpSp = RxContext->CurrentIrpSp;
491 
492     if (IrpSp) {
493         DbgP("FileOject %p name %wZ access r=%d,w=%d,d=%d share r=%d,w=%d,d=%d\n",
494             IrpSp->FileObject, &IrpSp->FileObject->FileName,
495             IrpSp->FileObject->ReadAccess, IrpSp->FileObject->WriteAccess,
496             IrpSp->FileObject->DeleteAccess, IrpSp->FileObject->SharedRead,
497             IrpSp->FileObject->SharedWrite, IrpSp->FileObject->SharedDelete);
498         print_file_object(0, IrpSp->FileObject);
499         print_irps_flags(0, RxContext->CurrentIrpSp);
500     } else
501         DbgP("Couldn't print FileObject IrpSp is NULL\n");
502 
503     print_fo_all(1, RxContext);
504     if (RxContext->CurrentIrp)
505         print_irp_flags(0, RxContext->CurrentIrp);
506 }
507 
508 /* convert strings from unicode -> ansi during marshalling to
509  * save space in the upcall buffers and avoid extra copies */
510 INLINE ULONG length_as_utf8(
511     PCUNICODE_STRING str)
512 {
513     ULONG ActualCount = 0;
514     RtlUnicodeToUTF8N(NULL, 0xffff, &ActualCount, str->Buffer, str->Length);
515     return sizeof(str->MaximumLength) + ActualCount + sizeof(UNICODE_NULL);
516 }
517 
518 NTSTATUS marshall_unicode_as_utf8(
519     IN OUT unsigned char **pos,
520     IN PCUNICODE_STRING str)
521 {
522     ANSI_STRING ansi;
523     ULONG ActualCount;
524     NTSTATUS status;
525 
526     if (str->Length == 0) {
527         status = STATUS_SUCCESS;
528         ActualCount = 0;
529         ansi.MaximumLength = 1;
530         goto out_copy;
531     }
532 
533     /* query the number of bytes required for the utf8 encoding */
534     status = RtlUnicodeToUTF8N(NULL, 0xffff,
535         &ActualCount, str->Buffer, str->Length);
536     if (status) {
537         print_error("RtlUnicodeToUTF8N('%wZ') failed with 0x%08X\n",
538             str, status);
539         goto out;
540     }
541 
542     /* convert the string directly into the upcall buffer */
543     ansi.Buffer = (PCHAR)*pos + sizeof(ansi.MaximumLength);
544     ansi.MaximumLength = (USHORT)ActualCount + sizeof(UNICODE_NULL);
545     status = RtlUnicodeToUTF8N(ansi.Buffer, ansi.MaximumLength,
546         &ActualCount, str->Buffer, str->Length);
547     if (status) {
548         print_error("RtlUnicodeToUTF8N(%hu, '%wZ', %hu) failed with 0x%08X\n",
549             ansi.MaximumLength, str, str->Length, status);
550         goto out;
551     }
552 
553 out_copy:
554     RtlCopyMemory(*pos, &ansi.MaximumLength, sizeof(ansi.MaximumLength));
555     *pos += sizeof(ansi.MaximumLength);
556     (*pos)[ActualCount] = '\0';
557     *pos += ansi.MaximumLength;
558 out:
559     return status;
560 }
561 
562 NTSTATUS marshal_nfs41_header(
563     nfs41_updowncall_entry *entry,
564     unsigned char *buf,
565     ULONG buf_len,
566     ULONG *len)
567 {
568     NTSTATUS status = STATUS_SUCCESS;
569     ULONG header_len = 0;
570     unsigned char *tmp = buf;
571 
572     header_len = sizeof(entry->version) + sizeof(entry->xid) +
573         sizeof(entry->opcode) + 2 * sizeof(HANDLE);
574     if (header_len > buf_len) {
575         status = STATUS_INSUFFICIENT_RESOURCES;
576         goto out;
577     }
578     else
579         *len = header_len;
580     RtlCopyMemory(tmp, &entry->version, sizeof(entry->version));
581     tmp += sizeof(entry->version);
582     RtlCopyMemory(tmp, &entry->xid, sizeof(entry->xid));
583     tmp += sizeof(entry->xid);
584     RtlCopyMemory(tmp, &entry->opcode, sizeof(entry->opcode));
585     tmp += sizeof(entry->opcode);
586     RtlCopyMemory(tmp, &entry->session, sizeof(HANDLE));
587     tmp += sizeof(HANDLE);
588     RtlCopyMemory(tmp, &entry->open_state, sizeof(HANDLE));
589     tmp += sizeof(HANDLE);
590 
591 #ifdef DEBUG_MARSHAL_HEADER
592     if (MmIsAddressValid(entry->filename))
593         DbgP("[upcall header] xid=%lld opcode=%s filename=%wZ version=%d "
594             "session=0x%x open_state=0x%x\n", entry->xid,
595             opcode2string(entry->opcode), entry->filename,
596             entry->version, entry->session, entry->open_state);
597     else
598         status = STATUS_INTERNAL_ERROR;
599 #endif
600 out:
601     return status;
602 }
603 
604 const char* secflavorop2name(
605     DWORD sec_flavor)
606 {
607     switch(sec_flavor) {
608     case RPCSEC_AUTH_SYS:      return "AUTH_SYS";
609     case RPCSEC_AUTHGSS_KRB5:  return "AUTHGSS_KRB5";
610     case RPCSEC_AUTHGSS_KRB5I: return "AUTHGSS_KRB5I";
611     case RPCSEC_AUTHGSS_KRB5P: return "AUTHGSS_KRB5P";
612     }
613 
614     return "UNKNOWN FLAVOR";
615 }
616 NTSTATUS marshal_nfs41_mount(
617     nfs41_updowncall_entry *entry,
618     unsigned char *buf,
619     ULONG buf_len,
620     ULONG *len)
621 {
622     NTSTATUS status = STATUS_SUCCESS;
623     ULONG header_len = 0;
624     unsigned char *tmp = buf;
625 
626     status = marshal_nfs41_header(entry, tmp, buf_len, len);
627     if (status) goto out;
628     else tmp += *len;
629 
630     /* 03/25/2011: Kernel crash to nfsd not running but mount upcall cued up */
631     if (!MmIsAddressValid(entry->u.Mount.srv_name) ||
632             !MmIsAddressValid(entry->u.Mount.root)) {
633         status = STATUS_INTERNAL_ERROR;
634         goto out;
635     }
636     header_len = *len + length_as_utf8(entry->u.Mount.srv_name) +
637         length_as_utf8(entry->u.Mount.root) + 3 * sizeof(DWORD);
638     if (header_len > buf_len) {
639         status = STATUS_INSUFFICIENT_RESOURCES;
640         goto out;
641     }
642     status = marshall_unicode_as_utf8(&tmp, entry->u.Mount.srv_name);
643     if (status) goto out;
644     status = marshall_unicode_as_utf8(&tmp, entry->u.Mount.root);
645     if (status) goto out;
646     RtlCopyMemory(tmp, &entry->u.Mount.sec_flavor, sizeof(DWORD));
647     tmp += sizeof(DWORD);
648     RtlCopyMemory(tmp, &entry->u.Mount.rsize, sizeof(DWORD));
649     tmp += sizeof(DWORD);
650     RtlCopyMemory(tmp, &entry->u.Mount.wsize, sizeof(DWORD));
651 
652     *len = header_len;
653 
654 #ifdef DEBUG_MARSHAL_DETAIL
655     DbgP("marshal_nfs41_mount: server name=%wZ mount point=%wZ sec_flavor=%s "
656          "rsize=%d wsize=%d\n", entry->u.Mount.srv_name, entry->u.Mount.root,
657          secflavorop2name(entry->u.Mount.sec_flavor), entry->u.Mount.rsize,
658          entry->u.Mount.wsize);
659 #endif
660 out:
661     return status;
662 }
663 
664 NTSTATUS marshal_nfs41_unmount(
665     nfs41_updowncall_entry *entry,
666     unsigned char *buf,
667     ULONG buf_len,
668     ULONG *len)
669 {
670     return marshal_nfs41_header(entry, buf, buf_len, len);
671 }
672 
673 NTSTATUS marshal_nfs41_open(
674     nfs41_updowncall_entry *entry,
675     unsigned char *buf,
676     ULONG buf_len,
677     ULONG *len)
678 {
679     NTSTATUS status = STATUS_SUCCESS;
680     ULONG header_len = 0;
681     unsigned char *tmp = buf;
682 
683     status = marshal_nfs41_header(entry, tmp, buf_len, len);
684     if (status) goto out;
685     else tmp += *len;
686 
687     header_len = *len + length_as_utf8(entry->filename) +
688         7 * sizeof(ULONG) + 2 * sizeof(HANDLE) +
689         length_as_utf8(&entry->u.Open.symlink);
690     if (header_len > buf_len) {
691         status = STATUS_INSUFFICIENT_RESOURCES;
692         goto out;
693     }
694     status = marshall_unicode_as_utf8(&tmp, entry->filename);
695     if (status) goto out;
696     RtlCopyMemory(tmp, &entry->u.Open.access_mask,
697         sizeof(entry->u.Open.access_mask));
698     tmp += sizeof(entry->u.Open.access_mask);
699     RtlCopyMemory(tmp, &entry->u.Open.access_mode,
700         sizeof(entry->u.Open.access_mode));
701     tmp += sizeof(entry->u.Open.access_mode);
702     RtlCopyMemory(tmp, &entry->u.Open.attrs, sizeof(entry->u.Open.attrs));
703     tmp += sizeof(entry->u.Open.attrs);
704     RtlCopyMemory(tmp, &entry->u.Open.copts, sizeof(entry->u.Open.copts));
705     tmp += sizeof(entry->u.Open.copts);
706     RtlCopyMemory(tmp, &entry->u.Open.disp, sizeof(entry->u.Open.disp));
707     tmp += sizeof(entry->u.Open.disp);
708     RtlCopyMemory(tmp, &entry->u.Open.open_owner_id,
709         sizeof(entry->u.Open.open_owner_id));
710     tmp += sizeof(entry->u.Open.open_owner_id);
711     RtlCopyMemory(tmp, &entry->u.Open.mode, sizeof(DWORD));
712     tmp += sizeof(DWORD);
713     RtlCopyMemory(tmp, &entry->u.Open.srv_open, sizeof(HANDLE));
714     tmp += sizeof(HANDLE);
715     status = marshall_unicode_as_utf8(&tmp, &entry->u.Open.symlink);
716     if (status) goto out;
717 
718     _SEH2_TRY {
719         if (entry->u.Open.EaMdl) {
720             entry->u.Open.EaBuffer =
721                 MmMapLockedPagesSpecifyCache(entry->u.Open.EaMdl,
722 #ifndef __REACTOS__
723                     UserMode, MmNonCached, NULL, TRUE, NormalPagePriority);
724 #else
725                     UserMode, MmCached, NULL, TRUE, NormalPagePriority);
726 #endif
727             if (entry->u.Open.EaBuffer == NULL) {
728                 print_error("MmMapLockedPagesSpecifyCache failed to map pages\n");
729                 status = STATUS_INSUFFICIENT_RESOURCES;
730                 goto out;
731             }
732         }
733     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
734         print_error("Call to MmMapLocked failed due to exception 0x%x\n", _SEH2_GetExceptionCode());
735         status = STATUS_ACCESS_DENIED;
736         goto out;
737     } _SEH2_END;
738     RtlCopyMemory(tmp, &entry->u.Open.EaBuffer, sizeof(HANDLE));
739     *len = header_len;
740 
741 #ifdef DEBUG_MARSHAL_DETAIL
742     DbgP("marshal_nfs41_open: name=%wZ mask=0x%x access=0x%x attrs=0x%x "
743          "opts=0x%x dispo=0x%x open_owner_id=0x%x mode=%o srv_open=%p ea=%p\n",
744          entry->filename, entry->u.Open.access_mask,
745          entry->u.Open.access_mode, entry->u.Open.attrs, entry->u.Open.copts,
746          entry->u.Open.disp, entry->u.Open.open_owner_id, entry->u.Open.mode,
747          entry->u.Open.srv_open, entry->u.Open.EaBuffer);
748 #endif
749 out:
750     return status;
751 }
752 
753 NTSTATUS marshal_nfs41_rw(
754     nfs41_updowncall_entry *entry,
755     unsigned char *buf,
756     ULONG buf_len,
757     ULONG *len)
758 {
759     NTSTATUS status = STATUS_SUCCESS;
760     ULONG header_len = 0;
761     unsigned char *tmp = buf;
762 
763     status = marshal_nfs41_header(entry, tmp, buf_len, len);
764     if (status) goto out;
765     else tmp += *len;
766 
767     header_len = *len + sizeof(entry->buf_len) +
768         sizeof(entry->u.ReadWrite.offset) + sizeof(HANDLE);
769     if (header_len > buf_len) {
770         status = STATUS_INSUFFICIENT_RESOURCES;
771         goto out;
772     }
773 
774     RtlCopyMemory(tmp, &entry->buf_len, sizeof(entry->buf_len));
775     tmp += sizeof(entry->buf_len);
776     RtlCopyMemory(tmp, &entry->u.ReadWrite.offset,
777         sizeof(entry->u.ReadWrite.offset));
778     tmp += sizeof(entry->u.ReadWrite.offset);
779     _SEH2_TRY {
780         entry->u.ReadWrite.MdlAddress->MdlFlags |= MDL_MAPPING_CAN_FAIL;
781         entry->buf =
782             MmMapLockedPagesSpecifyCache(entry->u.ReadWrite.MdlAddress,
783 #ifndef __REACTOS__
784                 UserMode, MmNonCached, NULL, TRUE, NormalPagePriority);
785 #else
786                 UserMode, MmCached, NULL, TRUE, NormalPagePriority);
787 #endif
788         if (entry->buf == NULL) {
789             print_error("MmMapLockedPagesSpecifyCache failed to map pages\n");
790             status = STATUS_INSUFFICIENT_RESOURCES;
791             goto out;
792         }
793     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
794         NTSTATUS code;
795         code = _SEH2_GetExceptionCode();
796         print_error("Call to MmMapLocked failed due to exception 0x%x\n", code);
797         status = STATUS_ACCESS_DENIED;
798         goto out;
799     } _SEH2_END;
800     RtlCopyMemory(tmp, &entry->buf, sizeof(HANDLE));
801     *len = header_len;
802 
803 #ifdef DEBUG_MARSHAL_DETAIL
804     DbgP("marshal_nfs41_rw: len=%lu offset=%llu MdlAddress=%p Userspace=%p\n",
805          entry->buf_len, entry->u.ReadWrite.offset,
806          entry->u.ReadWrite.MdlAddress, entry->buf);
807 #endif
808 out:
809     return status;
810 }
811 
812 NTSTATUS marshal_nfs41_lock(
813     nfs41_updowncall_entry *entry,
814     unsigned char *buf,
815     ULONG buf_len,
816     ULONG *len)
817 {
818     NTSTATUS status = STATUS_SUCCESS;
819     ULONG header_len = 0;
820     unsigned char *tmp = buf;
821 
822     status = marshal_nfs41_header(entry, tmp, buf_len, len);
823     if (status) goto out;
824     else tmp += *len;
825 
826     header_len = *len + 2 * sizeof(LONGLONG) + 2 * sizeof(BOOLEAN);
827     if (header_len > buf_len) {
828         status = STATUS_INSUFFICIENT_RESOURCES;
829         goto out;
830     }
831     RtlCopyMemory(tmp, &entry->u.Lock.offset, sizeof(LONGLONG));
832     tmp += sizeof(LONGLONG);
833     RtlCopyMemory(tmp, &entry->u.Lock.length, sizeof(LONGLONG));
834     tmp += sizeof(LONGLONG);
835     RtlCopyMemory(tmp, &entry->u.Lock.exclusive, sizeof(BOOLEAN));
836     tmp += sizeof(BOOLEAN);
837     RtlCopyMemory(tmp, &entry->u.Lock.blocking, sizeof(BOOLEAN));
838     *len = header_len;
839 
840 #ifdef DEBUG_MARSHAL_DETAIL
841     DbgP("marshal_nfs41_lock: offset=%llx length=%llx exclusive=%u "
842          "blocking=%u\n", entry->u.Lock.offset, entry->u.Lock.length,
843          entry->u.Lock.exclusive, entry->u.Lock.blocking);
844 #endif
845 out:
846     return status;
847 }
848 
849 NTSTATUS marshal_nfs41_unlock(
850     nfs41_updowncall_entry *entry,
851     unsigned char *buf,
852     ULONG buf_len,
853     ULONG *len)
854 {
855     NTSTATUS status = STATUS_SUCCESS;
856     ULONG header_len = 0;
857     unsigned char *tmp = buf;
858     PLOWIO_LOCK_LIST lock;
859 
860     status = marshal_nfs41_header(entry, tmp, buf_len, len);
861     if (status) goto out;
862     else tmp += *len;
863 
864     header_len = *len + sizeof(ULONG) +
865         entry->u.Unlock.count * 2 * sizeof(LONGLONG);
866     if (header_len > buf_len) {
867         status = STATUS_INSUFFICIENT_RESOURCES;
868         goto out;
869     }
870     RtlCopyMemory(tmp, &entry->u.Unlock.count, sizeof(ULONG));
871     tmp += sizeof(ULONG);
872 
873     lock = &entry->u.Unlock.locks;
874     while (lock) {
875         RtlCopyMemory(tmp, &lock->ByteOffset, sizeof(LONGLONG));
876         tmp += sizeof(LONGLONG);
877         RtlCopyMemory(tmp, &lock->Length, sizeof(LONGLONG));
878         tmp += sizeof(LONGLONG);
879         lock = lock->Next;
880     }
881     *len = header_len;
882 
883 #ifdef DEBUG_MARSHAL_DETAIL
884     DbgP("marshal_nfs41_unlock: count=%u\n", entry->u.Unlock.count);
885 #endif
886 out:
887     return status;
888 }
889 
890 NTSTATUS marshal_nfs41_close(
891     nfs41_updowncall_entry *entry,
892     unsigned char *buf,
893     ULONG buf_len,
894     ULONG *len)
895 {
896     NTSTATUS status = STATUS_SUCCESS;
897     ULONG header_len = 0;
898     unsigned char *tmp = buf;
899 
900     status = marshal_nfs41_header(entry, tmp, buf_len, len);
901     if (status) goto out;
902     else tmp += *len;
903 
904     header_len = *len + sizeof(BOOLEAN) + sizeof(HANDLE);
905     if (entry->u.Close.remove)
906         header_len += length_as_utf8(entry->filename) +
907             sizeof(BOOLEAN);
908 
909     if (header_len > buf_len) {
910         status = STATUS_INSUFFICIENT_RESOURCES;
911         goto out;
912     }
913     RtlCopyMemory(tmp, &entry->u.Close.remove, sizeof(BOOLEAN));
914     tmp += sizeof(BOOLEAN);
915     RtlCopyMemory(tmp, &entry->u.Close.srv_open, sizeof(HANDLE));
916     if (entry->u.Close.remove) {
917         tmp += sizeof(HANDLE);
918         status = marshall_unicode_as_utf8(&tmp, entry->filename);
919         if (status) goto out;
920         RtlCopyMemory(tmp, &entry->u.Close.renamed, sizeof(BOOLEAN));
921     }
922     *len = header_len;
923 
924 #ifdef DEBUG_MARSHAL_DETAIL
925     DbgP("marshal_nfs41_close: name=%wZ remove=%d srv_open=%p renamed=%d\n",
926         entry->filename->Length?entry->filename:&SLASH,
927         entry->u.Close.remove, entry->u.Close.srv_open, entry->u.Close.renamed);
928 #endif
929 out:
930     return status;
931 }
932 
933 NTSTATUS marshal_nfs41_dirquery(
934     nfs41_updowncall_entry *entry,
935     unsigned char *buf,
936     ULONG buf_len,
937     ULONG *len)
938 {
939     NTSTATUS status = STATUS_SUCCESS;
940     ULONG header_len = 0;
941     unsigned char *tmp = buf;
942 
943     status = marshal_nfs41_header(entry, tmp, buf_len, len);
944     if (status) goto out;
945     else tmp += *len;
946 
947     header_len = *len + 2 * sizeof(ULONG) + sizeof(HANDLE) +
948         length_as_utf8(entry->u.QueryFile.filter) + 3 * sizeof(BOOLEAN);
949     if (header_len > buf_len) {
950         status = STATUS_INSUFFICIENT_RESOURCES;
951         goto out;
952     }
953 
954     RtlCopyMemory(tmp, &entry->u.QueryFile.InfoClass, sizeof(ULONG));
955     tmp += sizeof(ULONG);
956     RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
957     tmp += sizeof(ULONG);
958     status = marshall_unicode_as_utf8(&tmp, entry->u.QueryFile.filter);
959     if (status) goto out;
960     RtlCopyMemory(tmp, &entry->u.QueryFile.initial_query, sizeof(BOOLEAN));
961     tmp += sizeof(BOOLEAN);
962     RtlCopyMemory(tmp, &entry->u.QueryFile.restart_scan, sizeof(BOOLEAN));
963     tmp += sizeof(BOOLEAN);
964     RtlCopyMemory(tmp, &entry->u.QueryFile.return_single, sizeof(BOOLEAN));
965     tmp += sizeof(BOOLEAN);
966     _SEH2_TRY {
967         entry->u.QueryFile.mdl_buf =
968             MmMapLockedPagesSpecifyCache(entry->u.QueryFile.mdl,
969 #ifndef __REACTOS__
970                 UserMode, MmNonCached, NULL, TRUE, NormalPagePriority);
971 #else
972                 UserMode, MmCached, NULL, TRUE, NormalPagePriority);
973 #endif
974         if (entry->u.QueryFile.mdl_buf == NULL) {
975             print_error("MmMapLockedPagesSpecifyCache failed to map pages\n");
976             status = STATUS_INSUFFICIENT_RESOURCES;
977             goto out;
978         }
979     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
980         NTSTATUS code;
981         code = _SEH2_GetExceptionCode();
982         print_error("Call to MmMapLocked failed due to exception 0x%x\n", code);
983         status = STATUS_ACCESS_DENIED;
984         goto out;
985     } _SEH2_END;
986     RtlCopyMemory(tmp, &entry->u.QueryFile.mdl_buf, sizeof(HANDLE));
987     *len = header_len;
988 
989 #ifdef DEBUG_MARSHAL_DETAIL
990     DbgP("marshal_nfs41_dirquery: filter='%wZ'class=%d len=%d "
991          "1st\\restart\\single=%d\\%d\\%d\n", entry->u.QueryFile.filter,
992          entry->u.QueryFile.InfoClass, entry->buf_len,
993          entry->u.QueryFile.initial_query, entry->u.QueryFile.restart_scan,
994          entry->u.QueryFile.return_single);
995 #endif
996 out:
997     return status;
998 }
999 
1000 NTSTATUS marshal_nfs41_filequery(
1001     nfs41_updowncall_entry *entry,
1002     unsigned char *buf,
1003     ULONG buf_len,
1004     ULONG *len)
1005 {
1006     NTSTATUS status = STATUS_SUCCESS;
1007     ULONG header_len = 0;
1008     unsigned char *tmp = buf;
1009 
1010     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1011     if (status) goto out;
1012     else tmp += *len;
1013 
1014     header_len = *len + 2 * sizeof(ULONG);
1015     if (header_len > buf_len) {
1016         status = STATUS_INSUFFICIENT_RESOURCES;
1017         goto out;
1018     }
1019     RtlCopyMemory(tmp, &entry->u.QueryFile.InfoClass, sizeof(ULONG));
1020     tmp += sizeof(ULONG);
1021     RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
1022     tmp += sizeof(ULONG);
1023     RtlCopyMemory(tmp, &entry->session, sizeof(HANDLE));
1024     tmp += sizeof(HANDLE);
1025     RtlCopyMemory(tmp, &entry->open_state, sizeof(HANDLE));
1026     *len = header_len;
1027 
1028 #ifdef DEBUG_MARSHAL_DETAIL
1029     DbgP("marshal_nfs41_filequery: class=%d\n", entry->u.QueryFile.InfoClass);
1030 #endif
1031 out:
1032     return status;
1033 }
1034 
1035 NTSTATUS marshal_nfs41_fileset(
1036     nfs41_updowncall_entry *entry,
1037     unsigned char *buf,
1038     ULONG buf_len,
1039     ULONG *len)
1040 {
1041     NTSTATUS status = STATUS_SUCCESS;
1042     ULONG header_len = 0;
1043     unsigned char *tmp = buf;
1044 
1045     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1046     if (status) goto out;
1047     else tmp += *len;
1048 
1049     header_len = *len + length_as_utf8(entry->filename) +
1050         2 * sizeof(ULONG) + entry->buf_len;
1051     if (header_len > buf_len) {
1052         status = STATUS_INSUFFICIENT_RESOURCES;
1053         goto out;
1054     }
1055     status = marshall_unicode_as_utf8(&tmp, entry->filename);
1056     if (status) goto out;
1057     RtlCopyMemory(tmp, &entry->u.SetFile.InfoClass, sizeof(ULONG));
1058     tmp += sizeof(ULONG);
1059     RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
1060     tmp += sizeof(ULONG);
1061     RtlCopyMemory(tmp, entry->buf, entry->buf_len);
1062     *len = header_len;
1063 
1064 #ifdef DEBUG_MARSHAL_DETAIL
1065     DbgP("marshal_nfs41_fileset: filename='%wZ' class=%d\n",
1066         entry->filename, entry->u.SetFile.InfoClass);
1067 #endif
1068 out:
1069     return status;
1070 }
1071 
1072 NTSTATUS marshal_nfs41_easet(
1073     nfs41_updowncall_entry *entry,
1074     unsigned char *buf,
1075     ULONG buf_len,
1076     ULONG *len)
1077 {
1078     NTSTATUS status = STATUS_SUCCESS;
1079     ULONG header_len = 0;
1080     unsigned char *tmp = buf;
1081 
1082     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1083     if (status) goto out;
1084     else tmp += *len;
1085 
1086     header_len = *len + length_as_utf8(entry->filename) +
1087         sizeof(ULONG) + entry->buf_len  + sizeof(DWORD);
1088     if (header_len > buf_len) {
1089         status = STATUS_INSUFFICIENT_RESOURCES;
1090         goto out;
1091     }
1092 
1093     status = marshall_unicode_as_utf8(&tmp, entry->filename);
1094     if (status) goto out;
1095     RtlCopyMemory(tmp, &entry->u.SetEa.mode, sizeof(DWORD));
1096     tmp += sizeof(DWORD);
1097     RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
1098     tmp += sizeof(ULONG);
1099     RtlCopyMemory(tmp, entry->buf, entry->buf_len);
1100     *len = header_len;
1101 
1102 #ifdef DEBUG_MARSHAL_DETAIL
1103     DbgP("marshal_nfs41_easet: filename=%wZ, buflen=%d mode=0x%x\n",
1104         entry->filename, entry->buf_len, entry->u.SetEa.mode);
1105 #endif
1106 out:
1107     return status;
1108 }
1109 
1110 NTSTATUS marshal_nfs41_eaget(
1111     nfs41_updowncall_entry *entry,
1112     unsigned char *buf,
1113     ULONG buf_len,
1114     ULONG *len)
1115 {
1116     NTSTATUS status = STATUS_SUCCESS;
1117     ULONG header_len = 0;
1118     unsigned char *tmp = buf;
1119 
1120     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1121     if (status) goto out;
1122     else tmp += *len;
1123 
1124     header_len = *len + length_as_utf8(entry->filename) +
1125         3 * sizeof(ULONG) + entry->u.QueryEa.EaListLength + 2 * sizeof(BOOLEAN);
1126 
1127     if (header_len > buf_len) {
1128         status = STATUS_INSUFFICIENT_RESOURCES;
1129         goto out;
1130     }
1131 
1132     status = marshall_unicode_as_utf8(&tmp, entry->filename);
1133     if (status) goto out;
1134     RtlCopyMemory(tmp, &entry->u.QueryEa.EaIndex, sizeof(ULONG));
1135     tmp += sizeof(ULONG);
1136     RtlCopyMemory(tmp, &entry->u.QueryEa.RestartScan, sizeof(BOOLEAN));
1137     tmp += sizeof(BOOLEAN);
1138     RtlCopyMemory(tmp, &entry->u.QueryEa.ReturnSingleEntry, sizeof(BOOLEAN));
1139     tmp += sizeof(BOOLEAN);
1140     RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
1141     tmp += sizeof(ULONG);
1142     RtlCopyMemory(tmp, &entry->u.QueryEa.EaListLength, sizeof(ULONG));
1143     tmp += sizeof(ULONG);
1144     if (entry->u.QueryEa.EaList && entry->u.QueryEa.EaListLength)
1145         RtlCopyMemory(tmp, entry->u.QueryEa.EaList,
1146             entry->u.QueryEa.EaListLength);
1147     *len = header_len;
1148 
1149 #ifdef DEBUG_MARSHAL_DETAIL
1150     DbgP("marshal_nfs41_eaget: filename=%wZ, index=%d list_len=%d "
1151         "rescan=%d single=%d\n", entry->filename,
1152         entry->u.QueryEa.EaIndex, entry->u.QueryEa.EaListLength,
1153         entry->u.QueryEa.RestartScan, entry->u.QueryEa.ReturnSingleEntry);
1154 #endif
1155 out:
1156     return status;
1157 }
1158 
1159 NTSTATUS marshal_nfs41_symlink(
1160     nfs41_updowncall_entry *entry,
1161     unsigned char *buf,
1162     ULONG buf_len,
1163     ULONG *len)
1164 {
1165     NTSTATUS status = STATUS_SUCCESS;
1166     ULONG header_len = 0;
1167     unsigned char *tmp = buf;
1168 
1169     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1170     if (status) goto out;
1171     else tmp += *len;
1172 
1173     header_len = *len + sizeof(BOOLEAN) + length_as_utf8(entry->filename);
1174     if (entry->u.Symlink.set)
1175         header_len += length_as_utf8(entry->u.Symlink.target);
1176     if (header_len > buf_len) {
1177         status = STATUS_INSUFFICIENT_RESOURCES;
1178         goto out;
1179     }
1180 
1181     status = marshall_unicode_as_utf8(&tmp, entry->filename);
1182     if (status) goto out;
1183     RtlCopyMemory(tmp, &entry->u.Symlink.set, sizeof(BOOLEAN));
1184     tmp += sizeof(BOOLEAN);
1185     if (entry->u.Symlink.set) {
1186         status = marshall_unicode_as_utf8(&tmp, entry->u.Symlink.target);
1187         if (status) goto out;
1188     }
1189     *len = header_len;
1190 
1191 #ifdef DEBUG_MARSHAL_DETAIL
1192     DbgP("marshal_nfs41_symlink: name %wZ symlink target %wZ\n",
1193          entry->filename,
1194          entry->u.Symlink.set?entry->u.Symlink.target : NULL);
1195 #endif
1196 out:
1197     return status;
1198 }
1199 
1200 NTSTATUS marshal_nfs41_volume(
1201     nfs41_updowncall_entry *entry,
1202     unsigned char *buf,
1203     ULONG buf_len,
1204     ULONG *len)
1205 {
1206     NTSTATUS status = STATUS_SUCCESS;
1207     ULONG header_len = 0;
1208     unsigned char *tmp = buf;
1209 
1210     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1211     if (status) goto out;
1212     else tmp += *len;
1213 
1214     header_len = *len + sizeof(FS_INFORMATION_CLASS);
1215     if (header_len > buf_len) {
1216         status = STATUS_INSUFFICIENT_RESOURCES;
1217         goto out;
1218     }
1219 
1220     RtlCopyMemory(tmp, &entry->u.Volume.query, sizeof(FS_INFORMATION_CLASS));
1221     *len = header_len;
1222 
1223 #ifdef DEBUG_MARSHAL_DETAIL
1224     DbgP("marshal_nfs41_volume: class=%d\n", entry->u.Volume.query);
1225 #endif
1226 out:
1227     return status;
1228 }
1229 
1230 NTSTATUS marshal_nfs41_getacl(
1231     nfs41_updowncall_entry *entry,
1232     unsigned char *buf,
1233     ULONG buf_len,
1234     ULONG *len)
1235 {
1236     NTSTATUS status = STATUS_SUCCESS;
1237     ULONG header_len = 0;
1238     unsigned char *tmp = buf;
1239 
1240     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1241     if (status) goto out;
1242     else tmp += *len;
1243 
1244     header_len = *len + sizeof(SECURITY_INFORMATION);
1245     if (header_len > buf_len) {
1246         status = STATUS_INSUFFICIENT_RESOURCES;
1247         goto out;
1248     }
1249 
1250     RtlCopyMemory(tmp, &entry->u.Acl.query, sizeof(SECURITY_INFORMATION));
1251     *len = header_len;
1252 
1253 #ifdef DEBUG_MARSHAL_DETAIL
1254     DbgP("marshal_nfs41_getacl: class=0x%x\n", entry->u.Acl.query);
1255 #endif
1256 out:
1257     return status;
1258 }
1259 
1260 NTSTATUS marshal_nfs41_setacl(
1261     nfs41_updowncall_entry *entry,
1262     unsigned char *buf,
1263     ULONG buf_len,
1264     ULONG *len)
1265 {
1266     NTSTATUS status = STATUS_SUCCESS;
1267     ULONG header_len = 0;
1268     unsigned char *tmp = buf;
1269 
1270     status = marshal_nfs41_header(entry, tmp, buf_len, len);
1271     if (status) goto out;
1272     else tmp += *len;
1273 
1274     header_len = *len + sizeof(SECURITY_INFORMATION) +
1275         sizeof(ULONG) + entry->buf_len;
1276     if (header_len > buf_len) {
1277         status = STATUS_INSUFFICIENT_RESOURCES;
1278         goto out;
1279     }
1280 
1281     RtlCopyMemory(tmp, &entry->u.Acl.query, sizeof(SECURITY_INFORMATION));
1282     tmp += sizeof(SECURITY_INFORMATION);
1283     RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
1284     tmp += sizeof(ULONG);
1285     RtlCopyMemory(tmp, entry->buf, entry->buf_len);
1286     *len = header_len;
1287 
1288 #ifdef DEBUG_MARSHAL_DETAIL
1289     DbgP("marshal_nfs41_setacl: class=0x%x sec_desc_len=%lu\n",
1290          entry->u.Acl.query, entry->buf_len);
1291 #endif
1292 out:
1293     return status;
1294 }
1295 
1296 NTSTATUS marshal_nfs41_shutdown(
1297     nfs41_updowncall_entry *entry,
1298     unsigned char *buf,
1299     ULONG buf_len,
1300     ULONG *len)
1301 {
1302     return marshal_nfs41_header(entry, buf, buf_len, len);
1303 }
1304 
1305 void nfs41_invalidate_cache (
1306     IN PRX_CONTEXT RxContext)
1307 {
1308     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
1309     unsigned char *buf = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
1310     ULONG flag = DISABLE_CACHING;
1311     PMRX_SRV_OPEN srv_open;
1312 
1313     RtlCopyMemory(&srv_open, buf, sizeof(HANDLE));
1314 #ifdef DEBUG_INVALIDATE_CACHE
1315     DbgP("nfs41_invalidate_cache: received srv_open=%p %wZ\n",
1316         srv_open, srv_open->pAlreadyPrefixedName);
1317 #endif
1318     if (MmIsAddressValid(srv_open))
1319         RxIndicateChangeOfBufferingStateForSrvOpen(
1320             srv_open->pFcb->pNetRoot->pSrvCall, srv_open,
1321             srv_open->Key, ULongToPtr(flag));
1322 }
1323 
1324 NTSTATUS handle_upcall(
1325     IN PRX_CONTEXT RxContext,
1326     IN nfs41_updowncall_entry *entry,
1327     OUT ULONG *len)
1328 {
1329     NTSTATUS status = STATUS_SUCCESS;
1330     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
1331     ULONG cbOut = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
1332     unsigned char *pbOut = LowIoContext->ParamsFor.IoCtl.pOutputBuffer;
1333 
1334     status = SeImpersonateClientEx(entry->psec_ctx, NULL);
1335     if (status != STATUS_SUCCESS) {
1336         print_error("SeImpersonateClientEx failed %x\n", status);
1337         goto out;
1338     }
1339 
1340     switch(entry->opcode) {
1341     case NFS41_SHUTDOWN:
1342         status = marshal_nfs41_shutdown(entry, pbOut, cbOut, len);
1343         KeSetEvent(&entry->cond, 0, FALSE);
1344         break;
1345     case NFS41_MOUNT:
1346         status = marshal_nfs41_mount(entry, pbOut, cbOut, len);
1347         break;
1348     case NFS41_UNMOUNT:
1349         status = marshal_nfs41_unmount(entry, pbOut, cbOut, len);
1350         break;
1351     case NFS41_OPEN:
1352         status = marshal_nfs41_open(entry, pbOut, cbOut, len);
1353         break;
1354     case NFS41_READ:
1355         status = marshal_nfs41_rw(entry, pbOut, cbOut, len);
1356         break;
1357     case NFS41_WRITE:
1358         status = marshal_nfs41_rw(entry, pbOut, cbOut, len);
1359         break;
1360     case NFS41_LOCK:
1361         status = marshal_nfs41_lock(entry, pbOut, cbOut, len);
1362         break;
1363     case NFS41_UNLOCK:
1364         status = marshal_nfs41_unlock(entry, pbOut, cbOut, len);
1365         break;
1366     case NFS41_CLOSE:
1367         status = marshal_nfs41_close(entry, pbOut, cbOut, len);
1368         break;
1369     case NFS41_DIR_QUERY:
1370         status = marshal_nfs41_dirquery(entry, pbOut, cbOut, len);
1371         break;
1372     case NFS41_FILE_QUERY:
1373         status = marshal_nfs41_filequery(entry, pbOut, cbOut, len);
1374         break;
1375     case NFS41_FILE_SET:
1376         status = marshal_nfs41_fileset(entry, pbOut, cbOut, len);
1377         break;
1378     case NFS41_EA_SET:
1379         status = marshal_nfs41_easet(entry, pbOut, cbOut, len);
1380         break;
1381     case NFS41_EA_GET:
1382         status = marshal_nfs41_eaget(entry, pbOut, cbOut, len);
1383         break;
1384     case NFS41_SYMLINK:
1385         status = marshal_nfs41_symlink(entry, pbOut, cbOut, len);
1386         break;
1387     case NFS41_VOLUME_QUERY:
1388         status = marshal_nfs41_volume(entry, pbOut, cbOut, len);
1389         break;
1390     case NFS41_ACL_QUERY:
1391         status = marshal_nfs41_getacl(entry, pbOut, cbOut, len);
1392         break;
1393     case NFS41_ACL_SET:
1394         status = marshal_nfs41_setacl(entry, pbOut, cbOut, len);
1395         break;
1396     default:
1397         status = STATUS_INVALID_PARAMETER;
1398         print_error("Unknown nfs41 ops %d\n", entry->opcode);
1399     }
1400 
1401     if (status == STATUS_SUCCESS)
1402         print_hexbuf(0, (unsigned char *)"upcall buffer", pbOut, *len);
1403 
1404 out:
1405     return status;
1406 }
1407 
1408 NTSTATUS nfs41_UpcallCreate(
1409     IN DWORD opcode,
1410     IN PSECURITY_CLIENT_CONTEXT clnt_sec_ctx,
1411     IN HANDLE session,
1412     IN HANDLE open_state,
1413     IN DWORD version,
1414     IN PUNICODE_STRING filename,
1415     OUT nfs41_updowncall_entry **entry_out)
1416 {
1417     NTSTATUS status = STATUS_SUCCESS;
1418     nfs41_updowncall_entry *entry;
1419     SECURITY_SUBJECT_CONTEXT sec_ctx;
1420     SECURITY_QUALITY_OF_SERVICE sec_qos;
1421 
1422     entry = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_entry),
1423                 NFS41_MM_POOLTAG_UP);
1424     if (entry == NULL) {
1425         status = STATUS_INSUFFICIENT_RESOURCES;
1426         goto out;
1427     }
1428 
1429     RtlZeroMemory(entry, sizeof(nfs41_updowncall_entry));
1430     entry->xid = InterlockedIncrement64(&xid);
1431     entry->opcode = opcode;
1432     entry->state = NFS41_WAITING_FOR_UPCALL;
1433     entry->session = session;
1434     entry->open_state = open_state;
1435     entry->version = version;
1436     if (filename && filename->Length) entry->filename = filename;
1437     else if (filename && !filename->Length) entry->filename = (PUNICODE_STRING)&SLASH;
1438     else entry->filename = (PUNICODE_STRING)&EMPTY_STRING;
1439     /*XXX KeInitializeEvent will bugcheck under verifier if allocated
1440      * from PagedPool? */
1441     KeInitializeEvent(&entry->cond, SynchronizationEvent, FALSE);
1442     ExInitializeFastMutex(&entry->lock);
1443 
1444     if (clnt_sec_ctx == NULL) {
1445         SeCaptureSubjectContext(&sec_ctx);
1446         sec_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
1447         sec_qos.ImpersonationLevel = SecurityImpersonation;
1448         sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
1449         sec_qos.EffectiveOnly = 0;
1450         status = SeCreateClientSecurityFromSubjectContext(&sec_ctx, &sec_qos,
1451                     1, &entry->sec_ctx);
1452         if (status != STATUS_SUCCESS) {
1453             print_error("nfs41_UpcallCreate: "
1454                 "SeCreateClientSecurityFromSubjectContext failed with %x\n",
1455                 status);
1456             RxFreePool(entry);
1457         } else
1458             entry->psec_ctx = &entry->sec_ctx;
1459         SeReleaseSubjectContext(&sec_ctx);
1460     } else
1461         entry->psec_ctx = clnt_sec_ctx;
1462 
1463     *entry_out = entry;
1464 out:
1465     return status;
1466 }
1467 
1468 NTSTATUS nfs41_UpcallWaitForReply(
1469     IN nfs41_updowncall_entry *entry,
1470     IN DWORD secs)
1471 {
1472     NTSTATUS status = STATUS_SUCCESS;
1473 
1474     nfs41_AddEntry(upcallLock, upcall, entry);
1475     KeSetEvent(&upcallEvent, 0, FALSE);
1476     if (!entry->async_op) {
1477         LARGE_INTEGER timeout;
1478         timeout.QuadPart = RELATIVE(SECONDS(secs));
1479         /* 02/03/2011 AGLO: it is not clear what the "right" waiting design
1480          * should be. Having non-interruptable waiting seems to be the right
1481          * approach. However, when things go wrong, the only wait to proceed
1482          * is a reboot (since "waits" are not interruptable we can't stop a
1483          * hung task. Having interruptable wait causes issues with security
1484          * context. For now, I'm making CLOSE non-interruptable but keeping
1485          * the rest interruptable so that we don't have to reboot all the time
1486          */
1487         /* 02/15/2011 cbodley: added NFS41_UNLOCK for the same reason. locking
1488          * tests were triggering an interrupted unlock, which led to a bugcheck
1489          * in CloseSrvOpen() */
1490 #define MAKE_WAITONCLOSE_NONITERRUPTABLE
1491 #ifdef MAKE_WAITONCLOSE_NONITERRUPTABLE
1492         if (entry->opcode == NFS41_CLOSE || entry->opcode == NFS41_UNLOCK)
1493             status = KeWaitForSingleObject(&entry->cond, Executive,
1494                         KernelMode, FALSE, &timeout);
1495         else {
1496             status = KeWaitForSingleObject(&entry->cond, Executive,
1497                         UserMode, TRUE, &timeout);
1498         }
1499         if (status != STATUS_SUCCESS) {
1500             print_wait_status(1, "[downcall]", status,
1501                 opcode2string(entry->opcode), entry, entry->xid);
1502             if (status == STATUS_TIMEOUT)
1503                 status = STATUS_NETWORK_UNREACHABLE;
1504         }
1505 #else
1506 
1507         status = KeWaitForSingleObject(&entry->cond, Executive, KernelMode, FALSE, NULL);
1508 #endif
1509         print_wait_status(0, "[downcall]", status, opcode2string(entry->opcode),
1510             entry, entry->xid);
1511     } else
1512         goto out;
1513 
1514     switch(status) {
1515     case STATUS_SUCCESS: break;
1516     case STATUS_USER_APC:
1517     case STATUS_ALERTED:
1518     default:
1519         ExAcquireFastMutex(&entry->lock);
1520         if (entry->state == NFS41_DONE_PROCESSING) {
1521             ExReleaseFastMutex(&entry->lock);
1522             break;
1523         }
1524         DbgP("[upcall] abandoning %s entry=%p xid=%lld\n",
1525             opcode2string(entry->opcode), entry, entry->xid);
1526         entry->state = NFS41_NOT_WAITING;
1527         ExReleaseFastMutex(&entry->lock);
1528         goto out;
1529     }
1530     nfs41_RemoveEntry(downcallLock, entry);
1531 out:
1532     return status;
1533 }
1534 
1535 NTSTATUS nfs41_upcall(
1536     IN PRX_CONTEXT RxContext)
1537 {
1538     NTSTATUS status = STATUS_SUCCESS;
1539     nfs41_updowncall_entry *entry = NULL;
1540     ULONG len = 0;
1541     PLIST_ENTRY pEntry;
1542 
1543 process_upcall:
1544     nfs41_RemoveFirst(upcallLock, upcall, pEntry);
1545     if (pEntry) {
1546         entry = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
1547                     nfs41_updowncall_entry, next);
1548         ExAcquireFastMutex(&entry->lock);
1549         nfs41_AddEntry(downcallLock, downcall, entry);
1550         status = handle_upcall(RxContext, entry, &len);
1551         if (status == STATUS_SUCCESS &&
1552                 entry->state == NFS41_WAITING_FOR_UPCALL)
1553             entry->state = NFS41_WAITING_FOR_DOWNCALL;
1554         ExReleaseFastMutex(&entry->lock);
1555         if (status) {
1556             entry->status = status;
1557             KeSetEvent(&entry->cond, 0, FALSE);
1558             RxContext->InformationToReturn = 0;
1559         } else
1560             RxContext->InformationToReturn = len;
1561     }
1562     else {
1563         status = KeWaitForSingleObject(&upcallEvent, Executive, UserMode, TRUE,
1564             (PLARGE_INTEGER) NULL);
1565         print_wait_status(0, "[upcall]", status, NULL, NULL, 0);
1566         switch (status) {
1567         case STATUS_SUCCESS: goto process_upcall;
1568         case STATUS_USER_APC:
1569         case STATUS_ALERTED:
1570         default: goto out;
1571         }
1572     }
1573 out:
1574     return status;
1575 }
1576 
1577 void unmarshal_nfs41_header(
1578     nfs41_updowncall_entry *tmp,
1579     unsigned char **buf)
1580 {
1581     RtlZeroMemory(tmp, sizeof(nfs41_updowncall_entry));
1582 
1583     RtlCopyMemory(&tmp->xid, *buf, sizeof(tmp->xid));
1584     *buf += sizeof(tmp->xid);
1585     RtlCopyMemory(&tmp->opcode, *buf, sizeof(tmp->opcode));
1586     *buf += sizeof(tmp->opcode);
1587     RtlCopyMemory(&tmp->status, *buf, sizeof(tmp->status));
1588     *buf += sizeof(tmp->status);
1589     RtlCopyMemory(&tmp->errno, *buf, sizeof(tmp->errno));
1590     *buf += sizeof(tmp->errno);
1591 #ifdef DEBUG_MARSHAL_HEADER
1592     DbgP("[downcall header] xid=%lld opcode=%s status=%d errno=%d\n", tmp->xid,
1593         opcode2string(tmp->opcode), tmp->status, tmp->errno);
1594 #endif
1595 }
1596 
1597 void unmarshal_nfs41_mount(
1598     nfs41_updowncall_entry *cur,
1599     unsigned char **buf)
1600 {
1601     RtlCopyMemory(&cur->session, *buf, sizeof(HANDLE));
1602     *buf += sizeof(HANDLE);
1603     RtlCopyMemory(&cur->version, *buf, sizeof(DWORD));
1604     *buf += sizeof(DWORD);
1605     RtlCopyMemory(&cur->u.Mount.lease_time, *buf, sizeof(DWORD));
1606     *buf += sizeof(DWORD);
1607     RtlCopyMemory(cur->u.Mount.FsAttrs, *buf, sizeof(FILE_FS_ATTRIBUTE_INFORMATION));
1608 #ifdef DEBUG_MARSHAL_DETAIL
1609     DbgP("unmarshal_nfs41_mount: session pointer 0x%x version %d lease_time "
1610          "%d\n", cur->session, cur->version, cur->u.Mount.lease_time);
1611 #endif
1612 }
1613 
1614 VOID unmarshal_nfs41_setattr(
1615     nfs41_updowncall_entry *cur,
1616     PULONGLONG dest_buf,
1617     unsigned char **buf)
1618 {
1619     RtlCopyMemory(dest_buf, *buf, sizeof(ULONGLONG));
1620 #ifdef DEBUG_MARSHAL_DETAIL
1621     DbgP("unmarshal_nfs41_setattr: returned ChangeTime %llu\n", *dest_buf);
1622 #endif
1623 }
1624 
1625 NTSTATUS unmarshal_nfs41_rw(
1626     nfs41_updowncall_entry *cur,
1627     unsigned char **buf)
1628 {
1629     NTSTATUS status = STATUS_SUCCESS;
1630 
1631     RtlCopyMemory(&cur->buf_len, *buf, sizeof(cur->buf_len));
1632     *buf += sizeof(cur->buf_len);
1633     RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(ULONGLONG));
1634 #ifdef DEBUG_MARSHAL_DETAIL
1635     DbgP("unmarshal_nfs41_rw: returned len %lu ChangeTime %llu\n",
1636         cur->buf_len, cur->ChangeTime);
1637 #endif
1638 #if 1
1639     /* 08/27/2010: it looks like we really don't need to call
1640         * MmUnmapLockedPages() eventhough we called
1641         * MmMapLockedPagesSpecifyCache() as the MDL passed to us
1642         * is already locked.
1643         */
1644     _SEH2_TRY {
1645         MmUnmapLockedPages(cur->buf, cur->u.ReadWrite.MdlAddress);
1646     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1647         NTSTATUS code;
1648         code = _SEH2_GetExceptionCode();
1649         print_error("Call to MmUnmapLockedPages failed due to"
1650             " exception 0x%0x\n", code);
1651         status = STATUS_ACCESS_DENIED;
1652     } _SEH2_END;
1653 #endif
1654     return status;
1655 }
1656 
1657 NTSTATUS unmarshal_nfs41_open(
1658     nfs41_updowncall_entry *cur,
1659     unsigned char **buf)
1660 {
1661     NTSTATUS status = STATUS_SUCCESS;
1662 
1663     _SEH2_TRY {
1664         if (cur->u.Open.EaBuffer)
1665             MmUnmapLockedPages(cur->u.Open.EaBuffer, cur->u.Open.EaMdl);
1666     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1667         print_error("MmUnmapLockedPages thrown exception=0x%0x\n", _SEH2_GetExceptionCode());
1668         status = cur->status = STATUS_ACCESS_DENIED;
1669         goto out;
1670     } _SEH2_END;
1671 
1672     RtlCopyMemory(&cur->u.Open.binfo, *buf, sizeof(FILE_BASIC_INFORMATION));
1673     *buf += sizeof(FILE_BASIC_INFORMATION);
1674     RtlCopyMemory(&cur->u.Open.sinfo, *buf, sizeof(FILE_STANDARD_INFORMATION));
1675     *buf += sizeof(FILE_STANDARD_INFORMATION);
1676     RtlCopyMemory(&cur->open_state, *buf, sizeof(HANDLE));
1677     *buf += sizeof(HANDLE);
1678     RtlCopyMemory(&cur->u.Open.mode, *buf, sizeof(DWORD));
1679     *buf += sizeof(DWORD);
1680     RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(ULONGLONG));
1681     *buf += sizeof(ULONGLONG);
1682     RtlCopyMemory(&cur->u.Open.deleg_type, *buf, sizeof(DWORD));
1683     *buf += sizeof(DWORD);
1684     if (cur->errno == ERROR_REPARSE) {
1685         RtlCopyMemory(&cur->u.Open.symlink_embedded, *buf, sizeof(BOOLEAN));
1686         *buf += sizeof(BOOLEAN);
1687         RtlCopyMemory(&cur->u.Open.symlink.MaximumLength, *buf,
1688             sizeof(USHORT));
1689         *buf += sizeof(USHORT);
1690         cur->u.Open.symlink.Length = cur->u.Open.symlink.MaximumLength -
1691             sizeof(WCHAR);
1692         cur->u.Open.symlink.Buffer = RxAllocatePoolWithTag(NonPagedPool,
1693             cur->u.Open.symlink.MaximumLength, NFS41_MM_POOLTAG);
1694         if (cur->u.Open.symlink.Buffer == NULL) {
1695             cur->status = STATUS_INSUFFICIENT_RESOURCES;
1696             status = STATUS_UNSUCCESSFUL;
1697             goto out;
1698         }
1699         RtlCopyMemory(cur->u.Open.symlink.Buffer, *buf,
1700             cur->u.Open.symlink.MaximumLength);
1701 #ifdef DEBUG_MARSHAL_DETAIL
1702         DbgP("unmarshal_nfs41_open: ERROR_REPARSE -> '%wZ'\n", &cur->u.Open.symlink);
1703 #endif
1704     }
1705 #ifdef DEBUG_MARSHAL_DETAIL
1706     DbgP("unmarshal_nfs41_open: open_state 0x%x mode %o changeattr %llu "
1707         "deleg_type %d\n", cur->open_state, cur->u.Open.mode,
1708         cur->ChangeTime, cur->u.Open.deleg_type);
1709 #endif
1710 out:
1711     return status;
1712 }
1713 
1714 NTSTATUS unmarshal_nfs41_dirquery(
1715     nfs41_updowncall_entry *cur,
1716     unsigned char **buf)
1717 {
1718     NTSTATUS status = STATUS_SUCCESS;
1719     ULONG buf_len;
1720 
1721     RtlCopyMemory(&buf_len, *buf, sizeof(ULONG));
1722 #ifdef DEBUG_MARSHAL_DETAIL
1723     DbgP("unmarshal_nfs41_dirquery: reply size %d\n", buf_len);
1724 #endif
1725     *buf += sizeof(ULONG);
1726     _SEH2_TRY {
1727         MmUnmapLockedPages(cur->u.QueryFile.mdl_buf, cur->u.QueryFile.mdl);
1728     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1729         NTSTATUS code;
1730         code = _SEH2_GetExceptionCode();
1731         print_error("MmUnmapLockedPages thrown exception=0x%0x\n", code);
1732         status = STATUS_ACCESS_DENIED;
1733     } _SEH2_END;
1734     if (buf_len > cur->buf_len)
1735         cur->status = STATUS_BUFFER_TOO_SMALL;
1736     cur->buf_len = buf_len;
1737 
1738     return status;
1739 }
1740 
1741 void unmarshal_nfs41_attrget(
1742     nfs41_updowncall_entry *cur,
1743     PVOID attr_value,
1744     ULONG *attr_len,
1745     unsigned char **buf)
1746 {
1747     ULONG buf_len;
1748     RtlCopyMemory(&buf_len, *buf, sizeof(ULONG));
1749     if (buf_len > *attr_len) {
1750         cur->status = STATUS_BUFFER_TOO_SMALL;
1751         return;
1752     }
1753     *buf += sizeof(ULONG);
1754     *attr_len = buf_len;
1755     RtlCopyMemory(attr_value, *buf, buf_len);
1756     *buf += buf_len;
1757 }
1758 
1759 void unmarshal_nfs41_eaget(
1760     nfs41_updowncall_entry *cur,
1761     unsigned char **buf)
1762 {
1763     RtlCopyMemory(&cur->u.QueryEa.Overflow, *buf, sizeof(ULONG));
1764     *buf += sizeof(ULONG);
1765     RtlCopyMemory(&cur->buf_len, *buf, sizeof(ULONG));
1766     *buf += sizeof(ULONG);
1767     if (cur->u.QueryEa.Overflow != ERROR_INSUFFICIENT_BUFFER) {
1768         RtlCopyMemory(cur->buf, *buf, cur->buf_len);
1769         *buf += cur->buf_len;
1770     }
1771 }
1772 
1773 void unmarshal_nfs41_getattr(
1774     nfs41_updowncall_entry *cur,
1775     unsigned char **buf)
1776 {
1777     unmarshal_nfs41_attrget(cur, cur->buf, &cur->buf_len, buf);
1778     RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(LONGLONG));
1779 #ifdef DEBUG_MARSHAL_DETAIL
1780     if (cur->u.QueryFile.InfoClass == FileBasicInformation)
1781         DbgP("[unmarshal_nfs41_getattr] ChangeTime %llu\n", cur->ChangeTime);
1782 #endif
1783 }
1784 
1785 NTSTATUS unmarshal_nfs41_getacl(
1786     nfs41_updowncall_entry *cur,
1787     unsigned char **buf)
1788 {
1789     NTSTATUS status = STATUS_SUCCESS;
1790     DWORD buf_len;
1791 
1792     RtlCopyMemory(&buf_len, *buf, sizeof(DWORD));
1793     *buf += sizeof(DWORD);
1794     cur->buf = RxAllocatePoolWithTag(NonPagedPool,
1795         buf_len, NFS41_MM_POOLTAG_ACL);
1796     if (cur->buf == NULL) {
1797         cur->status = status = STATUS_INSUFFICIENT_RESOURCES;
1798         goto out;
1799     }
1800     RtlCopyMemory(cur->buf, *buf, buf_len);
1801     if (buf_len > cur->buf_len)
1802         cur->status = STATUS_BUFFER_TOO_SMALL;
1803     cur->buf_len = buf_len;
1804 
1805 out:
1806     return status;
1807 }
1808 
1809 void unmarshal_nfs41_symlink(
1810     nfs41_updowncall_entry *cur,
1811     unsigned char **buf)
1812 {
1813     if (cur->u.Symlink.set) return;
1814 
1815     RtlCopyMemory(&cur->u.Symlink.target->Length, *buf, sizeof(USHORT));
1816     *buf += sizeof(USHORT);
1817     if (cur->u.Symlink.target->Length >
1818             cur->u.Symlink.target->MaximumLength) {
1819         cur->status = STATUS_BUFFER_TOO_SMALL;
1820         return;
1821     }
1822     RtlCopyMemory(cur->u.Symlink.target->Buffer, *buf,
1823         cur->u.Symlink.target->Length);
1824     cur->u.Symlink.target->Length -= sizeof(UNICODE_NULL);
1825 }
1826 
1827 NTSTATUS nfs41_downcall(
1828     IN PRX_CONTEXT RxContext)
1829 {
1830     NTSTATUS status = STATUS_SUCCESS;
1831     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
1832     ULONG in_len = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
1833     unsigned char *buf = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
1834     PLIST_ENTRY pEntry;
1835     nfs41_updowncall_entry *tmp, *cur= NULL;
1836     BOOLEAN found = 0;
1837 
1838     print_hexbuf(0, (unsigned char *)"downcall buffer", buf, in_len);
1839 
1840     tmp = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_entry),
1841             NFS41_MM_POOLTAG_DOWN);
1842     if (tmp == NULL) goto out;
1843 
1844     unmarshal_nfs41_header(tmp, &buf);
1845 
1846     ExAcquireFastMutex(&downcallLock);
1847     pEntry = &downcall.head;
1848     pEntry = pEntry->Flink;
1849     while (pEntry != NULL) {
1850         cur = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
1851                 nfs41_updowncall_entry, next);
1852         if (cur->xid == tmp->xid) {
1853             found = 1;
1854             break;
1855         }
1856         if (pEntry->Flink == &downcall.head)
1857             break;
1858         pEntry = pEntry->Flink;
1859     }
1860     ExReleaseFastMutex(&downcallLock);
1861     SeStopImpersonatingClient();
1862     if (!found) {
1863         print_error("Didn't find xid=%lld entry\n", tmp->xid);
1864         goto out_free;
1865     }
1866 
1867     ExAcquireFastMutex(&cur->lock);
1868     if (cur->state == NFS41_NOT_WAITING) {
1869         DbgP("[downcall] Nobody is waiting for this request!!!\n");
1870         switch(cur->opcode) {
1871         case NFS41_WRITE:
1872         case NFS41_READ:
1873             MmUnmapLockedPages(cur->buf, cur->u.ReadWrite.MdlAddress);
1874             break;
1875         case NFS41_DIR_QUERY:
1876             MmUnmapLockedPages(cur->u.QueryFile.mdl_buf,
1877                     cur->u.QueryFile.mdl);
1878             IoFreeMdl(cur->u.QueryFile.mdl);
1879             break;
1880         case NFS41_OPEN:
1881             if (cur->u.Open.EaMdl) {
1882                 MmUnmapLockedPages(cur->u.Open.EaBuffer,
1883                         cur->u.Open.EaMdl);
1884                 IoFreeMdl(cur->u.Open.EaMdl);
1885             }
1886             break;
1887         }
1888         ExReleaseFastMutex(&cur->lock);
1889         nfs41_RemoveEntry(downcallLock, cur);
1890         RxFreePool(cur);
1891         status = STATUS_UNSUCCESSFUL;
1892         goto out_free;
1893     }
1894     cur->state = NFS41_DONE_PROCESSING;
1895     cur->status = tmp->status;
1896     cur->errno = tmp->errno;
1897     status = STATUS_SUCCESS;
1898 
1899     if (!tmp->status) {
1900         switch (tmp->opcode) {
1901         case NFS41_MOUNT:
1902             unmarshal_nfs41_mount(cur, &buf);
1903             break;
1904         case NFS41_WRITE:
1905         case NFS41_READ:
1906             status = unmarshal_nfs41_rw(cur, &buf);
1907             break;
1908         case NFS41_OPEN:
1909             status = unmarshal_nfs41_open(cur, &buf);
1910             break;
1911         case NFS41_DIR_QUERY:
1912             status = unmarshal_nfs41_dirquery(cur, &buf);
1913             break;
1914         case NFS41_FILE_QUERY:
1915             unmarshal_nfs41_getattr(cur, &buf);
1916             break;
1917         case NFS41_EA_GET:
1918             unmarshal_nfs41_eaget(cur, &buf);
1919             break;
1920         case NFS41_SYMLINK:
1921             unmarshal_nfs41_symlink(cur, &buf);
1922             break;
1923         case NFS41_VOLUME_QUERY:
1924             unmarshal_nfs41_attrget(cur, cur->buf, &cur->buf_len, &buf);
1925             break;
1926         case NFS41_ACL_QUERY:
1927             status = unmarshal_nfs41_getacl(cur, &buf);
1928             break;
1929         case NFS41_FILE_SET:
1930             unmarshal_nfs41_setattr(cur, &cur->ChangeTime, &buf);
1931             break;
1932         case NFS41_EA_SET:
1933             unmarshal_nfs41_setattr(cur, &cur->ChangeTime, &buf);
1934             break;
1935         case NFS41_ACL_SET:
1936             unmarshal_nfs41_setattr(cur, &cur->ChangeTime, &buf);
1937             break;
1938         }
1939     }
1940     ExReleaseFastMutex(&cur->lock);
1941     if (cur->async_op) {
1942         if (cur->status == STATUS_SUCCESS) {
1943             cur->u.ReadWrite.rxcontext->StoredStatus = STATUS_SUCCESS;
1944             cur->u.ReadWrite.rxcontext->InformationToReturn =
1945                 cur->buf_len;
1946         } else {
1947             cur->u.ReadWrite.rxcontext->StoredStatus =
1948                 map_readwrite_errors(cur->status);
1949             cur->u.ReadWrite.rxcontext->InformationToReturn = 0;
1950         }
1951         nfs41_RemoveEntry(downcallLock, cur);
1952         RxLowIoCompletion(cur->u.ReadWrite.rxcontext);
1953         RxFreePool(cur);
1954     } else
1955         KeSetEvent(&cur->cond, 0, FALSE);
1956 
1957 out_free:
1958     RxFreePool(tmp);
1959 out:
1960     return status;
1961 }
1962 
1963 NTSTATUS nfs41_shutdown_daemon(
1964     DWORD version)
1965 {
1966     NTSTATUS status = STATUS_SUCCESS;
1967     nfs41_updowncall_entry *entry = NULL;
1968 
1969     DbgEn();
1970     status = nfs41_UpcallCreate(NFS41_SHUTDOWN, NULL, INVALID_HANDLE_VALUE,
1971         INVALID_HANDLE_VALUE, version, NULL, &entry);
1972     if (status) goto out;
1973 
1974     status = nfs41_UpcallWaitForReply(entry, UPCALL_TIMEOUT_DEFAULT);
1975     SeDeleteClientSecurity(&entry->sec_ctx);
1976     if (status) goto out;
1977 
1978     RxFreePool(entry);
1979 out:
1980     DbgEx();
1981     return status;
1982 }
1983 
1984 NTSTATUS SharedMemoryInit(
1985     OUT PHANDLE phSection)
1986 {
1987     NTSTATUS status;
1988     HANDLE hSection;
1989     UNICODE_STRING SectionName;
1990     SECURITY_DESCRIPTOR SecurityDesc;
1991     OBJECT_ATTRIBUTES SectionAttrs;
1992     LARGE_INTEGER nSectionSize;
1993 
1994     DbgEn();
1995 
1996     RtlInitUnicodeString(&SectionName, NFS41_SHARED_MEMORY_NAME);
1997 
1998     /* XXX: setting dacl=NULL grants access to everyone */
1999     status = RtlCreateSecurityDescriptor(&SecurityDesc,
2000         SECURITY_DESCRIPTOR_REVISION);
2001     if (status) {
2002         print_error("RtlCreateSecurityDescriptor() failed with %08X\n", status);
2003         goto out;
2004     }
2005     status = RtlSetDaclSecurityDescriptor(&SecurityDesc, TRUE, NULL, FALSE);
2006     if (status) {
2007         print_error("RtlSetDaclSecurityDescriptor() failed with %08X\n", status);
2008         goto out;
2009     }
2010 
2011     InitializeObjectAttributes(&SectionAttrs, &SectionName,
2012         0, NULL, &SecurityDesc);
2013 
2014     nSectionSize.QuadPart = sizeof(NFS41NP_SHARED_MEMORY);
2015 
2016     status = ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE,
2017         &SectionAttrs, &nSectionSize, PAGE_READWRITE, SEC_COMMIT, NULL);
2018     switch (status) {
2019     case STATUS_SUCCESS:
2020         break;
2021     case STATUS_OBJECT_NAME_COLLISION:
2022         DbgP("section already created; returning success\n");
2023         status = STATUS_SUCCESS;
2024         goto out;
2025     default:
2026         DbgP("ZwCreateSection failed with %08X\n", status);
2027         goto out;
2028     }
2029 out:
2030     DbgEx();
2031     return status;
2032 }
2033 
2034 NTSTATUS SharedMemoryFree(
2035     IN HANDLE hSection)
2036 {
2037     NTSTATUS status;
2038     DbgEn();
2039     status = ZwClose(hSection);
2040     DbgEx();
2041     return status;
2042 }
2043 
2044 #ifdef __REACTOS__
2045 NTSTATUS NTAPI nfs41_Start(
2046 #else
2047 NTSTATUS nfs41_Start(
2048 #endif
2049     IN OUT PRX_CONTEXT RxContext,
2050     IN OUT PRDBSS_DEVICE_OBJECT dev)
2051 {
2052     NTSTATUS status;
2053     NFS41GetDeviceExtension(RxContext, DevExt);
2054 
2055     DbgEn();
2056 
2057     status = SharedMemoryInit(&DevExt->SharedMemorySection);
2058     if (status) {
2059         print_error("InitSharedMemory failed with %08X\n", status);
2060         status = STATUS_INSUFFICIENT_RESOURCES;
2061         goto out;
2062     }
2063 
2064     InterlockedCompareExchange((PLONG)&nfs41_start_state,
2065         NFS41_START_DRIVER_STARTED,
2066         NFS41_START_DRIVER_START_IN_PROGRESS);
2067 out:
2068     DbgEx();
2069     return status;
2070 }
2071 
2072 #ifdef __REACTOS__
2073 NTSTATUS NTAPI nfs41_Stop(
2074 #else
2075 NTSTATUS nfs41_Stop(
2076 #endif
2077     IN OUT PRX_CONTEXT RxContext,
2078     IN OUT PRDBSS_DEVICE_OBJECT dev)
2079 {
2080     NTSTATUS status;
2081     NFS41GetDeviceExtension(RxContext, DevExt);
2082     DbgEn();
2083     status = SharedMemoryFree(DevExt->SharedMemorySection);
2084     DbgEx();
2085     return status;
2086 }
2087 
2088 NTSTATUS GetConnectionHandle(
2089     IN PUNICODE_STRING ConnectionName,
2090     IN PVOID EaBuffer,
2091     IN ULONG EaLength,
2092     OUT PHANDLE Handle)
2093 {
2094     NTSTATUS status;
2095     IO_STATUS_BLOCK IoStatusBlock;
2096     OBJECT_ATTRIBUTES ObjectAttributes;
2097 
2098 #ifdef DEBUG_MOUNT
2099     DbgEn();
2100 #endif
2101     InitializeObjectAttributes(&ObjectAttributes, ConnectionName,
2102         OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);
2103 
2104     print_error("Len %d Buf %p\n", EaLength, EaBuffer);
2105 
2106     status = ZwCreateFile(Handle, SYNCHRONIZE, &ObjectAttributes,
2107         &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
2108         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2109         FILE_OPEN_IF,
2110         FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
2111         EaBuffer, EaLength);
2112 
2113 #ifdef DEBUG_MOUNT
2114     DbgEx();
2115 #endif
2116     return status;
2117 }
2118 
2119 NTSTATUS nfs41_GetConnectionInfoFromBuffer(
2120     IN PVOID Buffer,
2121     IN ULONG BufferLen,
2122     OUT PUNICODE_STRING pConnectionName,
2123     OUT PVOID *ppEaBuffer,
2124     OUT PULONG pEaLength)
2125 {
2126     NTSTATUS status = STATUS_SUCCESS;
2127     USHORT NameLength, EaPadding;
2128     ULONG EaLength, BufferLenExpected;
2129     PBYTE ptr;
2130 
2131     /* make sure buffer is at least big enough for header */
2132     if (BufferLen < sizeof(USHORT) + sizeof(USHORT) + sizeof(ULONG)) {
2133         status = STATUS_BAD_NETWORK_NAME;
2134         print_error("Invalid input buffer.\n");
2135         pConnectionName->Length = pConnectionName->MaximumLength = 0;
2136         *ppEaBuffer = NULL;
2137         *pEaLength = 0;
2138         goto out;
2139     }
2140 
2141     ptr = Buffer;
2142     NameLength = *(PUSHORT)ptr;
2143     ptr += sizeof(USHORT);
2144     EaPadding = *(PUSHORT)ptr;
2145     ptr += sizeof(USHORT);
2146     EaLength = *(PULONG)ptr;
2147     ptr += sizeof(ULONG);
2148 
2149     /* validate buffer length */
2150     BufferLenExpected = sizeof(USHORT) + sizeof(USHORT) + sizeof(ULONG) +
2151         NameLength + EaPadding + EaLength;
2152     if (BufferLen != BufferLenExpected) {
2153         status = STATUS_BAD_NETWORK_NAME;
2154         print_error("Received buffer of length %lu, but expected %lu bytes.\n",
2155             BufferLen, BufferLenExpected);
2156         pConnectionName->Length = pConnectionName->MaximumLength = 0;
2157         *ppEaBuffer = NULL;
2158         *pEaLength = 0;
2159         goto out;
2160     }
2161 
2162     pConnectionName->Buffer = (PWCH)ptr;
2163     pConnectionName->Length = NameLength - sizeof(WCHAR);
2164     pConnectionName->MaximumLength = NameLength;
2165 
2166     if (EaLength)
2167         *ppEaBuffer = ptr + NameLength + EaPadding;
2168     else
2169         *ppEaBuffer = NULL;
2170     *pEaLength = EaLength;
2171 
2172 out:
2173     return status;
2174 }
2175 
2176 NTSTATUS nfs41_CreateConnection(
2177     IN PRX_CONTEXT RxContext,
2178     OUT PBOOLEAN PostToFsp)
2179 {
2180     NTSTATUS status = STATUS_SUCCESS;
2181     HANDLE Handle = INVALID_HANDLE_VALUE;
2182     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
2183     PVOID Buffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer, EaBuffer;
2184     ULONG BufferLen = LowIoContext->ParamsFor.IoCtl.InputBufferLength, EaLength;
2185     UNICODE_STRING FileName;
2186     BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
2187 
2188 #ifdef DEBUG_MOUNT
2189     DbgEn();
2190 #endif
2191 
2192     if (!Wait) {
2193         //just post right now!
2194         DbgP("returning STATUS_PENDING\n");
2195         *PostToFsp = TRUE;
2196         status = STATUS_PENDING;
2197         goto out;
2198     }
2199 
2200     status = nfs41_GetConnectionInfoFromBuffer(Buffer, BufferLen,
2201         &FileName, &EaBuffer, &EaLength);
2202     if (status != STATUS_SUCCESS)
2203         goto out;
2204 
2205     status = GetConnectionHandle(&FileName, EaBuffer, EaLength, &Handle);
2206     if (!status && Handle != INVALID_HANDLE_VALUE)
2207         ZwClose(Handle);
2208 out:
2209 #ifdef DEBUG_MOUNT
2210     DbgEx();
2211 #endif
2212     return status;
2213 }
2214 
2215 #ifdef ENABLE_TIMINGS
2216 void print_op_stat(
2217     const char *op_str,
2218     nfs41_timings *time, BOOLEAN clear)
2219 {
2220     DbgP("%-9s: num_ops=%-10d delta_ticks=%-10d size=%-10d\n", op_str,
2221         time->tops, time->tops ? time->ticks/time->tops : 0,
2222         time->sops ? time->size/time->sops : 0);
2223     if (clear) {
2224         time->tops = 0;
2225         time->ticks = 0;
2226         time->size = 0;
2227         time->sops = 0;
2228     }
2229 }
2230 #endif
2231 NTSTATUS nfs41_unmount(
2232     HANDLE session,
2233     DWORD version,
2234     DWORD timeout)
2235 {
2236     NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
2237     nfs41_updowncall_entry *entry;
2238 
2239 #ifdef DEBUG_MOUNT
2240     DbgEn();
2241 #endif
2242     status = nfs41_UpcallCreate(NFS41_UNMOUNT, NULL, session,
2243         INVALID_HANDLE_VALUE, version, NULL, &entry);
2244     SeDeleteClientSecurity(&entry->sec_ctx);
2245     if (status) goto out;
2246 
2247     nfs41_UpcallWaitForReply(entry, timeout);
2248     RxFreePool(entry);
2249 out:
2250 #ifdef ENABLE_TIMINGS
2251     print_op_stat("lookup", &lookup, 1);
2252     print_op_stat("open", &open, 1);
2253     print_op_stat("close", &close, 1);
2254     print_op_stat("volume", &volume, 1);
2255     print_op_stat("getattr", &getattr, 1);
2256     print_op_stat("setattr", &setattr, 1);
2257     print_op_stat("getexattr", &getexattr, 1);
2258     print_op_stat("setexattr", &setexattr, 1);
2259     print_op_stat("readdir", &readdir, 1);
2260     print_op_stat("getacl", &getacl, 1);
2261     print_op_stat("setacl", &setacl, 1);
2262     print_op_stat("read", &read, 1);
2263     print_op_stat("write", &write, 1);
2264     print_op_stat("lock", &lock, 1);
2265     print_op_stat("unlock", &unlock, 1);
2266 #endif
2267 #ifdef DEBUG_MOUNT
2268     DbgEx();
2269 #endif
2270     return status;
2271 }
2272 
2273 NTSTATUS nfs41_DeleteConnection (
2274     IN PRX_CONTEXT RxContext,
2275     OUT PBOOLEAN PostToFsp)
2276 {
2277     NTSTATUS status = STATUS_INVALID_PARAMETER;
2278     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
2279     PWCHAR ConnectName = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
2280     ULONG ConnectNameLen = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
2281     HANDLE Handle;
2282     UNICODE_STRING FileName;
2283     PFILE_OBJECT pFileObject;
2284     BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
2285 
2286 #ifdef DEBUG_MOUNT
2287     DbgEn();
2288 #endif
2289 
2290     if (!Wait) {
2291         //just post right now!
2292         *PostToFsp = TRUE;
2293         DbgP("returning STATUS_PENDING\n");
2294         status = STATUS_PENDING;
2295         goto out;
2296     }
2297 
2298     FileName.Buffer = ConnectName;
2299     FileName.Length = (USHORT) ConnectNameLen - sizeof(WCHAR);
2300     FileName.MaximumLength = (USHORT) ConnectNameLen;
2301 
2302     status = GetConnectionHandle(&FileName, NULL, 0, &Handle);
2303     if (status != STATUS_SUCCESS)
2304         goto out;
2305 
2306     status = ObReferenceObjectByHandle(Handle, 0L, NULL, KernelMode,
2307                 (PVOID *)&pFileObject, NULL);
2308     if (NT_SUCCESS(status)) {
2309         PV_NET_ROOT VNetRoot;
2310 
2311         // VNetRoot exists as FOBx in the FsContext2
2312         VNetRoot = (PV_NET_ROOT) pFileObject->FsContext2;
2313         // make sure the node looks right
2314         if (NodeType(VNetRoot) == RDBSS_NTC_V_NETROOT)
2315         {
2316 #ifdef DEBUG_MOUNT
2317             DbgP("Calling RxFinalizeConnection for NetRoot %p from VNetRoot %p\n",
2318                 VNetRoot->NetRoot, VNetRoot);
2319 #endif
2320             status = RxFinalizeConnection(VNetRoot->NetRoot, VNetRoot, TRUE);
2321         }
2322         else
2323             status = STATUS_BAD_NETWORK_NAME;
2324 
2325         ObDereferenceObject(pFileObject);
2326     }
2327     ZwClose(Handle);
2328 out:
2329 #ifdef DEBUG_MOUNT
2330     DbgEx();
2331 #endif
2332     return status;
2333 }
2334 
2335 #ifdef __REACTOS__
2336 NTSTATUS NTAPI nfs41_DevFcbXXXControlFile(
2337 #else
2338 NTSTATUS nfs41_DevFcbXXXControlFile(
2339 #endif
2340     IN OUT PRX_CONTEXT RxContext)
2341 {
2342     NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
2343     UCHAR op = RxContext->MajorFunction;
2344     PLOWIO_CONTEXT io_ctx = &RxContext->LowIoContext;
2345     ULONG fsop = io_ctx->ParamsFor.FsCtl.FsControlCode, state;
2346     ULONG in_len = io_ctx->ParamsFor.IoCtl.InputBufferLength;
2347     DWORD *buf = io_ctx->ParamsFor.IoCtl.pInputBuffer;
2348     NFS41GetDeviceExtension(RxContext, DevExt);
2349     DWORD nfs41d_version = 0;
2350 
2351     //DbgEn();
2352 
2353     print_ioctl(0, op);
2354     switch(op) {
2355     case IRP_MJ_FILE_SYSTEM_CONTROL:
2356         status = STATUS_INVALID_DEVICE_REQUEST;
2357         break;
2358     case IRP_MJ_DEVICE_CONTROL:
2359     case IRP_MJ_INTERNAL_DEVICE_CONTROL:
2360         print_fs_ioctl(0, fsop);
2361         switch (fsop) {
2362         case IOCTL_NFS41_INVALCACHE:
2363             nfs41_invalidate_cache(RxContext);
2364             status = STATUS_SUCCESS;
2365             break;
2366         case IOCTL_NFS41_READ:
2367             status = nfs41_upcall(RxContext);
2368             break;
2369         case IOCTL_NFS41_WRITE:
2370             status = nfs41_downcall(RxContext);
2371             break;
2372         case IOCTL_NFS41_ADDCONN:
2373             status = nfs41_CreateConnection(RxContext, &RxContext->PostRequest);
2374             break;
2375         case IOCTL_NFS41_DELCONN:
2376             if (RxContext->RxDeviceObject->NumberOfActiveFcbs > 0) {
2377                 DbgP("device has open handles %d\n",
2378                     RxContext->RxDeviceObject->NumberOfActiveFcbs);
2379 #ifdef __REACTOS__
2380                 if (RxContext->RxDeviceObject->pRxNetNameTable != NULL)
2381                 {
2382 #define DUMP_FCB_TABLE_FROM_NETROOT(N)                                               \
2383 {                                                                                    \
2384     USHORT Bucket2;                                                                  \
2385     BOOLEAN Release2 = FALSE;                                                        \
2386     if (!RxIsFcbTableLockAcquired(&(N)->FcbTable))                                   \
2387     {                                                                                \
2388         RxAcquireFcbTableLockExclusive(&(N)->FcbTable, TRUE);                        \
2389         Release2 = TRUE;                                                             \
2390     }                                                                                \
2391     for (Bucket2 = 0; Bucket2 < (N)->FcbTable.NumberOfBuckets; ++Bucket2)            \
2392     {                                                                                \
2393         PLIST_ENTRY Entry2;                                                          \
2394         for (Entry2 = (N)->FcbTable.HashBuckets[Bucket2].Flink;                      \
2395              Entry2 != &(N)->FcbTable.HashBuckets[Bucket2];                          \
2396              Entry2 = Entry2->Flink)                                                 \
2397         {                                                                            \
2398             PFCB Fcb;                                                                \
2399             Fcb = CONTAINING_RECORD(Entry2, FCB, FcbTableEntry.HashLinks);           \
2400             DbgP("Fcb: %p still has %d references\n", Fcb, Fcb->NodeReferenceCount); \
2401             DbgP("It is for: %wZ\n", &Fcb->FcbTableEntry.Path);                      \
2402         }                                                                            \
2403     }                                                                                \
2404     if (Release2)                                                                    \
2405     {                                                                                \
2406         RxReleaseFcbTableLock(&(N)->FcbTable);                                       \
2407     }                                                                                \
2408 }
2409                     USHORT Bucket;
2410                     BOOLEAN Release = FALSE;
2411 
2412                     if (!RxIsPrefixTableLockAcquired(RxContext->RxDeviceObject->pRxNetNameTable))
2413                     {
2414                         RxAcquirePrefixTableLockExclusive(RxContext->RxDeviceObject->pRxNetNameTable, TRUE);
2415                         Release = TRUE;
2416                     }
2417 
2418                     for (Bucket = 0; Bucket < RxContext->RxDeviceObject->pRxNetNameTable->TableSize; ++Bucket)
2419                     {
2420                         PLIST_ENTRY Entry;
2421 
2422                         for (Entry = RxContext->RxDeviceObject->pRxNetNameTable->HashBuckets[Bucket].Flink;
2423                              Entry != &RxContext->RxDeviceObject->pRxNetNameTable->HashBuckets[Bucket];
2424                              Entry = Entry->Flink)
2425                         {
2426                             PVOID Container;
2427 
2428                             Container = CONTAINING_RECORD(Entry, RX_PREFIX_ENTRY, HashLinks)->ContainingRecord;
2429                             switch (NodeType(Container) & ~RX_SCAVENGER_MASK)
2430                             {
2431                                 case RDBSS_NTC_NETROOT:
2432                                 {
2433                                     PNET_ROOT NetRoot;
2434 
2435                                     NetRoot = Container;
2436                                     DUMP_FCB_TABLE_FROM_NETROOT(NetRoot);
2437                                     break;
2438                                 }
2439 
2440                                 case RDBSS_NTC_V_NETROOT:
2441                                 {
2442                                     PV_NET_ROOT VNetRoot;
2443 
2444                                     VNetRoot = Container;
2445                                     if (VNetRoot->NetRoot != NULL)
2446                                     {
2447                                         PNET_ROOT NetRoot;
2448 
2449                                         NetRoot = VNetRoot->NetRoot;
2450                                         DUMP_FCB_TABLE_FROM_NETROOT(NetRoot);
2451                                     }
2452                                     break;
2453                                 }
2454 
2455                                 default:
2456                                 {
2457                                     DbgP("Other node found: %x\n", NodeType(Container));
2458                                     break;
2459                                 }
2460                             }
2461                         }
2462                     }
2463 
2464                     if (Release)
2465                     {
2466                         RxReleasePrefixTableLock(RxContext->RxDeviceObject->pRxNetNameTable);
2467                     }
2468 #undef DUMP_FCB_TABLE_FROM_NETROOT
2469                 }
2470                 else
2471                 {
2472                     DbgP("RxNetNameTable is NULL for: %p\n", RxContext->RxDeviceObject);
2473                 }
2474 #endif
2475                 status = STATUS_REDIRECTOR_HAS_OPEN_HANDLES;
2476                 break;
2477             }
2478             status = nfs41_DeleteConnection(RxContext, &RxContext->PostRequest);
2479             break;
2480         case IOCTL_NFS41_GETSTATE:
2481             state = RDR_NULL_STATE;
2482 
2483             if (io_ctx->ParamsFor.IoCtl.OutputBufferLength >= sizeof(ULONG)) {
2484                 // map the states to control app's equivalents
2485                 print_driver_state(nfs41_start_state);
2486                 switch (nfs41_start_state) {
2487                 case NFS41_START_DRIVER_STARTABLE:
2488                 case NFS41_START_DRIVER_STOPPED:
2489                     state = RDR_STOPPED;
2490                     break;
2491                 case NFS41_START_DRIVER_START_IN_PROGRESS:
2492                     state = RDR_STARTING;
2493                     break;
2494                 case NFS41_START_DRIVER_STARTED:
2495                     state = RDR_STARTED;
2496                     break;
2497                 }
2498                 *(ULONG *)io_ctx->ParamsFor.IoCtl.pOutputBuffer = state;
2499                 RxContext->InformationToReturn = sizeof(ULONG);
2500                 status = STATUS_SUCCESS;
2501             } else
2502                 status = STATUS_INVALID_PARAMETER;
2503             break;
2504         case IOCTL_NFS41_START:
2505             print_driver_state(nfs41_start_state);
2506             if (in_len >= sizeof(DWORD)) {
2507                 RtlCopyMemory(&nfs41d_version, buf, sizeof(DWORD));
2508                 DbgP("NFS41 Daemon sent start request with version %d\n",
2509                     nfs41d_version);
2510                 DbgP("Currently used NFS41 Daemon version is %d\n",
2511                     DevExt->nfs41d_version);
2512                 DevExt->nfs41d_version = nfs41d_version;
2513             }
2514             switch(nfs41_start_state) {
2515             case NFS41_START_DRIVER_STARTABLE:
2516                 (nfs41_start_driver_state)InterlockedCompareExchange(
2517                               (PLONG)&nfs41_start_state,
2518                               NFS41_START_DRIVER_START_IN_PROGRESS,
2519                               NFS41_START_DRIVER_STARTABLE);
2520                     //lack of break is intentional
2521             case NFS41_START_DRIVER_START_IN_PROGRESS:
2522                 status = RxStartMinirdr(RxContext, &RxContext->PostRequest);
2523                 if (status == STATUS_REDIRECTOR_STARTED) {
2524                     DbgP("redirector started\n");
2525                     status = STATUS_SUCCESS;
2526                 } else if (status == STATUS_PENDING &&
2527                             RxContext->PostRequest == TRUE) {
2528                     DbgP("RxStartMinirdr pending %08lx\n", status);
2529                     status = STATUS_MORE_PROCESSING_REQUIRED;
2530                 }
2531                 break;
2532             case NFS41_START_DRIVER_STARTED:
2533                 status = STATUS_SUCCESS;
2534                 break;
2535             default:
2536                 status = STATUS_INVALID_PARAMETER;
2537             }
2538             break;
2539         case IOCTL_NFS41_STOP:
2540             if (nfs41_start_state == NFS41_START_DRIVER_STARTED)
2541                 nfs41_shutdown_daemon(DevExt->nfs41d_version);
2542             if (RxContext->RxDeviceObject->NumberOfActiveFcbs > 0) {
2543                 DbgP("device has open handles %d\n",
2544                     RxContext->RxDeviceObject->NumberOfActiveFcbs);
2545                 status = STATUS_REDIRECTOR_HAS_OPEN_HANDLES;
2546                 break;
2547             }
2548 
2549             state = (nfs41_start_driver_state)InterlockedCompareExchange(
2550                         (PLONG)&nfs41_start_state,
2551                         NFS41_START_DRIVER_STARTABLE,
2552                         NFS41_START_DRIVER_STARTED);
2553 
2554             status = RxStopMinirdr(RxContext, &RxContext->PostRequest);
2555             DbgP("RxStopMinirdr status %08lx\n", status);
2556             if (status == STATUS_PENDING && RxContext->PostRequest == TRUE )
2557                 status = STATUS_MORE_PROCESSING_REQUIRED;
2558             break;
2559         default:
2560             status = STATUS_INVALID_DEVICE_REQUEST;
2561         };
2562         break;
2563     default:
2564         status = STATUS_INVALID_DEVICE_REQUEST;
2565     };
2566 
2567     //DbgEx();
2568     return status;
2569 }
2570 
2571 #ifndef __REACTOS__
2572 NTSTATUS _nfs41_CreateSrvCall(
2573     PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
2574 {
2575 #else
2576 NTSTATUS NTAPI _nfs41_CreateSrvCall(
2577     PVOID pContext)
2578 {
2579     PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext = pContext;
2580 #endif
2581     NTSTATUS status = STATUS_SUCCESS;
2582     PMRX_SRVCALL_CALLBACK_CONTEXT SCCBC = pCallbackContext;
2583     PMRX_SRV_CALL pSrvCall;
2584     PMRX_SRVCALLDOWN_STRUCTURE SrvCalldownStructure =
2585         (PMRX_SRVCALLDOWN_STRUCTURE)(SCCBC->SrvCalldownStructure);
2586     PNFS41_SERVER_ENTRY pServerEntry = NULL;
2587 
2588 #ifdef DEBUG_MOUNT
2589     DbgEn();
2590 #endif
2591 
2592     pSrvCall = SrvCalldownStructure->SrvCall;
2593 
2594     ASSERT( pSrvCall );
2595     ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
2596     print_srv_call(0, pSrvCall);
2597 
2598     // validate the server name with the test name of 'pnfs'
2599 #ifdef DEBUG_MOUNT
2600     DbgP("SrvCall: Connection Name Length: %d %wZ\n",
2601         pSrvCall->pSrvCallName->Length, pSrvCall->pSrvCallName);
2602 #endif
2603 
2604     if (pSrvCall->pSrvCallName->Length > SERVER_NAME_BUFFER_SIZE) {
2605         print_error("Server name '%wZ' too long for server entry (max %u)\n",
2606             pSrvCall->pSrvCallName, SERVER_NAME_BUFFER_SIZE);
2607         status = STATUS_NAME_TOO_LONG;
2608         goto out;
2609     }
2610 
2611     /* Let's create our own representation of the server */
2612     pServerEntry = (PNFS41_SERVER_ENTRY)RxAllocatePoolWithTag(PagedPool,
2613         sizeof(NFS41_SERVER_ENTRY), NFS41_MM_POOLTAG);
2614     if (pServerEntry == NULL) {
2615         status = STATUS_INSUFFICIENT_RESOURCES;
2616         goto out;
2617     }
2618     RtlZeroMemory(pServerEntry, sizeof(NFS41_SERVER_ENTRY));
2619 
2620     pServerEntry->Name.Buffer = pServerEntry->NameBuffer;
2621     pServerEntry->Name.Length = pSrvCall->pSrvCallName->Length;
2622     pServerEntry->Name.MaximumLength = SERVER_NAME_BUFFER_SIZE;
2623     RtlCopyMemory(pServerEntry->Name.Buffer, pSrvCall->pSrvCallName->Buffer,
2624         pServerEntry->Name.Length);
2625 
2626     pCallbackContext->RecommunicateContext = pServerEntry;
2627 #ifdef __REACTOS__
2628     InterlockedExchangePointer((void * volatile *)&pServerEntry->pRdbssSrvCall, pSrvCall);
2629 #else
2630     InterlockedExchangePointer(&pServerEntry->pRdbssSrvCall, pSrvCall);
2631 #endif
2632 
2633 out:
2634     SCCBC->Status = status;
2635     SrvCalldownStructure->CallBack(SCCBC);
2636 
2637 #ifdef DEBUG_MOUNT
2638     DbgEx();
2639 #endif
2640     return status;
2641 }
2642 
2643 #ifdef __REACTOS__
2644 VOID NTAPI _nfs41_CreateSrvCall_v(
2645     PVOID pCallbackContext)
2646 {
2647     _nfs41_CreateSrvCall(pCallbackContext);
2648 }
2649 #endif
2650 
2651 #ifdef __REACTOS__
2652 NTSTATUS NTAPI nfs41_CreateSrvCall(
2653 #else
2654 NTSTATUS nfs41_CreateSrvCall(
2655 #endif
2656     PMRX_SRV_CALL pSrvCall,
2657     PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
2658 {
2659     NTSTATUS status;
2660 
2661     ASSERT( pSrvCall );
2662     ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
2663 
2664     if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
2665         DbgP("executing with RDBSS context\n");
2666         status = _nfs41_CreateSrvCall(pCallbackContext);
2667     } else {
2668         status = RxDispatchToWorkerThread(nfs41_dev, DelayedWorkQueue,
2669 #ifdef __REACTOS__
2670             _nfs41_CreateSrvCall_v, pCallbackContext);
2671 #else
2672             _nfs41_CreateSrvCall, pCallbackContext);
2673 #endif
2674         if (status != STATUS_SUCCESS) {
2675             print_error("RxDispatchToWorkerThread returned status %08lx\n",
2676                 status);
2677             pCallbackContext->Status = status;
2678             pCallbackContext->SrvCalldownStructure->CallBack(pCallbackContext);
2679             status = STATUS_PENDING;
2680         }
2681     }
2682     /* RDBSS expects MRxCreateSrvCall to return STATUS_PENDING */
2683     if (status == STATUS_SUCCESS)
2684         status = STATUS_PENDING;
2685 
2686     return status;
2687 }
2688 
2689 #ifdef __REACTOS__
2690 NTSTATUS NTAPI nfs41_SrvCallWinnerNotify(
2691 #else
2692 NTSTATUS nfs41_SrvCallWinnerNotify(
2693 #endif
2694     IN OUT PMRX_SRV_CALL pSrvCall,
2695     IN BOOLEAN ThisMinirdrIsTheWinner,
2696     IN OUT PVOID pSrvCallContext)
2697 {
2698     NTSTATUS status = STATUS_SUCCESS;
2699     PNFS41_SERVER_ENTRY pServerEntry;
2700 
2701     pServerEntry = (PNFS41_SERVER_ENTRY)pSrvCallContext;
2702 
2703     if (!ThisMinirdrIsTheWinner) {
2704         ASSERT(1);
2705         goto out;
2706     }
2707 
2708     pSrvCall->Context = pServerEntry;
2709 out:
2710     return status;
2711 }
2712 
2713 NTSTATUS map_mount_errors(
2714     DWORD status)
2715 {
2716     switch (status) {
2717     case NO_ERROR:              return STATUS_SUCCESS;
2718     case ERROR_NETWORK_UNREACHABLE: return STATUS_NETWORK_UNREACHABLE;
2719     case ERROR_BAD_NET_RESP:    return STATUS_UNEXPECTED_NETWORK_ERROR;
2720     case ERROR_BAD_NET_NAME:    return STATUS_BAD_NETWORK_NAME;
2721     case ERROR_BAD_NETPATH:     return STATUS_BAD_NETWORK_PATH;
2722     default:
2723         print_error("failed to map windows error %d to NTSTATUS; "
2724             "defaulting to STATUS_INSUFFICIENT_RESOURCES\n", status);
2725         return STATUS_INSUFFICIENT_RESOURCES;
2726     }
2727 }
2728 
2729 NTSTATUS nfs41_mount(
2730     PNFS41_MOUNT_CONFIG config,
2731     DWORD sec_flavor,
2732     PHANDLE session,
2733     DWORD *version,
2734     PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs)
2735 {
2736     NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
2737     nfs41_updowncall_entry *entry;
2738 
2739 #ifdef DEBUG_MOUNT
2740     DbgEn();
2741     DbgP("Server Name %wZ Mount Point %wZ SecFlavor %d\n",
2742         &config->SrvName, &config->MntPt, sec_flavor);
2743 #endif
2744     status = nfs41_UpcallCreate(NFS41_MOUNT, NULL, *session,
2745         INVALID_HANDLE_VALUE, *version, &config->MntPt, &entry);
2746     if (status) goto out;
2747 
2748     entry->u.Mount.srv_name = &config->SrvName;
2749     entry->u.Mount.root = &config->MntPt;
2750     entry->u.Mount.rsize = config->ReadSize;
2751     entry->u.Mount.wsize = config->WriteSize;
2752     entry->u.Mount.sec_flavor = sec_flavor;
2753     entry->u.Mount.FsAttrs = FsAttrs;
2754 
2755     status = nfs41_UpcallWaitForReply(entry, config->timeout);
2756     SeDeleteClientSecurity(&entry->sec_ctx);
2757     if (status) goto out;
2758     *session = entry->session;
2759     if (entry->u.Mount.lease_time > config->timeout)
2760         config->timeout = entry->u.Mount.lease_time;
2761 
2762     /* map windows ERRORs to NTSTATUS */
2763     status = map_mount_errors(entry->status);
2764     if (status == STATUS_SUCCESS)
2765         *version = entry->version;
2766     RxFreePool(entry);
2767 out:
2768 #ifdef DEBUG_MOUNT
2769     DbgEx();
2770 #endif
2771     return status;
2772 }
2773 
2774 /* TODO: move mount config stuff to another file -cbodley */
2775 
2776 void nfs41_MountConfig_InitDefaults(
2777     OUT PNFS41_MOUNT_CONFIG Config)
2778 {
2779     RtlZeroMemory(Config, sizeof(NFS41_MOUNT_CONFIG));
2780 
2781     Config->ReadSize = MOUNT_CONFIG_RW_SIZE_DEFAULT;
2782     Config->WriteSize = MOUNT_CONFIG_RW_SIZE_DEFAULT;
2783     Config->ReadOnly = FALSE;
2784     Config->write_thru = FALSE;
2785     Config->nocache = FALSE;
2786     Config->SrvName.Length = 0;
2787     Config->SrvName.MaximumLength = SERVER_NAME_BUFFER_SIZE;
2788     Config->SrvName.Buffer = Config->srv_buffer;
2789     Config->MntPt.Length = 0;
2790     Config->MntPt.MaximumLength = MAX_PATH;
2791     Config->MntPt.Buffer = Config->mntpt_buffer;
2792     Config->SecFlavor.Length = 0;
2793     Config->SecFlavor.MaximumLength = MAX_SEC_FLAVOR_LEN;
2794     Config->SecFlavor.Buffer = Config->sec_flavor;
2795     RtlCopyUnicodeString(&Config->SecFlavor, &AUTH_SYS_NAME);
2796     Config->timeout = UPCALL_TIMEOUT_DEFAULT;
2797 }
2798 
2799 NTSTATUS nfs41_MountConfig_ParseBoolean(
2800     IN PFILE_FULL_EA_INFORMATION Option,
2801     IN PUNICODE_STRING usValue,
2802     OUT PBOOLEAN Value)
2803 {
2804     NTSTATUS status = STATUS_SUCCESS;
2805 
2806     /* if no value is specified, assume TRUE
2807      * if a value is specified, it must be a '1' */
2808     if (Option->EaValueLength == 0 || *usValue->Buffer == L'1')
2809         *Value = TRUE;
2810     else
2811         *Value = FALSE;
2812 
2813     DbgP("    '%ls' -> '%wZ' -> %u\n",
2814         (LPWSTR)Option->EaName, usValue, *Value);
2815     return status;
2816 }
2817 
2818 NTSTATUS nfs41_MountConfig_ParseDword(
2819     IN PFILE_FULL_EA_INFORMATION Option,
2820     IN PUNICODE_STRING usValue,
2821     OUT PDWORD Value,
2822     IN DWORD Minimum,
2823     IN DWORD Maximum)
2824 {
2825     NTSTATUS status = STATUS_INVALID_PARAMETER;
2826     LPWSTR Name = (LPWSTR)Option->EaName;
2827 
2828     if (Option->EaValueLength) {
2829         status = RtlUnicodeStringToInteger(usValue, 0, Value);
2830         if (status == STATUS_SUCCESS) {
2831 #ifdef IMPOSE_MINMAX_RWSIZES
2832             if (*Value < Minimum)
2833                 *Value = Minimum;
2834             if (*Value > Maximum)
2835                 *Value = Maximum;
2836             DbgP("    '%ls' -> '%wZ' -> %lu\n", Name, usValue, *Value);
2837 #endif
2838         }
2839         else
2840             print_error("Failed to convert %s='%wZ' to unsigned long.\n",
2841                 Name, usValue);
2842     }
2843 
2844     return status;
2845 }
2846 
2847 NTSTATUS nfs41_MountConfig_ParseOptions(
2848     IN PFILE_FULL_EA_INFORMATION EaBuffer,
2849     IN ULONG EaLength,
2850     IN OUT PNFS41_MOUNT_CONFIG Config)
2851 {
2852     NTSTATUS  status = STATUS_SUCCESS;
2853     PFILE_FULL_EA_INFORMATION Option;
2854     LPWSTR Name;
2855     size_t NameLen;
2856     UNICODE_STRING  usValue;
2857     Option = EaBuffer;
2858     while (status == STATUS_SUCCESS) {
2859         Name = (LPWSTR)Option->EaName;
2860         NameLen = Option->EaNameLength/sizeof(WCHAR);
2861 
2862         usValue.Length = usValue.MaximumLength = Option->EaValueLength;
2863         usValue.Buffer = (PWCH)(Option->EaName +
2864             Option->EaNameLength + sizeof(WCHAR));
2865 
2866         if (wcsncmp(L"ro", Name, NameLen) == 0) {
2867             status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
2868                 &Config->ReadOnly);
2869         }
2870         else if (wcsncmp(L"writethru", Name, NameLen) == 0) {
2871             status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
2872                 &Config->write_thru);
2873         }
2874         else if (wcsncmp(L"nocache", Name, NameLen) == 0) {
2875             status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
2876                 &Config->nocache);
2877         }
2878         else if (wcsncmp(L"timeout", Name, NameLen) == 0) {
2879             status = nfs41_MountConfig_ParseDword(Option, &usValue,
2880                 &Config->timeout, UPCALL_TIMEOUT_DEFAULT,
2881                 UPCALL_TIMEOUT_DEFAULT);
2882         }
2883         else if (wcsncmp(L"rsize", Name, NameLen) == 0) {
2884             status = nfs41_MountConfig_ParseDword(Option, &usValue,
2885                 &Config->ReadSize, MOUNT_CONFIG_RW_SIZE_MIN,
2886                 MOUNT_CONFIG_RW_SIZE_MAX);
2887         }
2888         else if (wcsncmp(L"wsize", Name, NameLen) == 0) {
2889             status = nfs41_MountConfig_ParseDword(Option, &usValue,
2890                 &Config->WriteSize, MOUNT_CONFIG_RW_SIZE_MIN,
2891                 MOUNT_CONFIG_RW_SIZE_MAX);
2892         }
2893         else if (wcsncmp(L"srvname", Name, NameLen) == 0) {
2894             if (usValue.Length > Config->SrvName.MaximumLength)
2895                 status = STATUS_NAME_TOO_LONG;
2896             else
2897                 RtlCopyUnicodeString(&Config->SrvName, &usValue);
2898         }
2899         else if (wcsncmp(L"mntpt", Name, NameLen) == 0) {
2900             if (usValue.Length > Config->MntPt.MaximumLength)
2901                 status = STATUS_NAME_TOO_LONG;
2902             else
2903                 RtlCopyUnicodeString(&Config->MntPt, &usValue);
2904         }
2905         else if (wcsncmp(L"sec", Name, NameLen) == 0) {
2906             if (usValue.Length > Config->SecFlavor.MaximumLength)
2907                 status = STATUS_NAME_TOO_LONG;
2908             else
2909                 RtlCopyUnicodeString(&Config->SecFlavor, &usValue);
2910         }
2911         else {
2912             status = STATUS_INVALID_PARAMETER;
2913             print_error("Unrecognized option '%ls' -> '%wZ'\n",
2914                 Name, usValue);
2915         }
2916 
2917         if (Option->NextEntryOffset == 0)
2918             break;
2919 
2920         Option = (PFILE_FULL_EA_INFORMATION)
2921             ((PBYTE)Option + Option->NextEntryOffset);
2922     }
2923 
2924     return status;
2925 }
2926 
2927 NTSTATUS has_nfs_prefix(
2928     IN PUNICODE_STRING SrvCallName,
2929     IN PUNICODE_STRING NetRootName)
2930 {
2931     NTSTATUS status = STATUS_BAD_NETWORK_NAME;
2932 
2933     if (NetRootName->Length == SrvCallName->Length + NfsPrefix.Length) {
2934         const UNICODE_STRING NetRootPrefix = {
2935             NfsPrefix.Length,
2936             NetRootName->MaximumLength - SrvCallName->Length,
2937             &NetRootName->Buffer[SrvCallName->Length/2]
2938         };
2939         if (RtlCompareUnicodeString(&NetRootPrefix, &NfsPrefix, FALSE) == 0)
2940             status = STATUS_SUCCESS;
2941     }
2942     return status;
2943 }
2944 
2945 NTSTATUS map_sec_flavor(
2946     IN PUNICODE_STRING sec_flavor_name,
2947     OUT PDWORD sec_flavor)
2948 {
2949     if (RtlCompareUnicodeString(sec_flavor_name, &AUTH_SYS_NAME, FALSE) == 0)
2950         *sec_flavor = RPCSEC_AUTH_SYS;
2951     else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5_NAME, FALSE) == 0)
2952         *sec_flavor = RPCSEC_AUTHGSS_KRB5;
2953     else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5I_NAME, FALSE) == 0)
2954         *sec_flavor = RPCSEC_AUTHGSS_KRB5I;
2955     else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5P_NAME, FALSE) == 0)
2956         *sec_flavor = RPCSEC_AUTHGSS_KRB5P;
2957     else return STATUS_INVALID_PARAMETER;
2958     return STATUS_SUCCESS;
2959 }
2960 
2961 NTSTATUS nfs41_GetLUID(
2962     PLUID id)
2963 {
2964     NTSTATUS status = STATUS_SUCCESS;
2965     SECURITY_SUBJECT_CONTEXT sec_ctx;
2966     SECURITY_QUALITY_OF_SERVICE sec_qos;
2967     SECURITY_CLIENT_CONTEXT clnt_sec_ctx;
2968 
2969     SeCaptureSubjectContext(&sec_ctx);
2970     sec_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
2971     sec_qos.ImpersonationLevel = SecurityIdentification;
2972     sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
2973     sec_qos.EffectiveOnly = 0;
2974     status = SeCreateClientSecurityFromSubjectContext(&sec_ctx, &sec_qos, 1,
2975                 &clnt_sec_ctx);
2976     if (status) {
2977         print_error("nfs41_GetLUID: SeCreateClientSecurityFromSubjectContext "
2978              "failed %x\n", status);
2979         goto release_sec_ctx;
2980     }
2981     status = SeQueryAuthenticationIdToken(clnt_sec_ctx.ClientToken, id);
2982     if (status) {
2983         print_error("SeQueryAuthenticationIdToken failed %x\n", status);
2984         goto release_clnt_sec_ctx;
2985     }
2986 release_clnt_sec_ctx:
2987     SeDeleteClientSecurity(&clnt_sec_ctx);
2988 release_sec_ctx:
2989     SeReleaseSubjectContext(&sec_ctx);
2990 
2991     return status;
2992 }
2993 
2994 NTSTATUS nfs41_get_sec_ctx(
2995     IN enum _SECURITY_IMPERSONATION_LEVEL level,
2996     OUT PSECURITY_CLIENT_CONTEXT out_ctx)
2997 {
2998     NTSTATUS status;
2999     SECURITY_SUBJECT_CONTEXT ctx;
3000     SECURITY_QUALITY_OF_SERVICE sec_qos;
3001 
3002     SeCaptureSubjectContext(&ctx);
3003     sec_qos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
3004     sec_qos.ImpersonationLevel = level;
3005     sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
3006     sec_qos.EffectiveOnly = 0;
3007     status = SeCreateClientSecurityFromSubjectContext(&ctx, &sec_qos, 1, out_ctx);
3008     if (status != STATUS_SUCCESS) {
3009         print_error("SeCreateClientSecurityFromSubjectContext "
3010             "failed with %x\n", status);
3011     }
3012 #ifdef DEBUG_MOUNT
3013     DbgP("Created client security token %p\n", out_ctx->ClientToken);
3014 #endif
3015     SeReleaseSubjectContext(&ctx);
3016 
3017     return status;
3018 }
3019 
3020 #ifdef __REACTOS__
3021 NTSTATUS NTAPI nfs41_CreateVNetRoot(
3022 #else
3023 NTSTATUS nfs41_CreateVNetRoot(
3024 #endif
3025     IN OUT PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext)
3026 {
3027     NTSTATUS status = STATUS_SUCCESS;
3028     NFS41_MOUNT_CONFIG *Config;
3029     __notnull PMRX_V_NET_ROOT pVNetRoot = (PMRX_V_NET_ROOT)
3030         pCreateNetRootContext->pVNetRoot;
3031     __notnull PMRX_NET_ROOT pNetRoot = pVNetRoot->pNetRoot;
3032     __notnull PMRX_SRV_CALL pSrvCall = pNetRoot->pSrvCall;
3033     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
3034         NFS41GetVNetRootExtension(pVNetRoot);
3035     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
3036         NFS41GetNetRootExtension(pNetRoot);
3037     NFS41GetDeviceExtension(pCreateNetRootContext->RxContext,DevExt);
3038     DWORD nfs41d_version = DevExt->nfs41d_version;
3039     nfs41_mount_entry *existing_mount = NULL;
3040     LUID luid;
3041     BOOLEAN found_existing_mount = FALSE, found_matching_flavor = FALSE;
3042 
3043     ASSERT((NodeType(pNetRoot) == RDBSS_NTC_NETROOT) &&
3044         (NodeType(pNetRoot->pSrvCall) == RDBSS_NTC_SRVCALL));
3045 
3046 #ifdef DEBUG_MOUNT
3047     DbgEn();
3048     print_srv_call(0, pSrvCall);
3049     print_net_root(0, pNetRoot);
3050     print_v_net_root(0, pVNetRoot);
3051 
3052     DbgP("pVNetRoot=%p pNetRoot=%p pSrvCall=%p\n", pVNetRoot, pNetRoot, pSrvCall);
3053     DbgP("pNetRoot=%wZ Type=%d pSrvCallName=%wZ VirtualNetRootStatus=0x%x "
3054         "NetRootStatus=0x%x\n", pNetRoot->pNetRootName,
3055         pNetRoot->Type, pSrvCall->pSrvCallName,
3056         pCreateNetRootContext->VirtualNetRootStatus,
3057         pCreateNetRootContext->NetRootStatus);
3058 #endif
3059 
3060     if (pNetRoot->Type != NET_ROOT_DISK && pNetRoot->Type != NET_ROOT_WILD) {
3061         print_error("nfs41_CreateVNetRoot: Unsupported NetRoot Type %u\n",
3062             pNetRoot->Type);
3063         status = STATUS_NOT_SUPPORTED;
3064         goto out;
3065     }
3066 
3067     pVNetRootContext->session = INVALID_HANDLE_VALUE;
3068 
3069     /* In order to cooperate with other network providers, we must
3070      * only claim paths of the form '\\server\nfs4\path' */
3071     status = has_nfs_prefix(pSrvCall->pSrvCallName, pNetRoot->pNetRootName);
3072     if (status) {
3073         print_error("nfs41_CreateVNetRoot: NetRootName %wZ doesn't match "
3074             "'\\nfs4'!\n", pNetRoot->pNetRootName);
3075         goto out;
3076     }
3077     pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_GOOD;
3078     pNetRoot->DeviceType = FILE_DEVICE_DISK;
3079 
3080     Config = RxAllocatePoolWithTag(NonPagedPool,
3081             sizeof(NFS41_MOUNT_CONFIG), NFS41_MM_POOLTAG);
3082     if (Config == NULL) {
3083         status = STATUS_INSUFFICIENT_RESOURCES;
3084         goto out;
3085     }
3086     nfs41_MountConfig_InitDefaults(Config);
3087 
3088     if (pCreateNetRootContext->RxContext->Create.EaLength) {
3089         /* parse the extended attributes for mount options */
3090         status = nfs41_MountConfig_ParseOptions(
3091             pCreateNetRootContext->RxContext->Create.EaBuffer,
3092             pCreateNetRootContext->RxContext->Create.EaLength,
3093             Config);
3094         if (status != STATUS_SUCCESS)
3095             goto out_free;
3096         pVNetRootContext->read_only = Config->ReadOnly;
3097         pVNetRootContext->write_thru = Config->write_thru;
3098         pVNetRootContext->nocache = Config->nocache;
3099     } else {
3100         /* use the SRV_CALL name (without leading \) as the hostname */
3101         Config->SrvName.Buffer = pSrvCall->pSrvCallName->Buffer + 1;
3102         Config->SrvName.Length =
3103             pSrvCall->pSrvCallName->Length - sizeof(WCHAR);
3104         Config->SrvName.MaximumLength =
3105             pSrvCall->pSrvCallName->MaximumLength - sizeof(WCHAR);
3106     }
3107     pVNetRootContext->MountPathLen = Config->MntPt.Length;
3108     pVNetRootContext->timeout = Config->timeout;
3109 
3110     status = map_sec_flavor(&Config->SecFlavor, &pVNetRootContext->sec_flavor);
3111     if (status != STATUS_SUCCESS) {
3112         DbgP("Invalid rpcsec security flavor %wZ\n", &Config->SecFlavor);
3113         goto out_free;
3114     }
3115 
3116     status = nfs41_GetLUID(&luid);
3117     if (status)
3118         goto out_free;
3119 
3120     if (!pNetRootContext->mounts_init) {
3121 #ifdef DEBUG_MOUNT
3122         DbgP("Initializing mount array\n");
3123 #endif
3124         ExInitializeFastMutex(&pNetRootContext->mountLock);
3125         InitializeListHead(&pNetRootContext->mounts.head);
3126         pNetRootContext->mounts_init = TRUE;
3127     } else {
3128         PLIST_ENTRY pEntry;
3129 
3130         ExAcquireFastMutex(&pNetRootContext->mountLock);
3131         pEntry = &pNetRootContext->mounts.head;
3132         pEntry = pEntry->Flink;
3133         while (pEntry != NULL) {
3134             existing_mount = (nfs41_mount_entry *)CONTAINING_RECORD(pEntry,
3135                     nfs41_mount_entry, next);
3136 #ifdef DEBUG_MOUNT
3137             DbgP("comparing %x.%x with %x.%x\n", luid.HighPart, luid.LowPart,
3138                 existing_mount->login_id.HighPart, existing_mount->login_id.LowPart);
3139 #endif
3140             if (RtlEqualLuid(&luid, &existing_mount->login_id)) {
3141 #ifdef DEBUG_MOUNT
3142                 DbgP("Found a matching LUID entry\n");
3143 #endif
3144                 found_existing_mount = TRUE;
3145                 switch(pVNetRootContext->sec_flavor) {
3146                 case RPCSEC_AUTH_SYS:
3147                     if (existing_mount->authsys_session != INVALID_HANDLE_VALUE)
3148                         pVNetRootContext->session =
3149                             existing_mount->authsys_session;
3150                     break;
3151                 case RPCSEC_AUTHGSS_KRB5:
3152                     if (existing_mount->gssi_session != INVALID_HANDLE_VALUE)
3153                         pVNetRootContext->session = existing_mount->gss_session;
3154                     break;
3155                 case RPCSEC_AUTHGSS_KRB5I:
3156                     if (existing_mount->gss_session != INVALID_HANDLE_VALUE)
3157                         pVNetRootContext->session = existing_mount->gssi_session;
3158                     break;
3159                 case RPCSEC_AUTHGSS_KRB5P:
3160                     if (existing_mount->gssp_session != INVALID_HANDLE_VALUE)
3161                         pVNetRootContext->session = existing_mount->gssp_session;
3162                     break;
3163                 }
3164                 if (pVNetRootContext->session &&
3165                         pVNetRootContext->session != INVALID_HANDLE_VALUE)
3166                     found_matching_flavor = 1;
3167                 break;
3168             }
3169             if (pEntry->Flink == &pNetRootContext->mounts.head)
3170                 break;
3171             pEntry = pEntry->Flink;
3172         }
3173         ExReleaseFastMutex(&pNetRootContext->mountLock);
3174 #ifdef DEBUG_MOUNT
3175         if (!found_matching_flavor)
3176             DbgP("Didn't find matching security flavor\n");
3177 #endif
3178     }
3179 
3180     /* send the mount upcall */
3181     status = nfs41_mount(Config, pVNetRootContext->sec_flavor,
3182         &pVNetRootContext->session, &nfs41d_version,
3183         &pVNetRootContext->FsAttrs);
3184     if (status != STATUS_SUCCESS) {
3185         BOOLEAN MountsEmpty;
3186         nfs41_IsListEmpty(pNetRootContext->mountLock,
3187             pNetRootContext->mounts, MountsEmpty);
3188         if (!found_existing_mount && MountsEmpty)
3189             pNetRootContext->mounts_init = FALSE;
3190         pVNetRootContext->session = INVALID_HANDLE_VALUE;
3191         goto out_free;
3192     }
3193     pVNetRootContext->timeout = Config->timeout;
3194 
3195     if (!found_existing_mount) {
3196         /* create a new mount entry and add it to the list */
3197         nfs41_mount_entry *entry;
3198         entry = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_mount_entry),
3199             NFS41_MM_POOLTAG_MOUNT);
3200         if (entry == NULL) {
3201             status = STATUS_INSUFFICIENT_RESOURCES;
3202             goto out_free;
3203         }
3204         entry->authsys_session = entry->gss_session =
3205             entry->gssi_session = entry->gssp_session = INVALID_HANDLE_VALUE;
3206         switch (pVNetRootContext->sec_flavor) {
3207         case RPCSEC_AUTH_SYS:
3208             entry->authsys_session = pVNetRootContext->session; break;
3209         case RPCSEC_AUTHGSS_KRB5:
3210             entry->gss_session = pVNetRootContext->session; break;
3211         case RPCSEC_AUTHGSS_KRB5I:
3212             entry->gssi_session = pVNetRootContext->session; break;
3213         case RPCSEC_AUTHGSS_KRB5P:
3214             entry->gssp_session = pVNetRootContext->session; break;
3215         }
3216         RtlCopyLuid(&entry->login_id, &luid);
3217         nfs41_AddEntry(pNetRootContext->mountLock,
3218             pNetRootContext->mounts, entry);
3219     } else if (!found_matching_flavor) {
3220         ASSERT(existing_mount != NULL);
3221         /* modify existing mount entry */
3222 #ifdef DEBUG_MOUNT
3223         DbgP("Using existing %d flavor session 0x%x\n",
3224             pVNetRootContext->sec_flavor);
3225 #endif
3226         switch (pVNetRootContext->sec_flavor) {
3227         case RPCSEC_AUTH_SYS:
3228             existing_mount->authsys_session = pVNetRootContext->session; break;
3229         case RPCSEC_AUTHGSS_KRB5:
3230             existing_mount->gss_session = pVNetRootContext->session; break;
3231         case RPCSEC_AUTHGSS_KRB5I:
3232             existing_mount->gssi_session = pVNetRootContext->session; break;
3233         case RPCSEC_AUTHGSS_KRB5P:
3234             existing_mount->gssp_session = pVNetRootContext->session; break;
3235         }
3236     }
3237     pNetRootContext->nfs41d_version = nfs41d_version;
3238 #ifdef DEBUG_MOUNT
3239     DbgP("Saving new session 0x%x\n", pVNetRootContext->session);
3240 #endif
3241 #ifdef STORE_MOUNT_SEC_CONTEXT
3242     status = nfs41_get_sec_ctx(SecurityImpersonation,
3243         &pVNetRootContext->mount_sec_ctx);
3244 #endif
3245 
3246 out_free:
3247     RxFreePool(Config);
3248 out:
3249     pCreateNetRootContext->VirtualNetRootStatus = status;
3250     if (pNetRoot->Context == NULL)
3251         pCreateNetRootContext->NetRootStatus = status;
3252     pCreateNetRootContext->Callback(pCreateNetRootContext);
3253 
3254     /* RDBSS expects that MRxCreateVNetRoot returns STATUS_PENDING
3255      * on success or failure */
3256     status = STATUS_PENDING;
3257 #ifdef DEBUG_MOUNT
3258     DbgEx();
3259 #endif
3260     return status;
3261 }
3262 
3263 #ifdef __REACTOS__
3264 VOID NTAPI nfs41_ExtractNetRootName(
3265 #else
3266 VOID nfs41_ExtractNetRootName(
3267 #endif
3268     IN PUNICODE_STRING FilePathName,
3269     IN PMRX_SRV_CALL SrvCall,
3270     OUT PUNICODE_STRING NetRootName,
3271     OUT PUNICODE_STRING RestOfName OPTIONAL)
3272 {
3273     ULONG length = FilePathName->Length;
3274     PWCH w = FilePathName->Buffer;
3275     PWCH wlimit = (PWCH)(((PCHAR)w)+length);
3276     PWCH wlow;
3277 
3278     w += (SrvCall->pSrvCallName->Length/sizeof(WCHAR));
3279     NetRootName->Buffer = wlow = w;
3280     /* parse the entire path into NetRootName */
3281 #if USE_ENTIRE_PATH
3282     w = wlimit;
3283 #else
3284     for (;;) {
3285         if (w >= wlimit)
3286             break;
3287         if ((*w == OBJ_NAME_PATH_SEPARATOR) && (w != wlow))
3288             break;
3289         w++;
3290     }
3291 #endif
3292     NetRootName->Length = NetRootName->MaximumLength
3293                 = (USHORT)((PCHAR)w - (PCHAR)wlow);
3294 #ifdef DEBUG_MOUNT
3295     DbgP("In: pSrvCall %p PathName=%wZ SrvCallName=%wZ Out: NetRootName=%wZ\n",
3296         SrvCall, FilePathName, SrvCall->pSrvCallName, NetRootName);
3297 #endif
3298     return;
3299 
3300 }
3301 
3302 #ifdef __REACTOS__
3303 NTSTATUS NTAPI nfs41_FinalizeSrvCall(
3304 #else
3305 NTSTATUS nfs41_FinalizeSrvCall(
3306 #endif
3307     PMRX_SRV_CALL pSrvCall,
3308     BOOLEAN Force)
3309 {
3310     NTSTATUS status = STATUS_SUCCESS;
3311     PNFS41_SERVER_ENTRY pServerEntry = (PNFS41_SERVER_ENTRY)(pSrvCall->Context);
3312 
3313 #ifdef DEBUG_MOUNT
3314     DbgEn();
3315 #endif
3316     print_srv_call(0, pSrvCall);
3317 
3318     if (pSrvCall->Context == NULL)
3319         goto out;
3320 
3321 #ifndef __REACTOS__
3322     InterlockedCompareExchangePointer(&pServerEntry->pRdbssSrvCall,
3323         NULL, pSrvCall);
3324 #else
3325     InterlockedCompareExchangePointer((void * volatile *)&pServerEntry->pRdbssSrvCall,
3326         NULL, pSrvCall);
3327 #endif
3328     RxFreePool(pServerEntry);
3329 
3330     pSrvCall->Context = NULL;
3331 out:
3332 #ifdef DEBUG_MOUNT
3333     DbgEx();
3334 #endif
3335     return status;
3336 }
3337 
3338 #ifdef __REACTOS__
3339 NTSTATUS NTAPI nfs41_FinalizeNetRoot(
3340 #else
3341 NTSTATUS nfs41_FinalizeNetRoot(
3342 #endif
3343     IN OUT PMRX_NET_ROOT pNetRoot,
3344     IN PBOOLEAN ForceDisconnect)
3345 {
3346     NTSTATUS status = STATUS_SUCCESS;
3347     PNFS41_NETROOT_EXTENSION pNetRootContext =
3348         NFS41GetNetRootExtension((PMRX_NET_ROOT)pNetRoot);
3349     nfs41_updowncall_entry *tmp;
3350     nfs41_mount_entry *mount_tmp;
3351 
3352 #ifdef DEBUG_MOUNT
3353     DbgEn();
3354     print_net_root(1, pNetRoot);
3355 #endif
3356 
3357     if (pNetRoot->Type != NET_ROOT_DISK && pNetRoot->Type != NET_ROOT_WILD) {
3358         status = STATUS_NOT_SUPPORTED;
3359         goto out;
3360     }
3361 
3362     if (pNetRootContext == NULL || !pNetRootContext->mounts_init) {
3363         print_error("nfs41_FinalizeNetRoot: No valid session established\n");
3364         goto out;
3365     }
3366 
3367     if (pNetRoot->NumberOfFcbs > 0 || pNetRoot->NumberOfSrvOpens > 0) {
3368         print_error("%d open Fcbs %d open SrvOpens\n", pNetRoot->NumberOfFcbs,
3369             pNetRoot->NumberOfSrvOpens);
3370         goto out;
3371     }
3372 
3373     do {
3374         nfs41_GetFirstMountEntry(pNetRootContext->mountLock,
3375             pNetRootContext->mounts, mount_tmp);
3376         if (mount_tmp == NULL)
3377             break;
3378 #ifdef DEBUG_MOUNT
3379         DbgP("Removing entry luid %x.%x from mount list\n",
3380             mount_tmp->login_id.HighPart, mount_tmp->login_id.LowPart);
3381 #endif
3382         if (mount_tmp->authsys_session != INVALID_HANDLE_VALUE) {
3383             status = nfs41_unmount(mount_tmp->authsys_session,
3384                 pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
3385             if (status)
3386                 print_error("nfs41_unmount AUTH_SYS failed with %d\n", status);
3387         }
3388         if (mount_tmp->gss_session != INVALID_HANDLE_VALUE) {
3389             status = nfs41_unmount(mount_tmp->gss_session,
3390                 pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
3391             if (status)
3392                 print_error("nfs41_unmount RPCSEC_GSS_KRB5 failed with %d\n",
3393                             status);
3394         }
3395         if (mount_tmp->gssi_session != INVALID_HANDLE_VALUE) {
3396             status = nfs41_unmount(mount_tmp->gssi_session,
3397                 pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
3398             if (status)
3399                 print_error("nfs41_unmount RPCSEC_GSS_KRB5I failed with %d\n",
3400                             status);
3401         }
3402         if (mount_tmp->gssp_session != INVALID_HANDLE_VALUE) {
3403             status = nfs41_unmount(mount_tmp->gssp_session,
3404                 pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
3405             if (status)
3406                 print_error("nfs41_unmount RPCSEC_GSS_KRB5P failed with %d\n",
3407                             status);
3408         }
3409         nfs41_RemoveEntry(pNetRootContext->mountLock, mount_tmp);
3410         RxFreePool(mount_tmp);
3411     } while (1);
3412     /* ignore any errors from unmount */
3413     status = STATUS_SUCCESS;
3414 
3415     // check if there is anything waiting in the upcall or downcall queue
3416     do {
3417         nfs41_GetFirstEntry(upcallLock, upcall, tmp);
3418         if (tmp != NULL) {
3419             DbgP("Removing entry from upcall list\n");
3420             nfs41_RemoveEntry(upcallLock, tmp);
3421             tmp->status = STATUS_INSUFFICIENT_RESOURCES;
3422             KeSetEvent(&tmp->cond, 0, FALSE);
3423         } else
3424             break;
3425     } while (1);
3426 
3427     do {
3428         nfs41_GetFirstEntry(downcallLock, downcall, tmp);
3429         if (tmp != NULL) {
3430             DbgP("Removing entry from downcall list\n");
3431             nfs41_RemoveEntry(downcallLock, tmp);
3432             tmp->status = STATUS_INSUFFICIENT_RESOURCES;
3433             KeSetEvent(&tmp->cond, 0, FALSE);
3434         } else
3435             break;
3436     } while (1);
3437 out:
3438 #ifdef DEBUG_MOUNT
3439     DbgEx();
3440 #endif
3441     return status;
3442 }
3443 
3444 #ifdef __REACTOS__
3445 NTSTATUS NTAPI nfs41_FinalizeVNetRoot(
3446 #else
3447 NTSTATUS nfs41_FinalizeVNetRoot(
3448 #endif
3449     IN OUT PMRX_V_NET_ROOT pVNetRoot,
3450     IN PBOOLEAN ForceDisconnect)
3451 {
3452     NTSTATUS status = STATUS_SUCCESS;
3453     PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
3454         NFS41GetVNetRootExtension(pVNetRoot);
3455 #ifdef DEBUG_MOUNT
3456     DbgEn();
3457     print_v_net_root(1, pVNetRoot);
3458 #endif
3459     if (pVNetRoot->pNetRoot->Type != NET_ROOT_DISK &&
3460             pVNetRoot->pNetRoot->Type != NET_ROOT_WILD)
3461         status = STATUS_NOT_SUPPORTED;
3462 #ifdef STORE_MOUNT_SEC_CONTEXT
3463     else if (pVNetRootContext->session != INVALID_HANDLE_VALUE) {
3464 #ifdef DEBUG_MOUNT
3465         DbgP("nfs41_FinalizeVNetRoot: deleting security context: %p\n",
3466             pVNetRootContext->mount_sec_ctx.ClientToken);
3467 #endif
3468         SeDeleteClientSecurity(&pVNetRootContext->mount_sec_ctx);
3469     }
3470 #endif
3471 #ifdef DEBUG_MOUNT
3472     DbgEx();
3473 #endif
3474     return status;
3475 }
3476 
3477 BOOLEAN isDataAccess(
3478     ACCESS_MASK mask)
3479 {
3480     if (mask & (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA))
3481         return TRUE;
3482     return FALSE;
3483 }
3484 
3485 BOOLEAN isOpen2Create(
3486     ULONG disposition)
3487 {
3488     if (disposition == FILE_CREATE || disposition == FILE_OPEN_IF ||
3489             disposition == FILE_OVERWRITE_IF || disposition == FILE_SUPERSEDE)
3490         return TRUE;
3491     return FALSE;
3492 }
3493 
3494 BOOLEAN isFilenameTooLong(
3495     PUNICODE_STRING name,
3496     PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext)
3497 {
3498     PFILE_FS_ATTRIBUTE_INFORMATION attrs = &pVNetRootContext->FsAttrs;
3499     LONG len = attrs->MaximumComponentNameLength, count = 1, i;
3500     PWCH p = name->Buffer;
3501     for (i = 0; i < name->Length / 2; i++) {
3502         if (p[0] == L'\\') count = 1;
3503         else {
3504             if (p[0] == L'\0') return FALSE;
3505             if (count > len) return TRUE;
3506             count++;
3507         }
3508         p++;
3509     }
3510     return FALSE;
3511 }
3512 
3513 BOOLEAN isStream(
3514     PUNICODE_STRING name)
3515 {
3516     LONG i;
3517     PWCH p = name->Buffer;
3518     for (i = 0; i < name->Length / 2; i++) {
3519         if (p[0] == L':') return TRUE;
3520         else if (p[0] == L'\0') return FALSE;
3521         p++;
3522     }
3523     return FALSE;
3524 }
3525 
3526 BOOLEAN areOpenParamsValid(NT_CREATE_PARAMETERS *params)
3527 {
3528     /* from ms-fsa page 52 */
3529     if ((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
3530             !(params->DesiredAccess & DELETE))
3531         return FALSE;
3532     if ((params->CreateOptions & FILE_DIRECTORY_FILE) &&
3533             (params->Disposition == FILE_SUPERSEDE ||
3534                 params->Disposition == FILE_OVERWRITE ||
3535                 params->Disposition == FILE_OVERWRITE_IF))
3536         return FALSE;
3537     if ((params->CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING) &&
3538             (params->DesiredAccess & FILE_APPEND_DATA) &&
3539             !(params->DesiredAccess & FILE_WRITE_DATA))
3540         return FALSE;
3541     /* from ms-fsa 3.1.5.1.1 page 56 */
3542     if ((params->CreateOptions & FILE_DIRECTORY_FILE) &&
3543             (params->FileAttributes & FILE_ATTRIBUTE_TEMPORARY))
3544         return FALSE;
3545     return TRUE;
3546 }
3547 
3548 NTSTATUS map_open_errors(
3549     DWORD status,
3550     USHORT len)
3551 {
3552     switch (status) {
3553     case NO_ERROR:                      return STATUS_SUCCESS;
3554     case ERROR_ACCESS_DENIED:
3555         if (len > 0)                    return STATUS_ACCESS_DENIED;
3556         else                            return STATUS_SUCCESS;
3557     case ERROR_INVALID_REPARSE_DATA:
3558     case ERROR_INVALID_NAME:            return STATUS_OBJECT_NAME_INVALID;
3559     case ERROR_FILE_EXISTS:             return STATUS_OBJECT_NAME_COLLISION;
3560     case ERROR_FILE_INVALID:            return STATUS_FILE_INVALID;
3561     case ERROR_FILE_NOT_FOUND:          return STATUS_OBJECT_NAME_NOT_FOUND;
3562     case ERROR_FILENAME_EXCED_RANGE:    return STATUS_NAME_TOO_LONG;
3563     case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
3564     case ERROR_PATH_NOT_FOUND:          return STATUS_OBJECT_PATH_NOT_FOUND;
3565     case ERROR_BAD_NETPATH:             return STATUS_BAD_NETWORK_PATH;
3566     case ERROR_SHARING_VIOLATION:       return STATUS_SHARING_VIOLATION;
3567     case ERROR_REPARSE:                 return STATUS_REPARSE;
3568     case ERROR_TOO_MANY_LINKS:          return STATUS_TOO_MANY_LINKS;
3569     case ERROR_DIRECTORY:               return STATUS_FILE_IS_A_DIRECTORY;
3570     case ERROR_BAD_FILE_TYPE:           return STATUS_NOT_A_DIRECTORY;
3571     default:
3572         print_error("[ERROR] nfs41_Create: upcall returned %d returning "
3573             "STATUS_INSUFFICIENT_RESOURCES\n", status);
3574     case ERROR_OUTOFMEMORY:             return STATUS_INSUFFICIENT_RESOURCES;
3575     }
3576 }
3577 
3578 DWORD map_disposition_to_create_retval(
3579     DWORD disposition,
3580     DWORD errno)
3581 {
3582     switch(disposition) {
3583     case FILE_SUPERSEDE:
3584         if (errno == ERROR_FILE_NOT_FOUND)  return FILE_CREATED;
3585         else                                return FILE_SUPERSEDED;
3586     case FILE_CREATE:                       return FILE_CREATED;
3587     case FILE_OPEN:                         return FILE_OPENED;
3588     case FILE_OPEN_IF:
3589         if (errno == ERROR_FILE_NOT_FOUND)  return FILE_CREATED;
3590         else                                return FILE_OPENED;
3591     case FILE_OVERWRITE:                    return FILE_OVERWRITTEN;
3592     case FILE_OVERWRITE_IF:
3593         if (errno == ERROR_FILE_NOT_FOUND)  return FILE_CREATED;
3594         else                                return FILE_OVERWRITTEN;
3595     default:
3596         print_error("unknown disposition %d\n", disposition);
3597         return FILE_OPENED;
3598     }
3599 }
3600 
3601 static BOOLEAN create_should_pass_ea(
3602     IN PFILE_FULL_EA_INFORMATION ea,
3603     IN ULONG disposition)
3604 {
3605     /* don't pass cygwin EAs */
3606     if (AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)
3607         || AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength)
3608         || AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength))
3609         return FALSE;
3610     /* only set EAs on file creation */
3611     return disposition == FILE_SUPERSEDE || disposition == FILE_CREATE
3612         || disposition == FILE_OPEN_IF || disposition == FILE_OVERWRITE
3613         || disposition == FILE_OVERWRITE_IF;
3614 }
3615 
3616 NTSTATUS check_nfs41_create_args(
3617     IN PRX_CONTEXT RxContext)
3618 {
3619     NTSTATUS status = STATUS_SUCCESS;
3620     PNT_CREATE_PARAMETERS params = &RxContext->Create.NtCreateParameters;
3621     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
3622     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
3623         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
3624     __notnull PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs =
3625         &pVNetRootContext->FsAttrs;
3626     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
3627         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
3628     __notnull PMRX_FCB Fcb = RxContext->pFcb;
3629     __notnull PNFS41_FCB nfs41_fcb = (PNFS41_FCB)Fcb->Context;
3630     PFILE_FULL_EA_INFORMATION ea = (PFILE_FULL_EA_INFORMATION)
3631         RxContext->CurrentIrp->AssociatedIrp.SystemBuffer;
3632 
3633     if (Fcb->pNetRoot->Type != NET_ROOT_DISK &&
3634             Fcb->pNetRoot->Type != NET_ROOT_WILD) {
3635         print_error("nfs41_Create: Unsupported NetRoot Type %u\n",
3636             Fcb->pNetRoot->Type);
3637         status = STATUS_NOT_SUPPORTED;
3638         goto out;
3639     }
3640 
3641     if (FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
3642         print_error("FCB_STATE_PAGING_FILE not implemented\n");
3643         status = STATUS_NOT_IMPLEMENTED;
3644         goto out;
3645     }
3646 
3647     if (!pNetRootContext->mounts_init) {
3648         print_error("nfs41_Create: No valid session established\n");
3649         status = STATUS_INSUFFICIENT_RESOURCES;
3650         goto out;
3651     }
3652 
3653     if (isStream(SrvOpen->pAlreadyPrefixedName)) {
3654         status = STATUS_NOT_SUPPORTED;
3655         goto out;
3656     }
3657 
3658     if (pVNetRootContext->read_only &&
3659             (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
3660         status = STATUS_NETWORK_ACCESS_DENIED;
3661         goto out;
3662     }
3663 
3664     /* if FCB was marked for deletion and opened multiple times, as soon
3665      * as first close happen, FCB transitions into delete_pending state
3666      * no more opens allowed
3667      */
3668     if (Fcb->OpenCount && nfs41_fcb->DeletePending) {
3669         status = STATUS_DELETE_PENDING;
3670         goto out;
3671     }
3672 
3673     /* ms-fsa: 3.1.5.1.2.1 page 68 */
3674     if (Fcb->OpenCount && nfs41_fcb->StandardInfo.DeletePending &&
3675             !(params->ShareAccess & FILE_SHARE_DELETE) &&
3676                 (params->DesiredAccess & (FILE_EXECUTE | FILE_READ_DATA |
3677                     FILE_WRITE_DATA | FILE_APPEND_DATA))) {
3678         status = STATUS_SHARING_VIOLATION;
3679         goto out;
3680     }
3681 
3682     /* rdbss seems miss this sharing_violation check */
3683     if (Fcb->OpenCount && params->Disposition == FILE_SUPERSEDE) {
3684 #ifdef __REACTOS__
3685         if ((!RxContext->CurrentIrpSp->FileObject->SharedRead &&
3686                 (params->DesiredAccess & FILE_READ_DATA)) ||
3687             ((!RxContext->CurrentIrpSp->FileObject->SharedWrite &&
3688                 (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA |
3689                     FILE_WRITE_ATTRIBUTES))) ||
3690             (!RxContext->CurrentIrpSp->FileObject->SharedDelete &&
3691                 (params->DesiredAccess & DELETE)))) {
3692 #else
3693         if ((!RxContext->CurrentIrpSp->FileObject->SharedRead &&
3694                 (params->DesiredAccess & FILE_READ_DATA)) ||
3695             (!RxContext->CurrentIrpSp->FileObject->SharedWrite &&
3696                 (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA |
3697                     FILE_WRITE_ATTRIBUTES)) ||
3698             (!RxContext->CurrentIrpSp->FileObject->SharedDelete &&
3699                 (params->DesiredAccess & DELETE)))) {
3700 #endif
3701             status = STATUS_SHARING_VIOLATION;
3702             goto out;
3703         }
3704     }
3705     if (isFilenameTooLong(SrvOpen->pAlreadyPrefixedName, pVNetRootContext)) {
3706         status = STATUS_OBJECT_NAME_INVALID;
3707         goto out;
3708     }
3709 
3710     if (!areOpenParamsValid(params)) {
3711         status = STATUS_INVALID_PARAMETER;
3712         goto out;
3713     }
3714 
3715     /* from ms-fsa 3.1.5.1.1 page 56 */
3716     if ((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
3717             (params->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
3718         status = STATUS_CANNOT_DELETE;
3719         goto out;
3720     }
3721 
3722     if (ea) {
3723         /* ignore cygwin EAs when checking support and access */
3724         if (!AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength) &&
3725             !AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength) &&
3726             !AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
3727             if (!(FsAttrs->FileSystemAttributes & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)) {
3728                 status = STATUS_EAS_NOT_SUPPORTED;
3729                 goto out;
3730             }
3731         }
3732     } else if (RxContext->CurrentIrpSp->Parameters.Create.EaLength) {
3733         status = STATUS_INVALID_PARAMETER;
3734         goto out;
3735     }
3736 
3737 out:
3738     return status;
3739 }
3740 
3741 #ifdef __REACTOS__
3742 NTSTATUS NTAPI nfs41_Create(
3743 #else
3744 NTSTATUS nfs41_Create(
3745 #endif
3746     IN OUT PRX_CONTEXT RxContext)
3747 {
3748     NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
3749     nfs41_updowncall_entry *entry = NULL;
3750     PNT_CREATE_PARAMETERS params = &RxContext->Create.NtCreateParameters;
3751     PFILE_FULL_EA_INFORMATION ea = (PFILE_FULL_EA_INFORMATION)
3752         RxContext->CurrentIrp->AssociatedIrp.SystemBuffer;
3753     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
3754     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
3755         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
3756     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
3757         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
3758     __notnull PMRX_FCB Fcb = RxContext->pFcb;
3759     __notnull PNFS41_FCB nfs41_fcb = (PNFS41_FCB)Fcb->Context;
3760     PNFS41_FOBX nfs41_fobx = NULL;
3761     BOOLEAN oldDeletePending = nfs41_fcb->StandardInfo.DeletePending;
3762 #ifdef ENABLE_TIMINGS
3763     LARGE_INTEGER t1, t2;
3764     t1 = KeQueryPerformanceCounter(NULL);
3765 #endif
3766 
3767     ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
3768 
3769 #ifdef DEBUG_OPEN
3770     DbgEn();
3771     print_debug_header(RxContext);
3772     print_nt_create_params(1, RxContext->Create.NtCreateParameters);
3773     if (ea) print_ea_info(0, ea);
3774 #endif
3775 
3776     status = check_nfs41_create_args(RxContext);
3777     if (status) goto out;
3778 
3779 #if defined(STORE_MOUNT_SEC_CONTEXT) && defined (USE_MOUNT_SEC_CONTEXT)
3780     status = nfs41_UpcallCreate(NFS41_OPEN, &pVNetRootContext->mount_sec_ctx,
3781 #else
3782     status = nfs41_UpcallCreate(NFS41_OPEN, NULL,
3783 #endif
3784         pVNetRootContext->session, INVALID_HANDLE_VALUE,
3785         pNetRootContext->nfs41d_version,
3786         SrvOpen->pAlreadyPrefixedName, &entry);
3787     if (status) goto out;
3788 
3789     entry->u.Open.access_mask = params->DesiredAccess;
3790     entry->u.Open.access_mode = params->ShareAccess;
3791     entry->u.Open.attrs = params->FileAttributes;
3792     if (!(params->CreateOptions & FILE_DIRECTORY_FILE))
3793         entry->u.Open.attrs |= FILE_ATTRIBUTE_ARCHIVE;
3794     entry->u.Open.disp = params->Disposition;
3795     entry->u.Open.copts = params->CreateOptions;
3796     entry->u.Open.srv_open = SrvOpen;
3797     /* treat the NfsActOnLink ea as FILE_OPEN_REPARSE_POINT */
3798     if ((ea && AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength)) ||
3799             (entry->u.Open.access_mask & DELETE))
3800         entry->u.Open.copts |= FILE_OPEN_REPARSE_POINT;
3801     if (isDataAccess(params->DesiredAccess) || isOpen2Create(params->Disposition))
3802         entry->u.Open.open_owner_id = InterlockedIncrement(&open_owner_id);
3803     // if we are creating a file check if nfsv3attributes were passed in
3804     if (params->Disposition != FILE_OPEN && params->Disposition != FILE_OVERWRITE) {
3805         entry->u.Open.mode = 0777;
3806         if (ea && AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)) {
3807             nfs3_attrs *attrs = (nfs3_attrs *)(ea->EaName + ea->EaNameLength + 1);
3808 #ifdef DEBUG_OPEN
3809             DbgP("creating file with mode %o\n", attrs->mode);
3810 #endif
3811             entry->u.Open.mode = attrs->mode;
3812         }
3813         if (params->FileAttributes & FILE_ATTRIBUTE_READONLY)
3814             entry->u.Open.mode = 0444;
3815     }
3816     if (entry->u.Open.disp == FILE_CREATE && ea &&
3817             AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
3818         /* for a cygwin symlink, given as a unicode string */
3819         entry->u.Open.symlink.Buffer = (PWCH)(ea->EaName + ea->EaNameLength + 1);
3820         entry->u.Open.symlink.MaximumLength = entry->u.Open.symlink.Length = ea->EaValueLength;
3821     }
3822 retry_on_link:
3823     if (ea && create_should_pass_ea(ea, params->Disposition)) {
3824         /* lock the extended attribute buffer for read access in user space */
3825         entry->u.Open.EaMdl = IoAllocateMdl(ea,
3826             RxContext->CurrentIrpSp->Parameters.Create.EaLength,
3827             FALSE, FALSE, NULL);
3828         if (entry->u.Open.EaMdl == NULL) {
3829             status = STATUS_INTERNAL_ERROR;
3830             RxFreePool(entry);
3831             goto out;
3832         }
3833         entry->u.Open.EaMdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
3834         MmProbeAndLockPages(entry->u.Open.EaMdl, KernelMode, IoModifyAccess);
3835     }
3836 
3837     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
3838 #ifndef USE_MOUNT_SEC_CONTEXT
3839     SeDeleteClientSecurity(&entry->sec_ctx);
3840 #endif
3841     if (status) goto out;
3842 
3843     if (entry->u.Open.EaMdl) {
3844         MmUnlockPages(entry->u.Open.EaMdl);
3845         IoFreeMdl(entry->u.Open.EaMdl);
3846     }
3847 
3848     if (entry->status == NO_ERROR && entry->errno == ERROR_REPARSE) {
3849         /* symbolic link handling. when attempting to open a symlink when the
3850          * FILE_OPEN_REPARSE_POINT flag is not set, replace the filename with
3851          * the symlink target's by calling RxPrepareToReparseSymbolicLink()
3852          * and returning STATUS_REPARSE. the object manager will attempt to
3853          * open the new path, and return its handle for the original open */
3854         PRDBSS_DEVICE_OBJECT DeviceObject = RxContext->RxDeviceObject;
3855         PV_NET_ROOT VNetRoot = (PV_NET_ROOT)
3856             RxContext->pRelevantSrvOpen->pVNetRoot;
3857         PUNICODE_STRING VNetRootPrefix = &VNetRoot->PrefixEntry.Prefix;
3858         UNICODE_STRING AbsPath;
3859         PCHAR buf;
3860         BOOLEAN ReparseRequired;
3861 
3862         /* allocate the string for RxPrepareToReparseSymbolicLink(), and
3863          * format an absolute path "DeviceName+VNetRootName+symlink" */
3864         AbsPath.Length = DeviceObject->DeviceName.Length +
3865             VNetRootPrefix->Length + entry->u.Open.symlink.Length;
3866         AbsPath.MaximumLength = AbsPath.Length + sizeof(UNICODE_NULL);
3867         AbsPath.Buffer = RxAllocatePoolWithTag(NonPagedPool,
3868             AbsPath.MaximumLength, NFS41_MM_POOLTAG);
3869         if (AbsPath.Buffer == NULL) {
3870             status = STATUS_INSUFFICIENT_RESOURCES;
3871             goto out_free;
3872         }
3873 
3874         buf = (PCHAR)AbsPath.Buffer;
3875         RtlCopyMemory(buf, DeviceObject->DeviceName.Buffer,
3876             DeviceObject->DeviceName.Length);
3877         buf += DeviceObject->DeviceName.Length;
3878         RtlCopyMemory(buf, VNetRootPrefix->Buffer, VNetRootPrefix->Length);
3879         buf += VNetRootPrefix->Length;
3880         RtlCopyMemory(buf, entry->u.Open.symlink.Buffer,
3881             entry->u.Open.symlink.Length);
3882         RxFreePool(entry->u.Open.symlink.Buffer);
3883         buf += entry->u.Open.symlink.Length;
3884         *(PWCHAR)buf = UNICODE_NULL;
3885 
3886         status = RxPrepareToReparseSymbolicLink(RxContext,
3887             entry->u.Open.symlink_embedded, &AbsPath, TRUE, &ReparseRequired);
3888 #ifdef DEBUG_OPEN
3889         DbgP("RxPrepareToReparseSymbolicLink(%u, '%wZ') returned %08lX, "
3890             "FileName is '%wZ'\n", entry->u.Open.symlink_embedded,
3891             &AbsPath, status, &RxContext->CurrentIrpSp->FileObject->FileName);
3892 #endif
3893         if (status == STATUS_SUCCESS) {
3894             /* if a reparse is not required, reopen the link itself.  this
3895              * happens with operations on cygwin symlinks, where the reparse
3896              * flag is not set */
3897             if (!ReparseRequired) {
3898                 entry->u.Open.symlink.Length = 0;
3899                 entry->u.Open.copts |= FILE_OPEN_REPARSE_POINT;
3900                 goto retry_on_link;
3901             }
3902             status = STATUS_REPARSE;
3903         }
3904         goto out_free;
3905     }
3906 
3907     status = map_open_errors(entry->status,
3908                 SrvOpen->pAlreadyPrefixedName->Length);
3909     if (status) {
3910 #ifdef DEBUG_OPEN
3911         print_open_error(1, status);
3912 #endif
3913         goto out_free;
3914     }
3915 
3916     if (!RxIsFcbAcquiredExclusive(Fcb)) {
3917         ASSERT(!RxIsFcbAcquiredShared(Fcb));
3918         RxAcquireExclusiveFcbResourceInMRx(Fcb);
3919     }
3920 
3921     RxContext->pFobx = RxCreateNetFobx(RxContext, SrvOpen);
3922     if (RxContext->pFobx == NULL) {
3923         status = STATUS_INSUFFICIENT_RESOURCES;
3924         goto out_free;
3925     }
3926 #ifdef DEBUG_OPEN
3927     DbgP("nfs41_Create: created FOBX %p\n", RxContext->pFobx);
3928 #endif
3929     nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
3930     nfs41_fobx->nfs41_open_state = entry->open_state;
3931 #ifndef USE_MOUNT_SEC_CONTEXT
3932     status = nfs41_get_sec_ctx(SecurityImpersonation, &nfs41_fobx->sec_ctx);
3933     if (status)
3934         goto out_free;
3935 #else
3936     RtlCopyMemory(&nfs41_fobx->sec_ctx, &pVNetRootContext->mount_sec_ctx,
3937         sizeof(nfs41_fobx->sec_ctx));
3938 #endif
3939 
3940     // we get attributes only for data access and file (not directories)
3941     if (Fcb->OpenCount == 0 ||
3942             (Fcb->OpenCount > 0 &&
3943                 nfs41_fcb->changeattr != entry->ChangeTime)) {
3944         FCB_INIT_PACKET InitPacket;
3945         RX_FILE_TYPE StorageType = FileTypeNotYetKnown;
3946         RtlCopyMemory(&nfs41_fcb->BasicInfo, &entry->u.Open.binfo,
3947             sizeof(entry->u.Open.binfo));
3948         RtlCopyMemory(&nfs41_fcb->StandardInfo, &entry->u.Open.sinfo,
3949             sizeof(entry->u.Open.sinfo));
3950         nfs41_fcb->mode = entry->u.Open.mode;
3951         nfs41_fcb->changeattr = entry->ChangeTime;
3952         if (((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
3953                 !pVNetRootContext->read_only) || oldDeletePending)
3954             nfs41_fcb->StandardInfo.DeletePending = TRUE;
3955 
3956         RxFormInitPacket(InitPacket,
3957             &entry->u.Open.binfo.FileAttributes,
3958             &entry->u.Open.sinfo.NumberOfLinks,
3959             &entry->u.Open.binfo.CreationTime,
3960             &entry->u.Open.binfo.LastAccessTime,
3961             &entry->u.Open.binfo.LastWriteTime,
3962             &entry->u.Open.binfo.ChangeTime,
3963             &entry->u.Open.sinfo.AllocationSize,
3964             &entry->u.Open.sinfo.EndOfFile,
3965             &entry->u.Open.sinfo.EndOfFile);
3966 
3967         if (entry->u.Open.sinfo.Directory)
3968             StorageType = FileTypeDirectory;
3969         else
3970             StorageType = FileTypeFile;
3971 
3972         RxFinishFcbInitialization(Fcb, RDBSS_STORAGE_NTC(StorageType),
3973                                     &InitPacket);
3974     }
3975 #ifdef DEBUG_OPEN
3976     else
3977         DbgP("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
3978 
3979     print_basic_info(1, &nfs41_fcb->BasicInfo);
3980     print_std_info(1, &nfs41_fcb->StandardInfo);
3981 #endif
3982 
3983     /* aglo: 05/10/2012. it seems like always have to invalid the cache if the
3984      * file has been opened before and being opened again for data access.
3985      * If the file was opened before, RDBSS might have cached (unflushed) data
3986      * and by opening it again, we will not have the correct representation of
3987      * the file size and data content. fileio tests 208, 219, 221.
3988      */
3989     if (Fcb->OpenCount > 0 && (isDataAccess(params->DesiredAccess) ||
3990             nfs41_fcb->changeattr != entry->ChangeTime) &&
3991                 !nfs41_fcb->StandardInfo.Directory) {
3992         ULONG flag = DISABLE_CACHING;
3993 #ifdef DEBUG_OPEN
3994         DbgP("nfs41_Create: reopening (changed) file %wZ\n",
3995             SrvOpen->pAlreadyPrefixedName);
3996 #endif
3997         RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
3998     }
3999     if (!nfs41_fcb->StandardInfo.Directory &&
4000             isDataAccess(params->DesiredAccess)) {
4001         nfs41_fobx->deleg_type = entry->u.Open.deleg_type;
4002 #ifdef DEBUG_OPEN
4003         DbgP("nfs41_Create: received delegation %d\n", entry->u.Open.deleg_type);
4004 #endif
4005         if (!(params->CreateOptions & FILE_WRITE_THROUGH) &&
4006                 !pVNetRootContext->write_thru &&
4007                 (entry->u.Open.deleg_type == 2 ||
4008                 (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)))) {
4009 #ifdef DEBUG_OPEN
4010             DbgP("nfs41_Create: enabling write buffering\n");
4011 #endif
4012             SrvOpen->BufferingFlags |=
4013                 (FCB_STATE_WRITECACHING_ENABLED |
4014                 FCB_STATE_WRITEBUFFERING_ENABLED);
4015         } else if (params->CreateOptions & FILE_WRITE_THROUGH ||
4016                     pVNetRootContext->write_thru)
4017             nfs41_fobx->write_thru = TRUE;
4018         if (entry->u.Open.deleg_type >= 1 ||
4019                 params->DesiredAccess & FILE_READ_DATA) {
4020 #ifdef DEBUG_OPEN
4021             DbgP("nfs41_Create: enabling read buffering\n");
4022 #endif
4023             SrvOpen->BufferingFlags |=
4024                 (FCB_STATE_READBUFFERING_ENABLED |
4025                 FCB_STATE_READCACHING_ENABLED);
4026         }
4027         if (pVNetRootContext->nocache ||
4028                 (params->CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING)) {
4029 #ifdef DEBUG_OPEN
4030             DbgP("nfs41_Create: disabling buffering\n");
4031 #endif
4032             SrvOpen->BufferingFlags = FCB_STATE_DISABLE_LOCAL_BUFFERING;
4033             nfs41_fobx->nocache = TRUE;
4034         } else if (!entry->u.Open.deleg_type && !Fcb->OpenCount) {
4035             nfs41_fcb_list_entry *oentry;
4036 #ifdef DEBUG_OPEN
4037             DbgP("nfs41_Create: received no delegations: srv_open=%p "
4038                 "ctime=%llu\n", SrvOpen, entry->ChangeTime);
4039 #endif
4040             oentry = RxAllocatePoolWithTag(NonPagedPool,
4041                 sizeof(nfs41_fcb_list_entry), NFS41_MM_POOLTAG_OPEN);
4042             if (oentry == NULL) {
4043                 status = STATUS_INSUFFICIENT_RESOURCES;
4044                 goto out_free;
4045             }
4046             oentry->fcb = RxContext->pFcb;
4047             oentry->nfs41_fobx = nfs41_fobx;
4048             oentry->session = pVNetRootContext->session;
4049             oentry->ChangeTime = entry->ChangeTime;
4050             oentry->skip = FALSE;
4051             nfs41_AddEntry(fcblistLock, openlist, oentry);
4052         }
4053     }
4054 
4055     if ((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
4056             !pVNetRootContext->read_only)
4057         nfs41_fcb->StandardInfo.DeletePending = TRUE;
4058 
4059     RxContext->Create.ReturnedCreateInformation =
4060         map_disposition_to_create_retval(params->Disposition, entry->errno);
4061 
4062     RxContext->pFobx->OffsetOfNextEaToReturn = 1;
4063 #ifndef __REACTOS__
4064     RxContext->CurrentIrp->IoStatus.Information =
4065         RxContext->Create.ReturnedCreateInformation;
4066 #endif
4067     status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
4068 
4069 out_free:
4070     if (entry)
4071         RxFreePool(entry);
4072 out:
4073 #ifdef ENABLE_TIMINGS
4074     t2 = KeQueryPerformanceCounter(NULL);
4075     if ((params->DesiredAccess & FILE_READ_DATA) ||
4076             (params->DesiredAccess & FILE_WRITE_DATA) ||
4077             (params->DesiredAccess & FILE_APPEND_DATA) ||
4078             (params->DesiredAccess & FILE_EXECUTE)) {
4079         InterlockedIncrement(&open.tops);
4080         InterlockedAdd64(&open.ticks, t2.QuadPart - t1.QuadPart);
4081 #ifdef ENABLE_INDV_TIMINGS
4082     DbgP("nfs41_Create open delta = %d op=%d sum=%d\n",
4083         t2.QuadPart - t1.QuadPart, open.tops, open.ticks);
4084 #endif
4085     } else {
4086         InterlockedIncrement(&lookup.tops);
4087         InterlockedAdd64(&lookup.ticks, t2.QuadPart - t1.QuadPart);
4088 #ifdef ENABLE_INDV_TIMINGS
4089     DbgP("nfs41_Create lookup delta = %d op=%d sum=%d\n",
4090         t2.QuadPart - t1.QuadPart, lookup.tops, lookup.ticks);
4091 #endif
4092     }
4093 #endif
4094 #ifdef DEBUG_OPEN
4095     DbgEx();
4096 #endif
4097     return status;
4098 }
4099 
4100 #ifdef __REACTOS__
4101 NTSTATUS NTAPI nfs41_CollapseOpen(
4102 #else
4103 NTSTATUS nfs41_CollapseOpen(
4104 #endif
4105     IN OUT PRX_CONTEXT RxContext)
4106 {
4107     NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
4108     DbgEn();
4109     DbgEx();
4110     return status;
4111 }
4112 
4113 #ifdef __REACTOS__
4114 NTSTATUS NTAPI nfs41_ShouldTryToCollapseThisOpen(
4115 #else
4116 NTSTATUS nfs41_ShouldTryToCollapseThisOpen(
4117 #endif
4118     IN OUT PRX_CONTEXT RxContext)
4119 {
4120     if (RxContext->pRelevantSrvOpen == NULL)
4121         return STATUS_SUCCESS;
4122     else return STATUS_MORE_PROCESSING_REQUIRED;
4123 }
4124 
4125 #ifdef __REACTOS__
4126 ULONG NTAPI nfs41_ExtendForCache(
4127 #else
4128 ULONG nfs41_ExtendForCache(
4129 #endif
4130     IN OUT PRX_CONTEXT RxContext,
4131     IN PLARGE_INTEGER pNewFileSize,
4132     OUT PLARGE_INTEGER pNewAllocationSize)
4133 {
4134     NTSTATUS status = STATUS_SUCCESS;
4135     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
4136 #ifdef DEBUG_CACHE
4137     PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
4138     DbgEn();
4139     print_debug_header(RxContext);
4140     DbgP("input: byte count 0x%x filesize 0x%x alloc size 0x%x\n",
4141         LowIoContext->ParamsFor.ReadWrite.ByteCount, *pNewFileSize,
4142         *pNewAllocationSize);
4143 #endif
4144     pNewAllocationSize->QuadPart = pNewFileSize->QuadPart + 8192;
4145     nfs41_fcb->StandardInfo.AllocationSize.QuadPart =
4146         pNewAllocationSize->QuadPart;
4147     nfs41_fcb->StandardInfo.EndOfFile.QuadPart = pNewFileSize->QuadPart;
4148 #ifdef DEBUG_CACHE
4149     DbgP("new filesize 0x%x new allocation size 0x%x\n", *pNewFileSize,
4150         *pNewAllocationSize);
4151 #endif
4152 #ifdef DEBUG_CACHE
4153     DbgEx();
4154 #endif
4155     return status;
4156 }
4157 
4158 VOID nfs41_remove_fcb_entry(
4159     PMRX_FCB fcb)
4160 {
4161     PLIST_ENTRY pEntry;
4162     nfs41_fcb_list_entry *cur;
4163     ExAcquireFastMutex(&fcblistLock);
4164 
4165     pEntry = openlist.head.Flink;
4166     while (!IsListEmpty(&openlist.head)) {
4167         cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
4168                 nfs41_fcb_list_entry, next);
4169         if (cur->fcb == fcb) {
4170 #ifdef DEBUG_CLOSE
4171             DbgP("nfs41_remove_srvopen_entry: Found match for fcb=%p\n", fcb);
4172 #endif
4173             RemoveEntryList(pEntry);
4174             RxFreePool(cur);
4175             break;
4176         }
4177         if (pEntry->Flink == &openlist.head) {
4178 #ifdef DEBUG_CLOSE
4179             DbgP("nfs41_remove_srvopen_entry: reached EOL looking for fcb "
4180                 "%p\n", fcb);
4181 #endif
4182             break;
4183         }
4184         pEntry = pEntry->Flink;
4185     }
4186     ExReleaseFastMutex(&fcblistLock);
4187 }
4188 
4189 NTSTATUS map_close_errors(
4190     DWORD status)
4191 {
4192     switch (status) {
4193     case NO_ERROR:              return STATUS_SUCCESS;
4194     case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
4195     case ERROR_NOT_EMPTY:       return STATUS_DIRECTORY_NOT_EMPTY;
4196     case ERROR_FILE_INVALID:    return STATUS_FILE_INVALID;
4197     default:
4198         print_error("failed to map windows error %d to NTSTATUS; "
4199             "defaulting to STATUS_INTERNAL_ERROR\n", status);
4200     case ERROR_INTERNAL_ERROR: return STATUS_INTERNAL_ERROR;
4201     }
4202 }
4203 
4204 #ifdef __REACTOS__
4205 NTSTATUS NTAPI nfs41_CloseSrvOpen(
4206 #else
4207 NTSTATUS nfs41_CloseSrvOpen(
4208 #endif
4209     IN OUT PRX_CONTEXT RxContext)
4210 {
4211     NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
4212     nfs41_updowncall_entry *entry;
4213     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
4214     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
4215         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
4216     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
4217         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
4218     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
4219     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
4220 #ifdef ENABLE_TIMINGS
4221     LARGE_INTEGER t1, t2;
4222     t1 = KeQueryPerformanceCounter(NULL);
4223 #endif
4224 
4225 #ifdef DEBUG_CLOSE
4226     DbgEn();
4227     print_debug_header(RxContext);
4228 #endif
4229 
4230     if (!nfs41_fobx->deleg_type && !nfs41_fcb->StandardInfo.Directory &&
4231             !RxContext->pFcb->OpenCount) {
4232         nfs41_remove_fcb_entry(RxContext->pFcb);
4233     }
4234 
4235     status = nfs41_UpcallCreate(NFS41_CLOSE, &nfs41_fobx->sec_ctx,
4236         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
4237         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
4238     if (status) goto out;
4239 
4240     entry->u.Close.srv_open = SrvOpen;
4241     if (nfs41_fcb->StandardInfo.DeletePending)
4242         nfs41_fcb->DeletePending = TRUE;
4243     if (!RxContext->pFcb->OpenCount ||
4244             (nfs41_fcb->StandardInfo.DeletePending &&
4245                 nfs41_fcb->StandardInfo.Directory))
4246         entry->u.Close.remove = nfs41_fcb->StandardInfo.DeletePending;
4247     if (!RxContext->pFcb->OpenCount)
4248         entry->u.Close.renamed = nfs41_fcb->Renamed;
4249 
4250     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
4251 #ifndef USE_MOUNT_SEC_CONTEXT
4252     SeDeleteClientSecurity(&nfs41_fobx->sec_ctx);
4253 #endif
4254     if (status) goto out;
4255 
4256     /* map windows ERRORs to NTSTATUS */
4257     status = map_close_errors(entry->status);
4258     RxFreePool(entry);
4259 out:
4260 #ifdef ENABLE_TIMINGS
4261     t2 = KeQueryPerformanceCounter(NULL);
4262     InterlockedIncrement(&close.tops);
4263     InterlockedAdd64(&close.ticks, t2.QuadPart - t1.QuadPart);
4264 #ifdef ENABLE_INDV_TIMINGS
4265     DbgP("nfs41_CloseSrvOpen delta = %d op=%d sum=%d\n",
4266         t2.QuadPart - t1.QuadPart, close.tops, close.ticks);
4267 #endif
4268 #endif
4269 #ifdef DEBUG_CLOSE
4270     DbgEx();
4271 #endif
4272     return status;
4273 }
4274 
4275 #ifdef __REACTOS__
4276 NTSTATUS NTAPI nfs41_Flush(
4277 #else
4278 NTSTATUS nfs41_Flush(
4279 #endif
4280     IN OUT PRX_CONTEXT RxContext)
4281 {
4282     return STATUS_SUCCESS;
4283 }
4284 
4285 #ifdef __REACTOS__
4286 NTSTATUS NTAPI nfs41_DeallocateForFcb(
4287 #else
4288 NTSTATUS nfs41_DeallocateForFcb(
4289 #endif
4290     IN OUT PMRX_FCB pFcb)
4291 {
4292     return STATUS_SUCCESS;
4293 }
4294 
4295 #ifdef __REACTOS__
4296 NTSTATUS NTAPI nfs41_DeallocateForFobx(
4297 #else
4298 NTSTATUS nfs41_DeallocateForFobx(
4299 #endif
4300     IN OUT PMRX_FOBX pFobx)
4301 {
4302     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
4303     if (nfs41_fobx->acl)
4304         RxFreePool(nfs41_fobx->acl);
4305     return STATUS_SUCCESS;
4306 }
4307 
4308 void print_debug_filedirquery_header(
4309     PRX_CONTEXT RxContext)
4310 {
4311     print_debug_header(RxContext);
4312     DbgP("FileName='%wZ', InfoClass = %s\n",
4313         GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext),
4314         print_file_information_class(RxContext->Info.FileInformationClass));
4315 }
4316 
4317 void print_querydir_args(
4318     PRX_CONTEXT RxContext)
4319 {
4320     print_debug_filedirquery_header(RxContext);
4321     DbgP("Filter='%wZ', Index=%d, Restart/Single/Specified/Init=%d/%d/%d/%d\n",
4322         &RxContext->pFobx->UnicodeQueryTemplate,
4323         RxContext->QueryDirectory.FileIndex,
4324         RxContext->QueryDirectory.RestartScan,
4325         RxContext->QueryDirectory.ReturnSingleEntry,
4326         RxContext->QueryDirectory.IndexSpecified,
4327         RxContext->QueryDirectory.InitialQuery);
4328 }
4329 
4330 NTSTATUS map_querydir_errors(
4331     DWORD status)
4332 {
4333     switch (status) {
4334     case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
4335     case ERROR_BUFFER_OVERFLOW:     return STATUS_BUFFER_OVERFLOW;
4336     case ERROR_FILE_NOT_FOUND:      return STATUS_NO_SUCH_FILE;
4337     case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
4338     case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
4339     case ERROR_NO_MORE_FILES:       return STATUS_NO_MORE_FILES;
4340     case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
4341     case ERROR_FILENAME_EXCED_RANGE: return STATUS_NAME_TOO_LONG;
4342     default:
4343         print_error("failed to map windows error %d to NTSTATUS; "
4344             "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
4345     case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
4346     }
4347 }
4348 
4349 NTSTATUS check_nfs41_dirquery_args(
4350     IN PRX_CONTEXT RxContext)
4351 {
4352     if (RxContext->Info.Buffer == NULL)
4353         return STATUS_INVALID_USER_BUFFER;
4354     return STATUS_SUCCESS;
4355 }
4356 
4357 #ifdef __REACTOS__
4358 NTSTATUS NTAPI nfs41_QueryDirectory(
4359 #else
4360 NTSTATUS nfs41_QueryDirectory(
4361 #endif
4362     IN OUT PRX_CONTEXT RxContext)
4363 {
4364     NTSTATUS status = STATUS_INVALID_PARAMETER;
4365     nfs41_updowncall_entry *entry;
4366     FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
4367     PUNICODE_STRING Filter = &RxContext->pFobx->UnicodeQueryTemplate;
4368     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
4369     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
4370         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
4371     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
4372         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
4373     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
4374 #ifdef ENABLE_TIMINGS
4375     LARGE_INTEGER t1, t2;
4376     t1 = KeQueryPerformanceCounter(NULL);
4377 #endif
4378 
4379 #ifdef DEBUG_DIR_QUERY
4380     DbgEn();
4381     print_querydir_args(RxContext);
4382 #endif
4383 
4384     status = check_nfs41_dirquery_args(RxContext);
4385     if (status) goto out;
4386 
4387     switch (InfoClass) {
4388     /* classes handled in readdir_copy_entry() and readdir_size_for_entry() */
4389     case FileNamesInformation:
4390     case FileDirectoryInformation:
4391     case FileFullDirectoryInformation:
4392     case FileIdFullDirectoryInformation:
4393     case FileBothDirectoryInformation:
4394     case FileIdBothDirectoryInformation:
4395         break;
4396     default:
4397         print_error("nfs41_QueryDirectory: unhandled dir query class %d\n",
4398             InfoClass);
4399         status = STATUS_NOT_SUPPORTED;
4400         goto out;
4401     }
4402     status = nfs41_UpcallCreate(NFS41_DIR_QUERY, &nfs41_fobx->sec_ctx,
4403         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
4404         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
4405     if (status) goto out;
4406 
4407     entry->u.QueryFile.InfoClass = InfoClass;
4408     entry->buf_len = RxContext->Info.LengthRemaining;
4409     entry->buf = RxContext->Info.Buffer;
4410     entry->u.QueryFile.mdl = IoAllocateMdl(RxContext->Info.Buffer,
4411         RxContext->Info.LengthRemaining, FALSE, FALSE, NULL);
4412     if (entry->u.QueryFile.mdl == NULL) {
4413         status = STATUS_INTERNAL_ERROR;
4414         RxFreePool(entry);
4415         goto out;
4416     }
4417     entry->u.QueryFile.mdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
4418     MmProbeAndLockPages(entry->u.QueryFile.mdl, KernelMode, IoModifyAccess);
4419 
4420     entry->u.QueryFile.filter = Filter;
4421     entry->u.QueryFile.initial_query = RxContext->QueryDirectory.InitialQuery;
4422     entry->u.QueryFile.restart_scan = RxContext->QueryDirectory.RestartScan;
4423     entry->u.QueryFile.return_single = RxContext->QueryDirectory.ReturnSingleEntry;
4424 
4425     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
4426     if (status) goto out;
4427     MmUnlockPages(entry->u.QueryFile.mdl);
4428 
4429     if (entry->status == STATUS_BUFFER_TOO_SMALL) {
4430         DbgP("nfs41_QueryDirectory: buffer too small provided %d need %lu\n",
4431             RxContext->Info.LengthRemaining, entry->buf_len);
4432         RxContext->InformationToReturn = entry->buf_len;
4433         status = STATUS_BUFFER_TOO_SMALL;
4434     } else if (entry->status == STATUS_SUCCESS) {
4435 #ifdef ENABLE_TIMINGS
4436         InterlockedIncrement(&readdir.sops);
4437         InterlockedAdd64(&readdir.size, entry->u.QueryFile.buf_len);
4438 #endif
4439         RxContext->Info.LengthRemaining -= entry->buf_len;
4440         status = STATUS_SUCCESS;
4441     } else {
4442         /* map windows ERRORs to NTSTATUS */
4443         status = map_querydir_errors(entry->status);
4444     }
4445     IoFreeMdl(entry->u.QueryFile.mdl);
4446     RxFreePool(entry);
4447 out:
4448 #ifdef ENABLE_TIMINGS
4449     t2 = KeQueryPerformanceCounter(NULL);
4450     InterlockedIncrement(&readdir.tops);
4451     InterlockedAdd64(&readdir.ticks, t2.QuadPart - t1.QuadPart);
4452 #ifdef ENABLE_INDV_TIMINGS
4453     DbgP("nfs41_QueryDirectory delta = %d ops=%d sum=%d\n",
4454         t2.QuadPart - t1.QuadPart, readdir.tops, readdir.ticks);
4455 #endif
4456 #endif
4457 #ifdef DEBUG_DIR_QUERY
4458     DbgEx();
4459 #endif
4460     return status;
4461 }
4462 
4463 void print_queryvolume_args(
4464     PRX_CONTEXT RxContext)
4465 {
4466     print_debug_header(RxContext);
4467     DbgP("FileName='%wZ', InfoClass = %s BufferLen = %d\n",
4468         GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext),
4469         print_fs_information_class(RxContext->Info.FileInformationClass),
4470         RxContext->Info.LengthRemaining);
4471 }
4472 
4473 NTSTATUS map_volume_errors(
4474     DWORD status)
4475 {
4476     switch (status) {
4477     case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
4478     case ERROR_VC_DISCONNECTED:     return STATUS_CONNECTION_DISCONNECTED;
4479     case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
4480     case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
4481     case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
4482     default:
4483         print_error("failed to map windows error %d to NTSTATUS; "
4484             "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
4485     case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
4486     }
4487 }
4488 
4489 void nfs41_create_volume_info(PFILE_FS_VOLUME_INFORMATION pVolInfo, DWORD *len)
4490 {
4491     DECLARE_CONST_UNICODE_STRING(VolName, VOL_NAME);
4492 
4493     RtlZeroMemory(pVolInfo, sizeof(FILE_FS_VOLUME_INFORMATION));
4494     pVolInfo->VolumeSerialNumber = 0xBABAFACE;
4495     pVolInfo->VolumeLabelLength = VolName.Length;
4496     RtlCopyMemory(&pVolInfo->VolumeLabel[0], (PVOID)VolName.Buffer,
4497         VolName.MaximumLength);
4498     *len = sizeof(FILE_FS_VOLUME_INFORMATION) + VolName.Length;
4499 }
4500 
4501 static BOOLEAN is_root_directory(
4502     PRX_CONTEXT RxContext)
4503 {
4504     __notnull PV_NET_ROOT VNetRoot = (PV_NET_ROOT)
4505         RxContext->pRelevantSrvOpen->pVNetRoot;
4506     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
4507         NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
4508 
4509     /* calculate the root directory's length, including vnetroot prefix,
4510      * mount path, and a trailing \ */
4511     const USHORT RootPathLen = VNetRoot->PrefixEntry.Prefix.Length +
4512             pVNetRootContext->MountPathLen + sizeof(WCHAR);
4513 
4514     return RxContext->CurrentIrpSp->FileObject->FileName.Length <= RootPathLen;
4515 }
4516 
4517 #ifdef __REACTOS__
4518 NTSTATUS NTAPI nfs41_QueryVolumeInformation(
4519 #else
4520 NTSTATUS nfs41_QueryVolumeInformation(
4521 #endif
4522     IN OUT PRX_CONTEXT RxContext)
4523 {
4524     NTSTATUS status = STATUS_INVALID_PARAMETER;
4525     nfs41_updowncall_entry *entry;
4526     ULONG RemainingLength = RxContext->Info.LengthRemaining, SizeUsed;
4527     FS_INFORMATION_CLASS InfoClass = RxContext->Info.FsInformationClass;
4528     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
4529     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
4530         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
4531     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
4532         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
4533     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
4534     NFS41GetDeviceExtension(RxContext, DevExt);
4535 
4536 #ifdef ENABLE_TIMINGS
4537     LARGE_INTEGER t1, t2;
4538     t1 = KeQueryPerformanceCounter(NULL);
4539 #endif
4540 
4541 #ifdef DEBUG_VOLUME_QUERY
4542     DbgEn();
4543     print_queryvolume_args(RxContext);
4544 #endif
4545 
4546     status = check_nfs41_dirquery_args(RxContext);
4547     if (status) goto out;
4548 
4549     switch (InfoClass) {
4550     case FileFsVolumeInformation:
4551         if ((ULONG)RxContext->Info.LengthRemaining >= DevExt->VolAttrsLen) {
4552             RtlCopyMemory(RxContext->Info.Buffer, DevExt->VolAttrs,
4553                 DevExt->VolAttrsLen);
4554             RxContext->Info.LengthRemaining -= DevExt->VolAttrsLen;
4555             status = STATUS_SUCCESS;
4556         } else {
4557             RtlCopyMemory(RxContext->Info.Buffer, DevExt->VolAttrs,
4558                 RxContext->Info.LengthRemaining);
4559             status = STATUS_BUFFER_OVERFLOW;
4560         }
4561         goto out;
4562     case FileFsDeviceInformation:
4563     {
4564         PFILE_FS_DEVICE_INFORMATION pDevInfo = RxContext->Info.Buffer;
4565 
4566         SizeUsed = sizeof(FILE_FS_DEVICE_INFORMATION);
4567         if (RemainingLength < SizeUsed) {
4568             status = STATUS_BUFFER_TOO_SMALL;
4569             RxContext->InformationToReturn = SizeUsed;
4570             goto out;
4571         }
4572         pDevInfo->DeviceType = RxContext->pFcb->pNetRoot->DeviceType;
4573         pDevInfo->Characteristics = FILE_REMOTE_DEVICE | FILE_DEVICE_IS_MOUNTED;
4574         RxContext->Info.LengthRemaining -= SizeUsed;
4575         status = STATUS_SUCCESS;
4576         goto out;
4577     }
4578     case FileAccessInformation:
4579         status = STATUS_NOT_SUPPORTED;
4580         goto out;
4581 
4582     case FileFsAttributeInformation:
4583         if (RxContext->Info.LengthRemaining < FS_ATTR_LEN) {
4584             RxContext->InformationToReturn = FS_ATTR_LEN;
4585             status = STATUS_BUFFER_TOO_SMALL;
4586             goto out;
4587         }
4588 
4589         /* on attribute queries for the root directory,
4590          * use cached volume attributes from mount */
4591         if (is_root_directory(RxContext)) {
4592             PFILE_FS_ATTRIBUTE_INFORMATION attrs =
4593                 (PFILE_FS_ATTRIBUTE_INFORMATION)RxContext->Info.Buffer;
4594             DECLARE_CONST_UNICODE_STRING(FsName, FS_NAME);
4595 
4596             RtlCopyMemory(attrs, &pVNetRootContext->FsAttrs,
4597                 sizeof(pVNetRootContext->FsAttrs));
4598 
4599             /* fill in the FileSystemName */
4600             RtlCopyMemory(attrs->FileSystemName, FsName.Buffer,
4601                 FsName.MaximumLength); /* 'MaximumLength' to include null */
4602             attrs->FileSystemNameLength = FsName.Length;
4603 
4604             RxContext->Info.LengthRemaining -= FS_ATTR_LEN;
4605             goto out;
4606         }
4607         /* else fall through and send the upcall */
4608     case FileFsSizeInformation:
4609     case FileFsFullSizeInformation:
4610         break;
4611 
4612     default:
4613         print_error("nfs41_QueryVolumeInformation: unhandled class %d\n", InfoClass);
4614         status = STATUS_NOT_SUPPORTED;
4615         goto out;
4616     }
4617     status = nfs41_UpcallCreate(NFS41_VOLUME_QUERY, &nfs41_fobx->sec_ctx,
4618         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
4619         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
4620     if (status) goto out;
4621 
4622     entry->u.Volume.query = InfoClass;
4623     entry->buf = RxContext->Info.Buffer;
4624     entry->buf_len = RxContext->Info.LengthRemaining;
4625 
4626     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
4627     if (status) goto out;
4628 
4629     if (entry->status == STATUS_BUFFER_TOO_SMALL) {
4630         RxContext->InformationToReturn = entry->buf_len;
4631         status = STATUS_BUFFER_TOO_SMALL;
4632     } else if (entry->status == STATUS_SUCCESS) {
4633         if (InfoClass == FileFsAttributeInformation) {
4634             /* fill in the FileSystemName */
4635             PFILE_FS_ATTRIBUTE_INFORMATION attrs =
4636                 (PFILE_FS_ATTRIBUTE_INFORMATION)RxContext->Info.Buffer;
4637             DECLARE_CONST_UNICODE_STRING(FsName, FS_NAME);
4638 
4639             RtlCopyMemory(attrs->FileSystemName, FsName.Buffer,
4640                 FsName.MaximumLength); /* 'MaximumLength' to include null */
4641             attrs->FileSystemNameLength = FsName.Length;
4642 
4643             entry->buf_len = FS_ATTR_LEN;
4644         }
4645 #ifdef ENABLE_TIMINGS
4646         InterlockedIncrement(&volume.sops);
4647         InterlockedAdd64(&volume.size, entry->u.Volume.buf_len);
4648 #endif
4649         RxContext->Info.LengthRemaining -= entry->buf_len;
4650         status = STATUS_SUCCESS;
4651     } else {
4652         status = map_volume_errors(entry->status);
4653     }
4654     RxFreePool(entry);
4655 out:
4656 #ifdef ENABLE_TIMINGS
4657     t2 = KeQueryPerformanceCounter(NULL);
4658     InterlockedIncrement(&volume.tops);
4659     InterlockedAdd64(&volume.ticks, t2.QuadPart - t1.QuadPart);
4660 #ifdef ENABLE_INDV_TIMINGS
4661     DbgP("nfs41_QueryVolumeInformation delta = %d op=%d sum=%d\n",
4662         t2.QuadPart - t1.QuadPart, volume.tops, volume.ticks);
4663 #endif
4664 #endif
4665 #ifdef DEBUG_VOLUME_QUERY
4666     DbgEx();
4667 #endif
4668     return status;
4669 }
4670 
4671 VOID nfs41_update_fcb_list(
4672     PMRX_FCB fcb,
4673     ULONGLONG ChangeTime)
4674 {
4675     PLIST_ENTRY pEntry;
4676     nfs41_fcb_list_entry *cur;
4677     ExAcquireFastMutex(&fcblistLock);
4678     pEntry = openlist.head.Flink;
4679     while (!IsListEmpty(&openlist.head)) {
4680         cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
4681                 nfs41_fcb_list_entry, next);
4682         if (cur->fcb == fcb &&
4683                 cur->ChangeTime != ChangeTime) {
4684 #if defined(DEBUG_FILE_SET) || defined(DEBUG_ACL_SET) || \
4685     defined(DEBUG_WRITE) || defined(DEBUG_EA_SET)
4686             DbgP("nfs41_update_fcb_list: Found match for fcb %p: updating "
4687                 "%llu to %llu\n", fcb, cur->ChangeTime, ChangeTime);
4688 #endif
4689             cur->ChangeTime = ChangeTime;
4690             break;
4691         }
4692         /* place an upcall for this srv_open */
4693         if (pEntry->Flink == &openlist.head) {
4694 #if defined(DEBUG_FILE_SET) || defined(DEBUG_ACL_SET) || \
4695     defined(DEBUG_WRITE) || defined(DEBUG_EA_SET)
4696             DbgP("nfs41_update_fcb_list: reached EOL loooking for "
4697                 "fcb=%p\n", fcb);
4698 #endif
4699             break;
4700         }
4701         pEntry = pEntry->Flink;
4702     }
4703     ExReleaseFastMutex(&fcblistLock);
4704 }
4705 
4706 void print_nfs3_attrs(
4707     nfs3_attrs *attrs)
4708 {
4709     DbgP("type=%d mode=%o nlink=%d size=%d atime=%x mtime=%x ctime=%x\n",
4710         attrs->type, attrs->mode, attrs->nlink, attrs->size, attrs->atime,
4711         attrs->mtime, attrs->ctime);
4712 }
4713 
4714 void file_time_to_nfs_time(
4715     IN const PLARGE_INTEGER file_time,
4716     OUT LONGLONG *nfs_time)
4717 {
4718     LARGE_INTEGER diff = unix_time_diff;
4719     diff.QuadPart = file_time->QuadPart - diff.QuadPart;
4720     *nfs_time = diff.QuadPart / 10000000;
4721 }
4722 
4723 void create_nfs3_attrs(
4724     nfs3_attrs *attrs,
4725     PNFS41_FCB nfs41_fcb)
4726 {
4727     RtlZeroMemory(attrs, sizeof(nfs3_attrs));
4728     if (nfs41_fcb->BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
4729         attrs->type = NF3LNK;
4730     else if (nfs41_fcb->StandardInfo.Directory)
4731         attrs->type = NF3DIR;
4732     else
4733         attrs->type = NF3REG;
4734     attrs->mode = nfs41_fcb->mode;
4735     attrs->nlink = nfs41_fcb->StandardInfo.NumberOfLinks;
4736     attrs->size.QuadPart = attrs->used.QuadPart =
4737         nfs41_fcb->StandardInfo.EndOfFile.QuadPart;
4738     file_time_to_nfs_time(&nfs41_fcb->BasicInfo.LastAccessTime, &attrs->atime);
4739     file_time_to_nfs_time(&nfs41_fcb->BasicInfo.ChangeTime, &attrs->mtime);
4740     file_time_to_nfs_time(&nfs41_fcb->BasicInfo.CreationTime, &attrs->ctime);
4741 }
4742 
4743 
4744 NTSTATUS map_setea_error(
4745     DWORD error)
4746 {
4747     switch (error) {
4748     case NO_ERROR:                      return STATUS_SUCCESS;
4749     case ERROR_FILE_NOT_FOUND:          return STATUS_NO_EAS_ON_FILE;
4750     case ERROR_ACCESS_DENIED:           return STATUS_ACCESS_DENIED;
4751     case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
4752     case ERROR_NETNAME_DELETED:         return STATUS_NETWORK_NAME_DELETED;
4753     case ERROR_FILE_TOO_LARGE:          return STATUS_EA_TOO_LARGE;
4754     case ERROR_BUFFER_OVERFLOW:         return STATUS_BUFFER_OVERFLOW;
4755     case STATUS_BUFFER_TOO_SMALL:
4756     case ERROR_INSUFFICIENT_BUFFER:     return STATUS_BUFFER_TOO_SMALL;
4757     case ERROR_INVALID_EA_HANDLE:       return STATUS_NONEXISTENT_EA_ENTRY;
4758     case ERROR_NO_MORE_FILES:           return STATUS_NO_MORE_EAS;
4759     case ERROR_EA_FILE_CORRUPT:         return STATUS_EA_CORRUPT_ERROR;
4760     default:
4761         print_error("failed to map windows error %d to NTSTATUS; "
4762             "defaulting to STATUS_INVALID_PARAMETER\n", error);
4763     case ERROR_INVALID_PARAMETER:       return STATUS_INVALID_PARAMETER;
4764     }
4765 }
4766 
4767 NTSTATUS check_nfs41_setea_args(
4768     IN PRX_CONTEXT RxContext)
4769 {
4770     NTSTATUS status;
4771     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
4772         NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
4773     __notnull PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs =
4774         &pVNetRootContext->FsAttrs;
4775     __notnull PFILE_FULL_EA_INFORMATION ea =
4776         (PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer;
4777 
4778     status = check_nfs41_dirquery_args(RxContext);
4779     if (status) goto out;
4780 
4781     if (ea == NULL) {
4782         status = STATUS_INVALID_PARAMETER;
4783         goto out;
4784     }
4785     if (AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength) ||
4786         AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
4787         status = STATUS_INVALID_PARAMETER; /* only allowed on create */
4788         goto out;
4789     }
4790     /* ignore cygwin EAs when checking support */
4791     if (!(FsAttrs->FileSystemAttributes & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)
4792         && !AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)) {
4793         status = STATUS_EAS_NOT_SUPPORTED;
4794         goto out;
4795     }
4796     if ((RxContext->pRelevantSrvOpen->DesiredAccess & FILE_WRITE_EA) == 0) {
4797         status = STATUS_ACCESS_DENIED;
4798         goto out;
4799     }
4800     if (pVNetRootContext->read_only) {
4801         print_error("check_nfs41_setattr_args: Read-only mount\n");
4802         status = STATUS_ACCESS_DENIED;
4803         goto out;
4804     }
4805 out:
4806     return status;
4807 }
4808 
4809 #ifdef __REACTOS__
4810 NTSTATUS NTAPI nfs41_SetEaInformation(
4811 #else
4812 NTSTATUS nfs41_SetEaInformation(
4813 #endif
4814     IN OUT PRX_CONTEXT RxContext)
4815 {
4816     NTSTATUS status = STATUS_EAS_NOT_SUPPORTED;
4817     nfs41_updowncall_entry *entry;
4818     __notnull PFILE_FULL_EA_INFORMATION eainfo =
4819         (PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer;
4820     nfs3_attrs *attrs = NULL;
4821     ULONG buflen = RxContext->CurrentIrpSp->Parameters.SetEa.Length, error_offset;
4822     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
4823     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
4824         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
4825     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
4826         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
4827     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
4828     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
4829 #ifdef ENABLE_TIMINGS
4830     LARGE_INTEGER t1, t2;
4831     t1 = KeQueryPerformanceCounter(NULL);
4832 #endif
4833 
4834 #ifdef DEBUG_EA_SET
4835     DbgEn();
4836     print_debug_header(RxContext);
4837     print_ea_info(1, eainfo);
4838 #endif
4839 
4840     status = check_nfs41_setea_args(RxContext);
4841     if (status) goto out;
4842 
4843     status = nfs41_UpcallCreate(NFS41_EA_SET, &nfs41_fobx->sec_ctx,
4844         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
4845         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
4846     if (status) goto out;
4847 
4848     if (AnsiStrEq(&NfsV3Attributes, eainfo->EaName, eainfo->EaNameLength)) {
4849         attrs = (nfs3_attrs *)(eainfo->EaName + eainfo->EaNameLength + 1);
4850 #ifdef DEBUG_EA_SET
4851         print_nfs3_attrs(attrs);
4852         DbgP("old mode is %o new mode is %o\n", nfs41_fcb->mode, attrs->mode);
4853 #endif
4854         entry->u.SetEa.mode = attrs->mode;
4855     } else {
4856         entry->u.SetEa.mode = 0;
4857         status = IoCheckEaBufferValidity(eainfo, buflen, &error_offset);
4858         if (status) {
4859             RxFreePool(entry);
4860             goto out;
4861         }
4862     }
4863     entry->buf = eainfo;
4864     entry->buf_len = buflen;
4865 
4866     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
4867     if (status) goto out;
4868 #ifdef ENABLE_TIMINGS
4869     if (entry->status == STATUS_SUCCESS) {
4870         InterlockedIncrement(&setexattr.sops);
4871         InterlockedAdd64(&setexattr.size, entry->u.SetEa.buf_len);
4872     }
4873 #endif
4874     status = map_setea_error(entry->status);
4875     if (!status) {
4876         if (!nfs41_fobx->deleg_type && entry->ChangeTime &&
4877                 (SrvOpen->DesiredAccess &
4878                 (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)))
4879             nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
4880         nfs41_fcb->changeattr = entry->ChangeTime;
4881         nfs41_fcb->mode = entry->u.SetEa.mode;
4882     }
4883     RxFreePool(entry);
4884 out:
4885 #ifdef ENABLE_TIMINGS
4886     t2 = KeQueryPerformanceCounter(NULL);
4887     InterlockedIncrement(&setexattr.tops);
4888     InterlockedAdd64(&setexattr.ticks, t2.QuadPart - t1.QuadPart);
4889 #ifdef ENABLE_INDV_TIMINGS
4890     DbgP("nfs41_SetEaInformation delta = %d op=%d sum=%d\n",
4891         t2.QuadPart - t1.QuadPart, setexattr.tops, setexattr.ticks);
4892 #endif
4893 #endif
4894 #ifdef DEBUG_EA_SET
4895     DbgEx();
4896 #endif
4897     return status;
4898 }
4899 
4900 NTSTATUS check_nfs41_queryea_args(
4901     IN PRX_CONTEXT RxContext)
4902 {
4903     NTSTATUS status;
4904     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
4905         NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
4906     __notnull PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs =
4907         &pVNetRootContext->FsAttrs;
4908     PFILE_GET_EA_INFORMATION ea = (PFILE_GET_EA_INFORMATION)
4909             RxContext->CurrentIrpSp->Parameters.QueryEa.EaList;
4910 
4911     status = check_nfs41_dirquery_args(RxContext);
4912     if (status) goto out;
4913 
4914     if (!(FsAttrs->FileSystemAttributes & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)) {
4915         if (ea == NULL) {
4916             status = STATUS_EAS_NOT_SUPPORTED;
4917             goto out;
4918         }
4919         /* ignore cygwin EAs when checking support */
4920         if (!AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength) &&
4921             !AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength) &&
4922             !AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
4923             status = STATUS_EAS_NOT_SUPPORTED;
4924             goto out;
4925         }
4926     }
4927     if ((RxContext->pRelevantSrvOpen->DesiredAccess & FILE_READ_EA) == 0) {
4928         status = STATUS_ACCESS_DENIED;
4929         goto out;
4930     }
4931 out:
4932     return status;
4933 }
4934 
4935 static NTSTATUS QueryCygwinSymlink(
4936     IN OUT PRX_CONTEXT RxContext,
4937     IN PFILE_GET_EA_INFORMATION query,
4938     OUT PFILE_FULL_EA_INFORMATION info)
4939 {
4940     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
4941     __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
4942             NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
4943     __notnull PNFS41_NETROOT_EXTENSION NetRootContext =
4944             NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
4945     __notnull PNFS41_FOBX Fobx = NFS41GetFobxExtension(RxContext->pFobx);
4946     nfs41_updowncall_entry *entry;
4947     UNICODE_STRING TargetName;
4948     const USHORT HeaderLen = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
4949         query->EaNameLength + 1;
4950     NTSTATUS status;
4951 
4952     if (RxContext->Info.LengthRemaining < HeaderLen) {
4953         status = STATUS_BUFFER_TOO_SMALL;
4954         RxContext->InformationToReturn = HeaderLen;
4955         goto out;
4956     }
4957 
4958     TargetName.Buffer = (PWCH)(info->EaName + query->EaNameLength + 1);
4959     TargetName.MaximumLength = (USHORT)min(RxContext->Info.LengthRemaining -
4960         HeaderLen, 0xFFFF);
4961 
4962     status = nfs41_UpcallCreate(NFS41_SYMLINK, &Fobx->sec_ctx,
4963         VNetRootContext->session, Fobx->nfs41_open_state,
4964         NetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
4965     if (status) goto out;
4966 
4967     entry->u.Symlink.target = &TargetName;
4968     entry->u.Symlink.set = FALSE;
4969 
4970     status = nfs41_UpcallWaitForReply(entry, VNetRootContext->timeout);
4971     if (status) goto out;
4972 
4973     status = map_setea_error(entry->status);
4974     if (status == STATUS_SUCCESS) {
4975         info->NextEntryOffset = 0;
4976         info->Flags = 0;
4977         info->EaNameLength = query->EaNameLength;
4978         info->EaValueLength = TargetName.Length - sizeof(UNICODE_NULL);
4979         TargetName.Buffer[TargetName.Length/sizeof(WCHAR)] = UNICODE_NULL;
4980         RtlCopyMemory(info->EaName, query->EaName, query->EaNameLength);
4981         RxContext->Info.LengthRemaining = HeaderLen + info->EaValueLength;
4982     } else if (status == STATUS_BUFFER_TOO_SMALL) {
4983         RxContext->InformationToReturn = HeaderLen +
4984             entry->u.Symlink.target->Length;
4985     }
4986     RxFreePool(entry);
4987 out:
4988     return status;
4989 }
4990 
4991 static NTSTATUS QueryCygwinEA(
4992     IN OUT PRX_CONTEXT RxContext,
4993     IN PFILE_GET_EA_INFORMATION query,
4994     OUT PFILE_FULL_EA_INFORMATION info)
4995 {
4996     NTSTATUS status = STATUS_NONEXISTENT_EA_ENTRY;
4997 
4998     if (query == NULL)
4999         goto out;
5000 
5001     if (AnsiStrEq(&NfsSymlinkTargetName, query->EaName, query->EaNameLength)) {
5002         __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
5003         if (nfs41_fcb->BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
5004             status = QueryCygwinSymlink(RxContext, query, info);
5005             goto out;
5006         } else {
5007             const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
5008                 NfsSymlinkTargetName.Length - sizeof(CHAR);
5009             if (LengthRequired > RxContext->Info.LengthRemaining) {
5010                 status = STATUS_BUFFER_TOO_SMALL;
5011                 RxContext->InformationToReturn = LengthRequired;
5012                 goto out;
5013             }
5014             info->NextEntryOffset = 0;
5015             info->Flags = 0;
5016             info->EaValueLength = 0;
5017             info->EaNameLength = (UCHAR)NfsActOnLink.Length;
5018             RtlCopyMemory(info->EaName, NfsSymlinkTargetName.Buffer,
5019                 NfsSymlinkTargetName.Length);
5020             RxContext->Info.LengthRemaining = LengthRequired;
5021             status = STATUS_SUCCESS;
5022             goto out;
5023         }
5024     }
5025 
5026     if (AnsiStrEq(&NfsV3Attributes, query->EaName, query->EaNameLength)) {
5027         nfs3_attrs attrs;
5028 
5029         const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
5030             NfsV3Attributes.Length + sizeof(nfs3_attrs) - sizeof(CHAR);
5031         if (LengthRequired > RxContext->Info.LengthRemaining) {
5032             status = STATUS_BUFFER_TOO_SMALL;
5033             RxContext->InformationToReturn = LengthRequired;
5034             goto out;
5035         }
5036 
5037         create_nfs3_attrs(&attrs, NFS41GetFcbExtension(RxContext->pFcb));
5038 #ifdef DEBUG_EA_QUERY
5039         print_nfs3_attrs(&attrs);
5040 #endif
5041 
5042         info->NextEntryOffset = 0;
5043         info->Flags = 0;
5044         info->EaNameLength = (UCHAR)NfsV3Attributes.Length;
5045         info->EaValueLength = sizeof(nfs3_attrs);
5046         RtlCopyMemory(info->EaName, NfsV3Attributes.Buffer,
5047             NfsV3Attributes.Length);
5048         RtlCopyMemory(info->EaName + info->EaNameLength + 1, &attrs,
5049             sizeof(nfs3_attrs));
5050         RxContext->Info.LengthRemaining = LengthRequired;
5051         status = STATUS_SUCCESS;
5052         goto out;
5053     }
5054 
5055     if (AnsiStrEq(&NfsActOnLink, query->EaName, query->EaNameLength)) {
5056 
5057         const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
5058             query->EaNameLength - sizeof(CHAR);
5059         if (LengthRequired > RxContext->Info.LengthRemaining) {
5060             status = STATUS_BUFFER_TOO_SMALL;
5061             RxContext->InformationToReturn = LengthRequired;
5062             goto out;
5063         }
5064 
5065         info->NextEntryOffset = 0;
5066         info->Flags = 0;
5067         info->EaNameLength = query->EaNameLength;
5068         info->EaValueLength = 0;
5069         RtlCopyMemory(info->EaName, query->EaName, query->EaNameLength);
5070         RxContext->Info.LengthRemaining = LengthRequired;
5071         status = STATUS_SUCCESS;
5072         goto out;
5073     }
5074 out:
5075     return status;
5076 }
5077 
5078 #ifdef __REACTOS__
5079 NTSTATUS NTAPI nfs41_QueryEaInformation(
5080 #else
5081 NTSTATUS nfs41_QueryEaInformation(
5082 #endif
5083     IN OUT PRX_CONTEXT RxContext)
5084 {
5085     NTSTATUS status = STATUS_EAS_NOT_SUPPORTED;
5086     nfs41_updowncall_entry *entry;
5087     PFILE_GET_EA_INFORMATION query = (PFILE_GET_EA_INFORMATION)
5088             RxContext->CurrentIrpSp->Parameters.QueryEa.EaList;
5089     ULONG buflen = RxContext->CurrentIrpSp->Parameters.QueryEa.Length;
5090     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
5091     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
5092             NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
5093     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
5094             NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
5095     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
5096 #ifdef ENABLE_TIMINGS
5097     LARGE_INTEGER t1, t2;
5098     t1 = KeQueryPerformanceCounter(NULL);
5099 #endif
5100 
5101 #ifdef DEBUG_EA_QUERY
5102     DbgEn();
5103     print_debug_header(RxContext);
5104     print_get_ea(1, query);
5105 #endif
5106     status = check_nfs41_queryea_args(RxContext);
5107     if (status) goto out;
5108 
5109     /* handle queries for cygwin EAs */
5110     status = QueryCygwinEA(RxContext, query,
5111         (PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer);
5112     if (status != STATUS_NONEXISTENT_EA_ENTRY)
5113         goto out;
5114 
5115     status = nfs41_UpcallCreate(NFS41_EA_GET, &nfs41_fobx->sec_ctx,
5116         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
5117         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
5118     if (status) goto out;
5119 
5120     entry->buf_len = buflen;
5121     entry->buf = RxContext->Info.Buffer;
5122     entry->u.QueryEa.EaList = query;
5123     entry->u.QueryEa.EaListLength = query == NULL ? 0 :
5124         RxContext->QueryEa.UserEaListLength;
5125     entry->u.QueryEa.EaIndex = RxContext->QueryEa.IndexSpecified ?
5126         RxContext->QueryEa.UserEaIndex : 0;
5127     entry->u.QueryEa.RestartScan = RxContext->QueryEa.RestartScan;
5128     entry->u.QueryEa.ReturnSingleEntry = RxContext->QueryEa.ReturnSingleEntry;
5129 
5130     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
5131     if (status) goto out;
5132 
5133     if (entry->status == STATUS_SUCCESS) {
5134         switch (entry->u.QueryEa.Overflow) {
5135         case ERROR_INSUFFICIENT_BUFFER:
5136             status = STATUS_BUFFER_TOO_SMALL;
5137             break;
5138         case ERROR_BUFFER_OVERFLOW:
5139             status = RxContext->IoStatusBlock.Status = STATUS_BUFFER_OVERFLOW;
5140             break;
5141         default:
5142             RxContext->IoStatusBlock.Status = STATUS_SUCCESS;
5143             break;
5144         }
5145         RxContext->InformationToReturn = entry->buf_len;
5146 #ifdef ENABLE_TIMINGS
5147         InterlockedIncrement(&getexattr.sops);
5148         InterlockedAdd64(&getexattr.size, entry->u.QueryEa.buf_len);
5149 #endif
5150     } else {
5151         status = map_setea_error(entry->status);
5152     }
5153     RxFreePool(entry);
5154 out:
5155 #ifdef ENABLE_TIMINGS
5156     t2 = KeQueryPerformanceCounter(NULL);
5157     InterlockedIncrement(&getexattr.tops);
5158     InterlockedAdd64(&getexattr.ticks, t2.QuadPart - t1.QuadPart);
5159 #ifdef ENABLE_INDV_TIMINGS
5160     DbgP("nfs41_QueryEaInformation delta = %d op=%d sum=%d\n",
5161         t2.QuadPart - t1.QuadPart, getexattr.tops, getexattr.ticks);
5162 #endif
5163 #endif
5164 #ifdef DEBUG_EA_QUERY
5165     DbgEx();
5166 #endif
5167     return status;
5168 }
5169 
5170 NTSTATUS map_query_acl_error(
5171     DWORD error)
5172 {
5173     switch (error) {
5174     case NO_ERROR:                  return STATUS_SUCCESS;
5175     case ERROR_NOT_SUPPORTED:       return STATUS_NOT_SUPPORTED;
5176     case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
5177     case ERROR_FILE_NOT_FOUND:      return STATUS_OBJECT_NAME_NOT_FOUND;
5178     case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
5179     default:
5180         print_error("failed to map windows error %d to NTSTATUS; "
5181             "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", error);
5182     case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
5183     }
5184 }
5185 
5186 NTSTATUS check_nfs41_getacl_args(
5187     PRX_CONTEXT RxContext)
5188 {
5189     NTSTATUS status = STATUS_SUCCESS;
5190     SECURITY_INFORMATION info_class =
5191         RxContext->CurrentIrpSp->Parameters.QuerySecurity.SecurityInformation;
5192 
5193     /* we don't support sacls */
5194     if (info_class == SACL_SECURITY_INFORMATION ||
5195             info_class == LABEL_SECURITY_INFORMATION) {
5196         status = STATUS_NOT_SUPPORTED;
5197         goto out;
5198     }
5199     if (RxContext->CurrentIrp->UserBuffer == NULL &&
5200             RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length)
5201         status = STATUS_INVALID_USER_BUFFER;
5202 out:
5203     return status;
5204 }
5205 
5206 #ifdef __REACTOS__
5207 NTSTATUS NTAPI nfs41_QuerySecurityInformation(
5208 #else
5209 NTSTATUS nfs41_QuerySecurityInformation(
5210 #endif
5211     IN OUT PRX_CONTEXT RxContext)
5212 {
5213     NTSTATUS status = STATUS_NOT_SUPPORTED;
5214     nfs41_updowncall_entry *entry;
5215     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
5216     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
5217     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
5218         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
5219     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
5220         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
5221     SECURITY_INFORMATION info_class =
5222         RxContext->CurrentIrpSp->Parameters.QuerySecurity.SecurityInformation;
5223 #ifdef ENABLE_TIMINGS
5224     LARGE_INTEGER t1, t2;
5225     t1 = KeQueryPerformanceCounter(NULL);
5226 #endif
5227 
5228 #ifdef DEBUG_ACL_QUERY
5229     DbgEn();
5230     print_debug_header(RxContext);
5231     print_acl_args(info_class);
5232 #endif
5233 
5234     status = check_nfs41_getacl_args(RxContext);
5235     if (status) goto out;
5236 
5237     if (nfs41_fobx->acl && nfs41_fobx->acl_len) {
5238         LARGE_INTEGER current_time;
5239         KeQuerySystemTime(&current_time);
5240 #ifdef DEBUG_ACL_QUERY
5241         DbgP("CurrentTime %lx Saved Acl time %lx\n",
5242             current_time.QuadPart, nfs41_fobx->time.QuadPart);
5243 #endif
5244         if (current_time.QuadPart - nfs41_fobx->time.QuadPart <= 20*1000) {
5245             PSECURITY_DESCRIPTOR sec_desc = (PSECURITY_DESCRIPTOR)
5246                 RxContext->CurrentIrp->UserBuffer;
5247             RtlCopyMemory(sec_desc, nfs41_fobx->acl, nfs41_fobx->acl_len);
5248             RxContext->IoStatusBlock.Information =
5249                 RxContext->InformationToReturn = nfs41_fobx->acl_len;
5250             RxContext->IoStatusBlock.Status = status = STATUS_SUCCESS;
5251 #ifdef ENABLE_TIMINGS
5252             InterlockedIncrement(&getacl.sops);
5253             InterlockedAdd64(&getacl.size, nfs41_fobx->acl_len);
5254 #endif
5255         } else status = 1;
5256         RxFreePool(nfs41_fobx->acl);
5257         nfs41_fobx->acl = NULL;
5258         nfs41_fobx->acl_len = 0;
5259         if (!status)
5260             goto out;
5261     }
5262 
5263     status = nfs41_UpcallCreate(NFS41_ACL_QUERY, &nfs41_fobx->sec_ctx,
5264         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
5265         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
5266     if (status) goto out;
5267 
5268     entry->u.Acl.query = info_class;
5269     /* we can't provide RxContext->CurrentIrp->UserBuffer to the upcall thread
5270      * because it becomes an invalid pointer with that execution context
5271      */
5272     entry->buf_len = RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length;
5273 
5274     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
5275     if (status) goto out;
5276 
5277     if (entry->status == STATUS_BUFFER_TOO_SMALL) {
5278 #ifdef DEBUG_ACL_QUERY
5279         DbgP("nfs41_QuerySecurityInformation: provided buffer size=%d but we "
5280              "need %lu\n",
5281              RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length,
5282              entry->buf_len);
5283 #endif
5284         status = STATUS_BUFFER_OVERFLOW;
5285         RxContext->InformationToReturn = entry->buf_len;
5286 
5287         /* Save ACL buffer */
5288         nfs41_fobx->acl = entry->buf;
5289         nfs41_fobx->acl_len = entry->buf_len;
5290         KeQuerySystemTime(&nfs41_fobx->time);
5291     } else if (entry->status == STATUS_SUCCESS) {
5292         PSECURITY_DESCRIPTOR sec_desc = (PSECURITY_DESCRIPTOR)
5293             RxContext->CurrentIrp->UserBuffer;
5294         RtlCopyMemory(sec_desc, entry->buf, entry->buf_len);
5295 #ifdef ENABLE_TIMINGS
5296         InterlockedIncrement(&getacl.sops);
5297         InterlockedAdd64(&getacl.size, entry->u.Acl.buf_len);
5298 #endif
5299         RxFreePool(entry->buf);
5300         nfs41_fobx->acl = NULL;
5301         nfs41_fobx->acl_len = 0;
5302         RxContext->IoStatusBlock.Information = RxContext->InformationToReturn =
5303             entry->buf_len;
5304         RxContext->IoStatusBlock.Status = status = STATUS_SUCCESS;
5305     } else {
5306         status = map_query_acl_error(entry->status);
5307     }
5308     RxFreePool(entry);
5309 out:
5310 #ifdef ENABLE_TIMINGS
5311     t2 = KeQueryPerformanceCounter(NULL);
5312     /* only count getacl that we made an upcall for */
5313     if (status == STATUS_BUFFER_OVERFLOW) {
5314         InterlockedIncrement(&getacl.tops);
5315         InterlockedAdd64(&getacl.ticks, t2.QuadPart - t1.QuadPart);
5316     }
5317 #ifdef ENABLE_INDV_TIMINGS
5318     DbgP("nfs41_QuerySecurityInformation: delta = %d op=%d sum=%d\n",
5319         t2.QuadPart - t1.QuadPart, getacl.tops, getacl.ticks);
5320 #endif
5321 #endif
5322 #ifdef DEBUG_ACL_QUERY
5323     DbgEx();
5324 #endif
5325     return status;
5326 }
5327 
5328 NTSTATUS check_nfs41_setacl_args(
5329     PRX_CONTEXT RxContext)
5330 {
5331     NTSTATUS status = STATUS_SUCCESS;
5332     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
5333         NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
5334     SECURITY_INFORMATION info_class =
5335         RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityInformation;
5336 
5337     if (pVNetRootContext->read_only) {
5338         print_error("check_nfs41_setacl_args: Read-only mount\n");
5339         status = STATUS_ACCESS_DENIED;
5340         goto out;
5341     }
5342     /* we don't support sacls */
5343     if (info_class == SACL_SECURITY_INFORMATION  ||
5344             info_class == LABEL_SECURITY_INFORMATION) {
5345         status = STATUS_NOT_SUPPORTED;
5346         goto out;
5347     }
5348 out:
5349     return status;
5350 }
5351 
5352 #ifdef __REACTOS__
5353 NTSTATUS NTAPI nfs41_SetSecurityInformation(
5354 #else
5355 NTSTATUS nfs41_SetSecurityInformation(
5356 #endif
5357     IN OUT PRX_CONTEXT RxContext)
5358 {
5359     NTSTATUS status = STATUS_NOT_SUPPORTED;
5360     nfs41_updowncall_entry *entry;
5361     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
5362     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
5363     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
5364         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
5365     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
5366         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
5367     __notnull PSECURITY_DESCRIPTOR sec_desc =
5368         RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityDescriptor;
5369     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
5370     SECURITY_INFORMATION info_class =
5371         RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityInformation;
5372 #ifdef ENABLE_TIMINGS
5373     LARGE_INTEGER t1, t2;
5374     t1 = KeQueryPerformanceCounter(NULL);
5375 #endif
5376 
5377 #ifdef DEBUG_ACL_SET
5378     DbgEn();
5379     print_debug_header(RxContext);
5380     print_acl_args(info_class);
5381 #endif
5382 
5383     status = check_nfs41_setacl_args(RxContext);
5384     if (status) goto out;
5385 
5386     /* check that ACL is present */
5387     if (info_class & DACL_SECURITY_INFORMATION) {
5388         PACL acl;
5389         BOOLEAN present, dacl_default;
5390         status = RtlGetDaclSecurityDescriptor(sec_desc, &present, &acl,
5391                     &dacl_default);
5392         if (status) {
5393             DbgP("RtlGetDaclSecurityDescriptor failed %x\n", status);
5394             goto out;
5395         }
5396         if (present == FALSE) {
5397             DbgP("NO ACL present\n");
5398             goto out;
5399         }
5400     }
5401 
5402     status = nfs41_UpcallCreate(NFS41_ACL_SET, &nfs41_fobx->sec_ctx,
5403         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
5404         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
5405     if (status) goto out;
5406 
5407     entry->u.Acl.query = info_class;
5408     entry->buf = sec_desc;
5409     entry->buf_len = RtlLengthSecurityDescriptor(sec_desc);
5410 #ifdef ENABLE_TIMINGS
5411     InterlockedIncrement(&setacl.sops);
5412     InterlockedAdd64(&setacl.size, entry->u.Acl.buf_len);
5413 #endif
5414 
5415     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
5416     if (status) goto out;
5417 
5418     status = map_query_acl_error(entry->status);
5419     if (!status) {
5420         if (!nfs41_fobx->deleg_type && entry->ChangeTime &&
5421                 (SrvOpen->DesiredAccess &
5422                 (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)))
5423             nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
5424         nfs41_fcb->changeattr = entry->ChangeTime;
5425     }
5426     RxFreePool(entry);
5427 out:
5428 #ifdef ENABLE_TIMINGS
5429     t2 = KeQueryPerformanceCounter(NULL);
5430     InterlockedIncrement(&setacl.tops);
5431     InterlockedAdd64(&setacl.ticks, t2.QuadPart - t1.QuadPart);
5432 #ifdef ENABLE_INDV_TIMINGS
5433     DbgP("nfs41_SetSecurityInformation delta = %d op=%d sum=%d\n",
5434         t2.QuadPart - t1.QuadPart, setacl.tops, setacl.ticks);
5435 #endif
5436 #endif
5437 #ifdef DEBUG_ACL_SET
5438     DbgEx();
5439 #endif
5440     return status;
5441 }
5442 
5443 NTSTATUS map_queryfile_error(
5444     DWORD error)
5445 {
5446     switch (error) {
5447     case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
5448     case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
5449     case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
5450     default:
5451         print_error("failed to map windows error %d to NTSTATUS; "
5452             "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", error);
5453     case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
5454     }
5455 }
5456 
5457 #ifdef __REACTOS__
5458 NTSTATUS NTAPI nfs41_QueryFileInformation(
5459 #else
5460 NTSTATUS nfs41_QueryFileInformation(
5461 #endif
5462     IN OUT PRX_CONTEXT RxContext)
5463 {
5464     NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
5465     FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
5466     nfs41_updowncall_entry *entry;
5467     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
5468     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
5469         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
5470     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
5471         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
5472     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
5473     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
5474 #ifdef ENABLE_TIMINGS
5475     LARGE_INTEGER t1, t2;
5476     t1 = KeQueryPerformanceCounter(NULL);
5477 #endif
5478 
5479 #ifdef DEBUG_FILE_QUERY
5480     DbgEn();
5481     print_debug_filedirquery_header(RxContext);
5482 #endif
5483 
5484     status = check_nfs41_dirquery_args(RxContext);
5485     if (status) goto out;
5486 
5487     switch (InfoClass) {
5488     case FileEaInformation:
5489     {
5490         PFILE_EA_INFORMATION info =
5491             (PFILE_EA_INFORMATION)RxContext->Info.Buffer;
5492         info->EaSize = 0;
5493         RxContext->Info.LengthRemaining -= sizeof(FILE_EA_INFORMATION);
5494         status = STATUS_SUCCESS;
5495         goto out;
5496     }
5497     case FileBasicInformation:
5498     case FileStandardInformation:
5499     case FileInternalInformation:
5500     case FileAttributeTagInformation:
5501     case FileNetworkOpenInformation:
5502         break;
5503     default:
5504         print_error("nfs41_QueryFileInformation: unhandled class %d\n", InfoClass);
5505         status = STATUS_NOT_SUPPORTED;
5506         goto out;
5507     }
5508 
5509     status = nfs41_UpcallCreate(NFS41_FILE_QUERY, &nfs41_fobx->sec_ctx,
5510         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
5511         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
5512     if (status) goto out;
5513 
5514     entry->u.QueryFile.InfoClass = InfoClass;
5515     entry->buf = RxContext->Info.Buffer;
5516     entry->buf_len = RxContext->Info.LengthRemaining;
5517 
5518     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
5519     if (status) goto out;
5520 
5521     if (entry->status == STATUS_BUFFER_TOO_SMALL) {
5522         RxContext->InformationToReturn = entry->buf_len;
5523         status = STATUS_BUFFER_TOO_SMALL;
5524     } else if (entry->status == STATUS_SUCCESS) {
5525         BOOLEAN DeletePending = FALSE;
5526 #ifdef ENABLE_TIMINGS
5527         InterlockedIncrement(&getattr.sops);
5528         InterlockedAdd64(&getattr.size, entry->u.QueryFile.buf_len);
5529 #endif
5530         RxContext->Info.LengthRemaining -= entry->buf_len;
5531         status = STATUS_SUCCESS;
5532 
5533         switch (InfoClass) {
5534         case FileBasicInformation:
5535             RtlCopyMemory(&nfs41_fcb->BasicInfo, RxContext->Info.Buffer,
5536                 sizeof(nfs41_fcb->BasicInfo));
5537 #ifdef DEBUG_FILE_QUERY
5538             print_basic_info(1, &nfs41_fcb->BasicInfo);
5539 #endif
5540             break;
5541         case FileStandardInformation:
5542             /* this a fix for RDBSS behaviour when it first calls ExtendForCache,
5543              * then it sends a file query irp for standard attributes and
5544              * expects to receive EndOfFile of value set by the ExtendForCache.
5545              * It seems to cache the filesize based on that instead of sending
5546              * a file size query for after doing the write.
5547              */
5548         {
5549             PFILE_STANDARD_INFORMATION std_info;
5550             std_info = (PFILE_STANDARD_INFORMATION)RxContext->Info.Buffer;
5551             if (nfs41_fcb->StandardInfo.AllocationSize.QuadPart >
5552                     std_info->AllocationSize.QuadPart) {
5553 #ifdef DEBUG_FILE_QUERY
5554                 DbgP("Old AllocationSize is bigger: saving %x\n",
5555                     nfs41_fcb->StandardInfo.AllocationSize.QuadPart);
5556 #endif
5557                 std_info->AllocationSize.QuadPart =
5558                     nfs41_fcb->StandardInfo.AllocationSize.QuadPart;
5559             }
5560             if (nfs41_fcb->StandardInfo.EndOfFile.QuadPart >
5561                     std_info->EndOfFile.QuadPart) {
5562 #ifdef DEBUG_FILE_QUERY
5563                 DbgP("Old EndOfFile is bigger: saving %x\n",
5564                     nfs41_fcb->StandardInfo.EndOfFile);
5565 #endif
5566                 std_info->EndOfFile.QuadPart =
5567                     nfs41_fcb->StandardInfo.EndOfFile.QuadPart;
5568             }
5569             std_info->DeletePending = nfs41_fcb->DeletePending;
5570         }
5571             if (nfs41_fcb->StandardInfo.DeletePending)
5572                 DeletePending = TRUE;
5573             RtlCopyMemory(&nfs41_fcb->StandardInfo, RxContext->Info.Buffer,
5574                 sizeof(nfs41_fcb->StandardInfo));
5575             nfs41_fcb->StandardInfo.DeletePending = DeletePending;
5576 #ifdef DEBUG_FILE_QUERY
5577             print_std_info(1, &nfs41_fcb->StandardInfo);
5578 #endif
5579             break;
5580         }
5581     } else {
5582         status = map_queryfile_error(entry->status);
5583     }
5584     RxFreePool(entry);
5585 out:
5586 #ifdef ENABLE_TIMINGS
5587     t2 = KeQueryPerformanceCounter(NULL);
5588     InterlockedIncrement(&getattr.tops);
5589     InterlockedAdd64(&getattr.ticks, t2.QuadPart - t1.QuadPart);
5590 #ifdef ENABLE_INDV_TIMINGS
5591     DbgP("nfs41_QueryFileInformation delta = %d op=%d sum=%d\n",
5592         t2.QuadPart - t1.QuadPart, getattr.tops, getattr.ticks);
5593 #endif
5594 #endif
5595 #ifdef DEBUG_FILE_QUERY
5596     DbgEx();
5597 #endif
5598     return status;
5599 }
5600 
5601 NTSTATUS map_setfile_error(
5602     DWORD error)
5603 {
5604     switch (error) {
5605     case NO_ERROR:                      return STATUS_SUCCESS;
5606     case ERROR_NOT_EMPTY:               return STATUS_DIRECTORY_NOT_EMPTY;
5607     case ERROR_FILE_EXISTS:             return STATUS_OBJECT_NAME_COLLISION;
5608     case ERROR_FILE_NOT_FOUND:          return STATUS_OBJECT_NAME_NOT_FOUND;
5609     case ERROR_PATH_NOT_FOUND:          return STATUS_OBJECT_PATH_NOT_FOUND;
5610     case ERROR_ACCESS_DENIED:           return STATUS_ACCESS_DENIED;
5611     case ERROR_FILE_INVALID:            return STATUS_FILE_INVALID;
5612     case ERROR_NOT_SAME_DEVICE:         return STATUS_NOT_SAME_DEVICE;
5613     case ERROR_NOT_SUPPORTED:           return STATUS_NOT_IMPLEMENTED;
5614     case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
5615     case ERROR_NETNAME_DELETED:         return STATUS_NETWORK_NAME_DELETED;
5616     case ERROR_BUFFER_OVERFLOW:         return STATUS_INSUFFICIENT_RESOURCES;
5617     default:
5618         print_error("failed to map windows error %d to NTSTATUS; "
5619             "defaulting to STATUS_INVALID_PARAMETER\n", error);
5620     case ERROR_INVALID_PARAMETER:       return STATUS_INVALID_PARAMETER;
5621     }
5622 }
5623 
5624 NTSTATUS check_nfs41_setattr_args(
5625     IN PRX_CONTEXT RxContext)
5626 {
5627     NTSTATUS status = STATUS_SUCCESS;
5628     FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
5629     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
5630         NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
5631 
5632     if (pVNetRootContext->read_only) {
5633         print_error("check_nfs41_setattr_args: Read-only mount\n");
5634         status = STATUS_ACCESS_DENIED;
5635         goto out;
5636     }
5637 
5638     /* http://msdn.microsoft.com/en-us/library/ff469355(v=PROT.10).aspx
5639      * http://msdn.microsoft.com/en-us/library/ff469424(v=PROT.10).aspx
5640      * If Open.GrantedAccess does not contain FILE_WRITE_DATA, the operation
5641      * MUST be failed with STATUS_ACCESS_DENIED.
5642      */
5643     if (InfoClass == FileAllocationInformation ||
5644             InfoClass == FileEndOfFileInformation) {
5645         if (!(RxContext->pRelevantSrvOpen->DesiredAccess & FILE_WRITE_DATA)) {
5646             status = STATUS_ACCESS_DENIED;
5647             goto out;
5648         }
5649     }
5650     status = check_nfs41_dirquery_args(RxContext);
5651     if (status) goto out;
5652 
5653     switch (InfoClass) {
5654     case FileRenameInformation:
5655     {
5656         PFILE_RENAME_INFORMATION rinfo =
5657             (PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
5658         UNICODE_STRING dst = { (USHORT)rinfo->FileNameLength,
5659             (USHORT)rinfo->FileNameLength, rinfo->FileName };
5660 #ifdef DEBUG_FILE_SET
5661         DbgP("Attempting to rename to '%wZ'\n", &dst);
5662 #endif
5663         if (isFilenameTooLong(&dst, pVNetRootContext)) {
5664             status = STATUS_OBJECT_NAME_INVALID;
5665             goto out;
5666         }
5667         if (rinfo->RootDirectory) {
5668             status = STATUS_INVALID_PARAMETER;
5669             goto out;
5670         }
5671         break;
5672     }
5673     case FileLinkInformation:
5674     {
5675         PFILE_LINK_INFORMATION linfo =
5676             (PFILE_LINK_INFORMATION)RxContext->Info.Buffer;
5677         UNICODE_STRING dst = { (USHORT)linfo->FileNameLength,
5678             (USHORT)linfo->FileNameLength, linfo->FileName };
5679 #ifdef DEBUG_FILE_SET
5680         DbgP("Attempting to add link as '%wZ'\n", &dst);
5681 #endif
5682         if (isFilenameTooLong(&dst, pVNetRootContext)) {
5683             status = STATUS_OBJECT_NAME_INVALID;
5684             goto out;
5685         }
5686         if (linfo->RootDirectory) {
5687             status = STATUS_INVALID_PARAMETER;
5688             goto out;
5689         }
5690         break;
5691     }
5692     case FileDispositionInformation:
5693     {
5694         PFILE_DISPOSITION_INFORMATION dinfo =
5695             (PFILE_DISPOSITION_INFORMATION)RxContext->Info.Buffer;
5696         __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
5697         if (dinfo->DeleteFile && nfs41_fcb->DeletePending) {
5698             status = STATUS_DELETE_PENDING;
5699             goto out;
5700         }
5701         break;
5702     }
5703     case FileBasicInformation:
5704     case FileAllocationInformation:
5705     case FileEndOfFileInformation:
5706         break;
5707     default:
5708         print_error("nfs41_SetFileInformation: unhandled class %d\n", InfoClass);
5709         status = STATUS_NOT_SUPPORTED;
5710     }
5711 
5712 out:
5713     return status;
5714 }
5715 
5716 #ifdef __REACTOS__
5717 NTSTATUS NTAPI nfs41_SetFileInformation(
5718 #else
5719 NTSTATUS nfs41_SetFileInformation(
5720 #endif
5721     IN OUT PRX_CONTEXT RxContext)
5722 {
5723     NTSTATUS status = STATUS_INVALID_PARAMETER;
5724     nfs41_updowncall_entry *entry;
5725     FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
5726     FILE_RENAME_INFORMATION rinfo;
5727     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
5728     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
5729         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
5730     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
5731         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
5732     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
5733     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
5734 #ifdef ENABLE_TIMINGS
5735     LARGE_INTEGER t1, t2;
5736     t1 = KeQueryPerformanceCounter(NULL);
5737 #endif
5738 
5739 #ifdef DEBUG_FILE_SET
5740     DbgEn();
5741     print_debug_filedirquery_header(RxContext);
5742 #endif
5743 
5744     status = check_nfs41_setattr_args(RxContext);
5745     if (status) goto out;
5746 
5747     switch (InfoClass) {
5748     case FileDispositionInformation:
5749         {
5750             PFILE_DISPOSITION_INFORMATION dinfo =
5751                 (PFILE_DISPOSITION_INFORMATION)RxContext->Info.Buffer;
5752             if (dinfo->DeleteFile) {
5753                 nfs41_fcb->DeletePending = TRUE;
5754                 // we can delete directories right away
5755                 if (nfs41_fcb->StandardInfo.Directory)
5756                     break;
5757                 nfs41_fcb->StandardInfo.DeletePending = TRUE;
5758                 if (RxContext->pFcb->OpenCount > 1) {
5759                     rinfo.ReplaceIfExists = 0;
5760                     rinfo.RootDirectory = INVALID_HANDLE_VALUE;
5761                     rinfo.FileNameLength = 0;
5762                     rinfo.FileName[0] = L'\0';
5763                     InfoClass = FileRenameInformation;
5764                     nfs41_fcb->Renamed = TRUE;
5765                     break;
5766                 }
5767             } else {
5768                 /* section 4.3.3 of [FSBO]
5769                  * "file system behavior in the microsoft windows environment"
5770                  */
5771                 if (nfs41_fcb->DeletePending) {
5772                     nfs41_fcb->DeletePending = 0;
5773                     nfs41_fcb->StandardInfo.DeletePending = 0;
5774                 }
5775             }
5776             status = STATUS_SUCCESS;
5777             goto out;
5778         }
5779     case FileEndOfFileInformation:
5780         {
5781             PFILE_END_OF_FILE_INFORMATION info =
5782                 (PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
5783             nfs41_fcb->StandardInfo.AllocationSize =
5784                 nfs41_fcb->StandardInfo.EndOfFile = info->EndOfFile;
5785             break;
5786         }
5787     case FileRenameInformation:
5788         {
5789             /* noop if filename and destination are the same */
5790             PFILE_RENAME_INFORMATION prinfo =
5791                 (PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
5792             const UNICODE_STRING dst = { (USHORT)prinfo->FileNameLength,
5793                 (USHORT)prinfo->FileNameLength, prinfo->FileName };
5794             if (RtlCompareUnicodeString(&dst,
5795                     SrvOpen->pAlreadyPrefixedName, FALSE) == 0) {
5796                 status = STATUS_SUCCESS;
5797                 goto out;
5798             }
5799         }
5800     }
5801 
5802     status = nfs41_UpcallCreate(NFS41_FILE_SET, &nfs41_fobx->sec_ctx,
5803         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
5804         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
5805     if (status) goto out;
5806 
5807     entry->u.SetFile.InfoClass = InfoClass;
5808 
5809     /* original irp has infoclass for remove but we need to rename instead,
5810      * thus we changed the local variable infoclass */
5811     if (RxContext->Info.FileInformationClass == FileDispositionInformation &&
5812             InfoClass == FileRenameInformation) {
5813         entry->buf = &rinfo;
5814         entry->buf_len = sizeof(rinfo);
5815     } else {
5816         entry->buf = RxContext->Info.Buffer;
5817         entry->buf_len = RxContext->Info.Length;
5818     }
5819 #ifdef ENABLE_TIMINGS
5820     InterlockedIncrement(&setattr.sops);
5821     InterlockedAdd64(&setattr.size, entry->u.SetFile.buf_len);
5822 #endif
5823 
5824     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
5825     if (status) goto out;
5826 
5827     status = map_setfile_error(entry->status);
5828     if (!status) {
5829         if (!nfs41_fobx->deleg_type && entry->ChangeTime &&
5830                 (SrvOpen->DesiredAccess &
5831                 (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)))
5832             nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
5833         nfs41_fcb->changeattr = entry->ChangeTime;
5834     }
5835     RxFreePool(entry);
5836 out:
5837 #ifdef ENABLE_TIMINGS
5838     t2 = KeQueryPerformanceCounter(NULL);
5839     InterlockedIncrement(&setattr.tops);
5840     InterlockedAdd64(&setattr.ticks, t2.QuadPart - t1.QuadPart);
5841 #ifdef ENABLE_INDV_TIMINGS
5842     DbgP("nfs41_SetFileInformation delta = %d op=%d sum=%d\n",
5843         t2.QuadPart - t1.QuadPart, setattr.tops, setattr.ticks);
5844 #endif
5845 #endif
5846 #ifdef DEBUG_FILE_SET
5847     DbgEx();
5848 #endif
5849     return status;
5850 }
5851 
5852 NTSTATUS nfs41_SetFileInformationAtCleanup(
5853       IN OUT PRX_CONTEXT RxContext)
5854 {
5855     NTSTATUS status;
5856     DbgEn();
5857     status = nfs41_SetFileInformation(RxContext);
5858     DbgEx();
5859     return status;
5860 }
5861 
5862 #ifdef __REACTOS__
5863 NTSTATUS NTAPI nfs41_IsValidDirectory (
5864 #else
5865 NTSTATUS nfs41_IsValidDirectory (
5866 #endif
5867     IN OUT PRX_CONTEXT RxContext,
5868     IN PUNICODE_STRING DirectoryName)
5869 {
5870     return STATUS_SUCCESS;
5871 }
5872 
5873 #ifdef __REACTOS__
5874 NTSTATUS NTAPI nfs41_ComputeNewBufferingState(
5875 #else
5876 NTSTATUS nfs41_ComputeNewBufferingState(
5877 #endif
5878     IN OUT PMRX_SRV_OPEN pSrvOpen,
5879     IN PVOID pMRxContext,
5880     OUT ULONG *pNewBufferingState)
5881 {
5882     NTSTATUS status = STATUS_SUCCESS;
5883     ULONG flag = PtrToUlong(pMRxContext), oldFlags = pSrvOpen->BufferingFlags;
5884 
5885     switch(flag) {
5886     case DISABLE_CACHING:
5887         if (pSrvOpen->BufferingFlags &
5888             (FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED))
5889             pSrvOpen->BufferingFlags &=
5890                 ~(FCB_STATE_READBUFFERING_ENABLED |
5891                   FCB_STATE_READCACHING_ENABLED);
5892         if (pSrvOpen->BufferingFlags &
5893             (FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED))
5894             pSrvOpen->BufferingFlags &=
5895                 ~(FCB_STATE_WRITECACHING_ENABLED |
5896                   FCB_STATE_WRITEBUFFERING_ENABLED);
5897         pSrvOpen->BufferingFlags |= FCB_STATE_DISABLE_LOCAL_BUFFERING;
5898         break;
5899     case ENABLE_READ_CACHING:
5900         pSrvOpen->BufferingFlags |=
5901             (FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED);
5902         break;
5903     case ENABLE_WRITE_CACHING:
5904         pSrvOpen->BufferingFlags |=
5905             (FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
5906         break;
5907     case ENABLE_READWRITE_CACHING:
5908         pSrvOpen->BufferingFlags =
5909             (FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED |
5910             FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
5911     }
5912 #ifdef DEBUG_TIME_BASED_COHERENCY
5913     DbgP("nfs41_ComputeNewBufferingState: %wZ pSrvOpen %p Old %08x New %08x\n",
5914          pSrvOpen->pAlreadyPrefixedName, pSrvOpen, oldFlags,
5915          pSrvOpen->BufferingFlags);
5916     *pNewBufferingState = pSrvOpen->BufferingFlags;
5917 #endif
5918     return status;
5919 }
5920 
5921 void print_readwrite_args(
5922     PRX_CONTEXT RxContext)
5923 {
5924     PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
5925 
5926     print_debug_header(RxContext);
5927     DbgP("Bytecount 0x%x Byteoffset 0x%x Buffer %p\n",
5928         LowIoContext->ParamsFor.ReadWrite.ByteCount,
5929         LowIoContext->ParamsFor.ReadWrite.ByteOffset,
5930         LowIoContext->ParamsFor.ReadWrite.Buffer);
5931 }
5932 
5933 void enable_caching(
5934     PMRX_SRV_OPEN SrvOpen,
5935     PNFS41_FOBX nfs41_fobx,
5936     ULONGLONG ChangeTime,
5937     HANDLE session)
5938 {
5939     ULONG flag = 0;
5940     PLIST_ENTRY pEntry;
5941     nfs41_fcb_list_entry *cur;
5942     BOOLEAN found = FALSE;
5943 
5944     if (SrvOpen->DesiredAccess & FILE_READ_DATA)
5945         flag = ENABLE_READ_CACHING;
5946     if ((SrvOpen->DesiredAccess & FILE_WRITE_DATA) &&
5947             !nfs41_fobx->write_thru)
5948         flag = ENABLE_WRITE_CACHING;
5949     if ((SrvOpen->DesiredAccess & FILE_READ_DATA) &&
5950             (SrvOpen->DesiredAccess & FILE_WRITE_DATA) &&
5951             !nfs41_fobx->write_thru)
5952         flag = ENABLE_READWRITE_CACHING;
5953 
5954 #if defined(DEBUG_TIME_BASED_COHERENCY) || \
5955         defined(DEBUG_WRITE) || defined(DEBUG_READ)
5956     print_caching_level(1, flag, SrvOpen->pAlreadyPrefixedName);
5957 #endif
5958 
5959     if (!flag)
5960         return;
5961 
5962     RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
5963 
5964     ExAcquireFastMutex(&fcblistLock);
5965     pEntry = openlist.head.Flink;
5966     while (!IsListEmpty(&openlist.head)) {
5967         cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
5968                 nfs41_fcb_list_entry, next);
5969         if (cur->fcb == SrvOpen->pFcb) {
5970 #ifdef DEBUG_TIME_BASED_COHERENCY
5971             DbgP("enable_caching: Looked&Found match for fcb=%p %wZ\n",
5972                 SrvOpen->pFcb, SrvOpen->pAlreadyPrefixedName);
5973 #endif
5974             cur->skip = FALSE;
5975             found = TRUE;
5976             break;
5977         }
5978         if (pEntry->Flink == &openlist.head) {
5979 #ifdef DEBUG_TIME_BASED_COHERENCY
5980             DbgP("enable_caching: reached EOL looking for fcb=%p %wZ\n",
5981                 SrvOpen->pFcb, SrvOpen->pAlreadyPrefixedName);
5982 #endif
5983             break;
5984         }
5985         pEntry = pEntry->Flink;
5986     }
5987     if (!found && nfs41_fobx->deleg_type) {
5988         nfs41_fcb_list_entry *oentry;
5989 #ifdef DEBUG_TIME_BASED_COHERENCY
5990         DbgP("enable_caching: delegation recalled: srv_open=%p\n", SrvOpen);
5991 #endif
5992         oentry = RxAllocatePoolWithTag(NonPagedPool,
5993             sizeof(nfs41_fcb_list_entry), NFS41_MM_POOLTAG_OPEN);
5994         if (oentry == NULL) return;
5995         oentry->fcb = SrvOpen->pFcb;
5996         oentry->session = session;
5997         oentry->nfs41_fobx = nfs41_fobx;
5998         oentry->ChangeTime = ChangeTime;
5999         oentry->skip = FALSE;
6000         InsertTailList(&openlist.head, &oentry->next);
6001         nfs41_fobx->deleg_type = 0;
6002     }
6003     ExReleaseFastMutex(&fcblistLock);
6004 }
6005 
6006 NTSTATUS map_readwrite_errors(
6007     DWORD status)
6008 {
6009     switch (status) {
6010     case ERROR_ACCESS_DENIED:           return STATUS_ACCESS_DENIED;
6011     case ERROR_HANDLE_EOF:              return STATUS_END_OF_FILE;
6012     case ERROR_FILE_INVALID:            return STATUS_FILE_INVALID;
6013     case ERROR_INVALID_PARAMETER:       return STATUS_INVALID_PARAMETER;
6014     case ERROR_LOCK_VIOLATION:          return STATUS_FILE_LOCK_CONFLICT;
6015     case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
6016     case ERROR_NETNAME_DELETED:         return STATUS_NETWORK_NAME_DELETED;
6017     default:
6018         print_error("failed to map windows error %d to NTSTATUS; "
6019             "defaulting to STATUS_NET_WRITE_FAULT\n", status);
6020     case ERROR_NET_WRITE_FAULT:         return STATUS_NET_WRITE_FAULT;
6021     }
6022 }
6023 
6024 NTSTATUS check_nfs41_read_args(
6025     IN PRX_CONTEXT RxContext)
6026 {
6027     if (!RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer)
6028         return STATUS_INVALID_USER_BUFFER;
6029     return STATUS_SUCCESS;
6030 }
6031 
6032 #ifdef __REACTOS__
6033 NTSTATUS NTAPI nfs41_Read(
6034 #else
6035 NTSTATUS nfs41_Read(
6036 #endif
6037     IN OUT PRX_CONTEXT RxContext)
6038 {
6039     NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
6040     nfs41_updowncall_entry *entry;
6041     BOOLEAN async = FALSE;
6042     PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
6043     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
6044     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
6045         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
6046     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
6047         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
6048     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
6049     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
6050     DWORD io_delay;
6051 #ifdef ENABLE_TIMINGS
6052     LARGE_INTEGER t1, t2;
6053     t1 = KeQueryPerformanceCounter(NULL);
6054 #endif
6055 
6056 #ifdef DEBUG_READ
6057     DbgEn();
6058     print_readwrite_args(RxContext);
6059 #endif
6060     status = check_nfs41_read_args(RxContext);
6061     if (status) goto out;
6062 
6063     status = nfs41_UpcallCreate(NFS41_READ, &nfs41_fobx->sec_ctx,
6064         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
6065         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
6066     if (status) goto out;
6067 
6068     entry->u.ReadWrite.MdlAddress = LowIoContext->ParamsFor.ReadWrite.Buffer;
6069     entry->buf_len = LowIoContext->ParamsFor.ReadWrite.ByteCount;
6070     entry->u.ReadWrite.offset = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
6071     if (FlagOn(RxContext->CurrentIrpSp->FileObject->Flags,
6072             FO_SYNCHRONOUS_IO) == FALSE) {
6073         entry->u.ReadWrite.rxcontext = RxContext;
6074         async = entry->async_op = TRUE;
6075     }
6076 
6077     /* assume network speed is 100MB/s and disk speed is 100MB/s so add
6078      * time to transfer requested bytes over the network and read from disk
6079      */
6080     io_delay = pVNetRootContext->timeout + 2 * entry->buf_len / 104857600;
6081     status = nfs41_UpcallWaitForReply(entry, io_delay);
6082     if (status) goto out;
6083 
6084     if (async) {
6085 #ifdef DEBUG_READ
6086         DbgP("This is asynchronous read, returning control back to the user\n");
6087 #endif
6088         status = STATUS_PENDING;
6089         goto out;
6090     }
6091 
6092     if (entry->status == NO_ERROR) {
6093 #ifdef ENABLE_TIMINGS
6094         InterlockedIncrement(&read.sops);
6095         InterlockedAdd64(&read.size, entry->u.ReadWrite.len);
6096 #endif
6097         status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
6098         RxContext->IoStatusBlock.Information = entry->buf_len;
6099 
6100         if ((!BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,
6101                 LOWIO_READWRITEFLAG_PAGING_IO) &&
6102                 (SrvOpen->DesiredAccess & FILE_READ_DATA) &&
6103                 !pVNetRootContext->nocache && !nfs41_fobx->nocache &&
6104                 !(SrvOpen->BufferingFlags &
6105                 (FCB_STATE_READBUFFERING_ENABLED |
6106                  FCB_STATE_READCACHING_ENABLED)))) {
6107             enable_caching(SrvOpen, nfs41_fobx, nfs41_fcb->changeattr,
6108                 pVNetRootContext->session);
6109         }
6110     } else {
6111         status = map_readwrite_errors(entry->status);
6112         RxContext->CurrentIrp->IoStatus.Status = status;
6113         RxContext->IoStatusBlock.Information = 0;
6114     }
6115     RxFreePool(entry);
6116 out:
6117 #ifdef ENABLE_TIMINGS
6118     t2 = KeQueryPerformanceCounter(NULL);
6119     InterlockedIncrement(&read.tops);
6120     InterlockedAdd64(&read.ticks, t2.QuadPart - t1.QuadPart);
6121 #ifdef ENABLE_INDV_TIMINGS
6122     DbgP("nfs41_Read delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
6123         read.tops, read.ticks);
6124 #endif
6125 #endif
6126 #ifdef DEBUG_READ
6127     DbgEx();
6128 #endif
6129     return status;
6130 }
6131 
6132 NTSTATUS check_nfs41_write_args(
6133     IN PRX_CONTEXT RxContext)
6134 {
6135     NTSTATUS status = STATUS_SUCCESS;
6136     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
6137         NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
6138 
6139     if (!RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer) {
6140         status = STATUS_INVALID_USER_BUFFER;
6141         goto out;
6142     }
6143 
6144     if (pVNetRootContext->read_only) {
6145         print_error("check_nfs41_write_args: Read-only mount\n");
6146         status = STATUS_ACCESS_DENIED;
6147         goto out;
6148     }
6149 out:
6150     return status;
6151 }
6152 
6153 #ifdef __REACTOS__
6154 NTSTATUS NTAPI nfs41_Write(
6155 #else
6156 NTSTATUS nfs41_Write(
6157 #endif
6158     IN OUT PRX_CONTEXT RxContext)
6159 {
6160     NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
6161     nfs41_updowncall_entry *entry;
6162     BOOLEAN async = FALSE;
6163     PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
6164     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
6165     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
6166         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
6167     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
6168         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
6169     __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
6170     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
6171     DWORD io_delay;
6172 #ifdef ENABLE_TIMINGS
6173     LARGE_INTEGER t1, t2;
6174     t1 = KeQueryPerformanceCounter(NULL);
6175 #endif
6176 
6177 #ifdef DEBUG_WRITE
6178     DbgEn();
6179     print_readwrite_args(RxContext);
6180 #endif
6181 
6182     status = check_nfs41_write_args(RxContext);
6183     if (status) goto out;
6184 
6185     status = nfs41_UpcallCreate(NFS41_WRITE, &nfs41_fobx->sec_ctx,
6186         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
6187         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
6188     if (status) goto out;
6189 
6190     entry->u.ReadWrite.MdlAddress = LowIoContext->ParamsFor.ReadWrite.Buffer;
6191     entry->buf_len = LowIoContext->ParamsFor.ReadWrite.ByteCount;
6192     entry->u.ReadWrite.offset = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
6193 
6194     if (FlagOn(RxContext->CurrentIrpSp->FileObject->Flags,
6195             FO_SYNCHRONOUS_IO) == FALSE) {
6196         entry->u.ReadWrite.rxcontext = RxContext;
6197         async = entry->async_op = TRUE;
6198     }
6199 
6200     /* assume network speed is 100MB/s and disk speed is 100MB/s so add
6201      * time to transfer requested bytes over the network and write to disk
6202      */
6203     io_delay = pVNetRootContext->timeout + 2 * entry->buf_len / 104857600;
6204     status = nfs41_UpcallWaitForReply(entry, io_delay);
6205     if (status) goto out;
6206 
6207     if (async) {
6208 #ifdef DEBUG_WRITE
6209         DbgP("This is asynchronous write, returning control back to the user\n");
6210 #endif
6211         status = STATUS_PENDING;
6212         goto out;
6213     }
6214 
6215     if (entry->status == NO_ERROR) {
6216         //update cached file attributes
6217 #ifdef ENABLE_TIMINGS
6218         InterlockedIncrement(&write.sops);
6219         InterlockedAdd64(&write.size, entry->u.ReadWrite.len);
6220 #endif
6221         nfs41_fcb->StandardInfo.EndOfFile.QuadPart = entry->buf_len +
6222             entry->u.ReadWrite.offset;
6223         status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
6224         RxContext->IoStatusBlock.Information = entry->buf_len;
6225         nfs41_fcb->changeattr = entry->ChangeTime;
6226 
6227         //re-enable write buffering
6228         if (!BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,
6229                 LOWIO_READWRITEFLAG_PAGING_IO) &&
6230                 (SrvOpen->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) &&
6231                 !pVNetRootContext->write_thru &&
6232                 !pVNetRootContext->nocache &&
6233                 !nfs41_fobx->write_thru && !nfs41_fobx->nocache &&
6234                 !(SrvOpen->BufferingFlags &
6235                 (FCB_STATE_WRITEBUFFERING_ENABLED |
6236                  FCB_STATE_WRITECACHING_ENABLED))) {
6237             enable_caching(SrvOpen, nfs41_fobx, nfs41_fcb->changeattr,
6238                 pVNetRootContext->session);
6239         } else if (!nfs41_fobx->deleg_type)
6240             nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
6241 
6242     } else {
6243         status = map_readwrite_errors(entry->status);
6244         RxContext->CurrentIrp->IoStatus.Status = status;
6245         RxContext->IoStatusBlock.Information = 0;
6246     }
6247     RxFreePool(entry);
6248 out:
6249 #ifdef ENABLE_TIMINGS
6250     t2 = KeQueryPerformanceCounter(NULL);
6251     InterlockedIncrement(&write.tops);
6252     InterlockedAdd64(&write.ticks, t2.QuadPart - t1.QuadPart);
6253 #ifdef ENABLE_INDV_TIMINGS
6254     DbgP("nfs41_Write delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
6255         write.tops, write.ticks);
6256 #endif
6257 #endif
6258 #ifdef DEBUG_WRITE
6259     DbgEx();
6260 #endif
6261     return status;
6262 }
6263 
6264 #ifdef __REACTOS__
6265 NTSTATUS NTAPI nfs41_IsLockRealizable(
6266 #else
6267 NTSTATUS nfs41_IsLockRealizable(
6268 #endif
6269     IN OUT PMRX_FCB pFcb,
6270     IN PLARGE_INTEGER  ByteOffset,
6271     IN PLARGE_INTEGER  Length,
6272     IN ULONG  LowIoLockFlags)
6273 {
6274     NTSTATUS status = STATUS_SUCCESS;
6275 #ifdef DEBUG_LOCK
6276     DbgEn();
6277     DbgP("offset 0x%llx, length 0x%llx, exclusive=%u, blocking=%u\n",
6278         ByteOffset->QuadPart,Length->QuadPart,
6279         BooleanFlagOn(LowIoLockFlags, SL_EXCLUSIVE_LOCK),
6280         !BooleanFlagOn(LowIoLockFlags, SL_FAIL_IMMEDIATELY));
6281 #endif
6282 
6283     /* NFS lock operations with length=0 MUST fail with NFS4ERR_INVAL */
6284     if (Length->QuadPart == 0)
6285         status = STATUS_NOT_SUPPORTED;
6286 
6287 #ifdef DEBUG_LOCK
6288     DbgEx();
6289 #endif
6290     return status;
6291 }
6292 
6293 NTSTATUS map_lock_errors(
6294     DWORD status)
6295 {
6296     switch (status) {
6297     case NO_ERROR:                  return STATUS_SUCCESS;
6298     case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
6299     case ERROR_LOCK_FAILED:         return STATUS_LOCK_NOT_GRANTED;
6300     case ERROR_NOT_LOCKED:          return STATUS_RANGE_NOT_LOCKED;
6301     case ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: return STATUS_UNSUCCESSFUL;
6302     case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
6303     case ERROR_SHARING_VIOLATION:   return STATUS_SHARING_VIOLATION;
6304     case ERROR_FILE_INVALID:        return STATUS_FILE_INVALID;
6305     /* if we return ERROR_INVALID_PARAMETER, Windows translates that to
6306      * success!! */
6307     case ERROR_INVALID_PARAMETER:   return STATUS_LOCK_NOT_GRANTED;
6308     default:
6309         print_error("failed to map windows error %d to NTSTATUS; "
6310             "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
6311     case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
6312     }
6313 }
6314 
6315 void print_lock_args(
6316     PRX_CONTEXT RxContext)
6317 {
6318     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
6319     const ULONG flags = LowIoContext->ParamsFor.Locks.Flags;
6320     print_debug_header(RxContext);
6321     DbgP("offset 0x%llx, length 0x%llx, exclusive=%u, blocking=%u\n",
6322         LowIoContext->ParamsFor.Locks.ByteOffset,
6323         LowIoContext->ParamsFor.Locks.Length,
6324         BooleanFlagOn(flags, SL_EXCLUSIVE_LOCK),
6325         !BooleanFlagOn(flags, SL_FAIL_IMMEDIATELY));
6326 }
6327 
6328 
6329 /* use exponential backoff between polls for blocking locks */
6330 #define MSEC_TO_RELATIVE_WAIT   (-10000)
6331 #define MIN_LOCK_POLL_WAIT      (500 * MSEC_TO_RELATIVE_WAIT) /* 500ms */
6332 #define MAX_LOCK_POLL_WAIT      (30000 * MSEC_TO_RELATIVE_WAIT) /* 30s */
6333 
6334 void denied_lock_backoff(
6335     IN OUT PLARGE_INTEGER delay)
6336 {
6337     if (delay->QuadPart == 0)
6338         delay->QuadPart = MIN_LOCK_POLL_WAIT;
6339     else
6340         delay->QuadPart <<= 1;
6341 
6342     if (delay->QuadPart < MAX_LOCK_POLL_WAIT)
6343         delay->QuadPart = MAX_LOCK_POLL_WAIT;
6344 }
6345 
6346 #ifdef __REACTOS__
6347 NTSTATUS NTAPI nfs41_Lock(
6348 #else
6349 NTSTATUS nfs41_Lock(
6350 #endif
6351     IN OUT PRX_CONTEXT RxContext)
6352 {
6353     NTSTATUS status = STATUS_SUCCESS;
6354     nfs41_updowncall_entry *entry;
6355     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
6356     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
6357     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
6358     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
6359         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
6360     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
6361         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
6362     const ULONG flags = LowIoContext->ParamsFor.Locks.Flags;
6363 #ifdef _MSC_VER
6364     LARGE_INTEGER poll_delay = {0};
6365 #else
6366     LARGE_INTEGER poll_delay;
6367 #endif
6368 #ifdef ENABLE_TIMINGS
6369     LARGE_INTEGER t1, t2;
6370     t1 = KeQueryPerformanceCounter(NULL);
6371 #endif
6372 
6373 #ifndef _MSC_VER
6374     poll_delay.QuadPart = 0;
6375 #endif
6376 
6377 #ifdef DEBUG_LOCK
6378     DbgEn();
6379     print_lock_args(RxContext);
6380 #endif
6381 
6382 /*  RxReleaseFcbResourceForThreadInMRx(RxContext, RxContext->pFcb,
6383         LowIoContext->ResourceThreadId); */
6384 
6385     status = nfs41_UpcallCreate(NFS41_LOCK, &nfs41_fobx->sec_ctx,
6386         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
6387         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
6388     if (status) goto out;
6389 
6390     entry->u.Lock.offset = LowIoContext->ParamsFor.Locks.ByteOffset;
6391     entry->u.Lock.length = LowIoContext->ParamsFor.Locks.Length;
6392     entry->u.Lock.exclusive = BooleanFlagOn(flags, SL_EXCLUSIVE_LOCK);
6393     entry->u.Lock.blocking = !BooleanFlagOn(flags, SL_FAIL_IMMEDIATELY);
6394 
6395 retry_upcall:
6396     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
6397     if (status) goto out;
6398 
6399     /* blocking locks keep trying until it succeeds */
6400     if (entry->status == ERROR_LOCK_FAILED && entry->u.Lock.blocking) {
6401         denied_lock_backoff(&poll_delay);
6402         DbgP("returned ERROR_LOCK_FAILED; retrying in %llums\n",
6403             poll_delay.QuadPart / MSEC_TO_RELATIVE_WAIT);
6404         KeDelayExecutionThread(KernelMode, FALSE, &poll_delay);
6405         entry->state = NFS41_WAITING_FOR_UPCALL;
6406         goto retry_upcall;
6407     }
6408 
6409     status = map_lock_errors(entry->status);
6410     RxContext->CurrentIrp->IoStatus.Status = status;
6411 
6412     RxFreePool(entry);
6413 out:
6414 #ifdef ENABLE_TIMINGS
6415     t2 = KeQueryPerformanceCounter(NULL);
6416     InterlockedIncrement(&lock.tops);
6417     InterlockedAdd64(&lock.ticks, t2.QuadPart - t1.QuadPart);
6418 #ifdef ENABLE_INDV_TIMINGS
6419     DbgP("nfs41_Lock delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
6420         lock.tops, lock.ticks);
6421 #endif
6422 #endif
6423 #ifdef DEBUG_LOCK
6424     DbgEx();
6425 #endif
6426     return status;
6427 }
6428 
6429 void print_unlock_args(
6430     PRX_CONTEXT RxContext)
6431 {
6432     PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
6433     print_debug_header(RxContext);
6434     if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) {
6435         PLOWIO_LOCK_LIST lock = LowIoContext->ParamsFor.Locks.LockList;
6436         DbgP("LOWIO_OP_UNLOCK_MULTIPLE:");
6437         while (lock) {
6438             DbgP(" (offset=%llu, length=%llu)", lock->ByteOffset, lock->Length);
6439             lock = lock->Next;
6440         }
6441         DbgP("\n");
6442     } else {
6443         DbgP("LOWIO_OP_UNLOCK: offset=%llu, length=%llu\n",
6444             LowIoContext->ParamsFor.Locks.ByteOffset,
6445             LowIoContext->ParamsFor.Locks.Length);
6446     }
6447 }
6448 
6449 __inline ULONG unlock_list_count(
6450     PLOWIO_LOCK_LIST lock)
6451 {
6452     ULONG count = 0;
6453     while (lock) {
6454         count++;
6455         lock = lock->Next;
6456     }
6457     return count;
6458 }
6459 
6460 #ifdef __REACTOS__
6461 NTSTATUS NTAPI nfs41_Unlock(
6462 #else
6463 NTSTATUS nfs41_Unlock(
6464 #endif
6465     IN OUT PRX_CONTEXT RxContext)
6466 {
6467     NTSTATUS status = STATUS_SUCCESS;
6468     nfs41_updowncall_entry *entry;
6469     PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
6470     __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
6471     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
6472     __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
6473         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
6474     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
6475         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
6476 #ifdef ENABLE_TIMINGS
6477     LARGE_INTEGER t1, t2;
6478     t1 = KeQueryPerformanceCounter(NULL);
6479 #endif
6480 #ifdef DEBUG_LOCK
6481     DbgEn();
6482     print_lock_args(RxContext);
6483 #endif
6484 
6485 /*  RxReleaseFcbResourceForThreadInMRx(RxContext, RxContext->pFcb,
6486         LowIoContext->ResourceThreadId); */
6487 
6488     status = nfs41_UpcallCreate(NFS41_UNLOCK, &nfs41_fobx->sec_ctx,
6489         pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
6490         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
6491     if (status) goto out;
6492 
6493     if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) {
6494         entry->u.Unlock.count = unlock_list_count(
6495             LowIoContext->ParamsFor.Locks.LockList);
6496         RtlCopyMemory(&entry->u.Unlock.locks,
6497             LowIoContext->ParamsFor.Locks.LockList,
6498             sizeof(LOWIO_LOCK_LIST));
6499     } else {
6500         entry->u.Unlock.count = 1;
6501         entry->u.Unlock.locks.ByteOffset =
6502             LowIoContext->ParamsFor.Locks.ByteOffset;
6503         entry->u.Unlock.locks.Length =
6504             LowIoContext->ParamsFor.Locks.Length;
6505     }
6506 
6507     status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
6508     if (status) goto out;
6509 
6510     status = map_lock_errors(entry->status);
6511     RxContext->CurrentIrp->IoStatus.Status = status;
6512     RxFreePool(entry);
6513 out:
6514 #ifdef ENABLE_TIMINGS
6515     t2 = KeQueryPerformanceCounter(NULL);
6516     InterlockedIncrement(&unlock.tops);
6517     InterlockedAdd64(&unlock.ticks, t2.QuadPart - t1.QuadPart);
6518 #ifdef ENABLE_INDV_TIMINGS
6519     DbgP("nfs41_Unlock delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
6520         unlock.tops, unlock.ticks);
6521 #endif
6522 #endif
6523 #ifdef DEBUG_LOCK
6524     DbgEx();
6525 #endif
6526     return status;
6527 }
6528 
6529 NTSTATUS map_symlink_errors(
6530     NTSTATUS status)
6531 {
6532     switch (status) {
6533     case NO_ERROR:                  return STATUS_SUCCESS;
6534     case ERROR_INVALID_REPARSE_DATA: return STATUS_IO_REPARSE_DATA_INVALID;
6535     case ERROR_NOT_A_REPARSE_POINT: return STATUS_NOT_A_REPARSE_POINT;
6536     case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
6537     case ERROR_NOT_EMPTY:           return STATUS_DIRECTORY_NOT_EMPTY;
6538     case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
6539     case ERROR_INSUFFICIENT_BUFFER: return STATUS_BUFFER_TOO_SMALL;
6540     case STATUS_BUFFER_TOO_SMALL:
6541     case ERROR_BUFFER_OVERFLOW:     return STATUS_BUFFER_OVERFLOW;
6542     default:
6543         print_error("failed to map windows error %d to NTSTATUS; "
6544             "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
6545     case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
6546     }
6547 }
6548 
6549 void print_reparse_buffer(
6550     PREPARSE_DATA_BUFFER Reparse)
6551 {
6552     UNICODE_STRING name;
6553     DbgP("ReparseTag:           %08X\n", Reparse->ReparseTag);
6554     DbgP("ReparseDataLength:    %8u\n", Reparse->ReparseDataLength);
6555     DbgP("Reserved:             %8u\n", Reparse->Reserved);
6556     DbgP("SubstituteNameOffset: %8u\n",
6557          Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset);
6558     DbgP("SubstituteNameLength: %8u\n",
6559          Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength);
6560     DbgP("PrintNameOffset:      %8u\n",
6561          Reparse->SymbolicLinkReparseBuffer.PrintNameOffset);
6562     DbgP("PrintNameLength:      %8u\n",
6563          Reparse->SymbolicLinkReparseBuffer.PrintNameLength);
6564     DbgP("Flags:                %08X\n",
6565          Reparse->SymbolicLinkReparseBuffer.Flags);
6566 
6567     name.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[
6568         Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
6569     name.MaximumLength = name.Length =
6570         Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength;
6571     DbgP("SubstituteName:       %wZ\n", &name);
6572 
6573     name.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[
6574         Reparse->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
6575     name.MaximumLength = name.Length =
6576         Reparse->SymbolicLinkReparseBuffer.PrintNameLength;
6577     DbgP("PrintName:            %wZ\n", &name);
6578 }
6579 
6580 NTSTATUS check_nfs41_setreparse_args(
6581     IN PRX_CONTEXT RxContext)
6582 {
6583     NTSTATUS status = STATUS_SUCCESS;
6584     __notnull XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
6585     __notnull PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)FsCtl->pInputBuffer;
6586     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
6587     __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
6588         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
6589     const ULONG HeaderLen = REPARSE_DATA_BUFFER_HEADER_SIZE;
6590 
6591     /* access checks */
6592     if (VNetRootContext->read_only) {
6593         status = STATUS_MEDIA_WRITE_PROTECTED;
6594         goto out;
6595     }
6596     if (!(SrvOpen->DesiredAccess & (FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES))) {
6597         status = STATUS_ACCESS_DENIED;
6598         goto out;
6599     }
6600 
6601     /* must have a filename longer than vnetroot name,
6602      * or it's trying to operate on the volume itself */
6603     if (is_root_directory(RxContext)) {
6604         status = STATUS_INVALID_PARAMETER;
6605         goto out;
6606     }
6607     if (FsCtl->pOutputBuffer != NULL) {
6608         status = STATUS_INVALID_PARAMETER;
6609         goto out;
6610     }
6611 
6612     /* validate input buffer and length */
6613     if (!Reparse) {
6614         status = STATUS_INVALID_BUFFER_SIZE;
6615         goto out;
6616     }
6617 
6618     if (FsCtl->InputBufferLength < HeaderLen ||
6619             FsCtl->InputBufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) {
6620         status = STATUS_IO_REPARSE_DATA_INVALID;
6621         goto out;
6622     }
6623     if (FsCtl->InputBufferLength != HeaderLen + Reparse->ReparseDataLength) {
6624         status = STATUS_IO_REPARSE_DATA_INVALID;
6625         goto out;
6626     }
6627 
6628     /* validate reparse tag */
6629     if (!IsReparseTagValid(Reparse->ReparseTag)) {
6630         status = STATUS_IO_REPARSE_TAG_INVALID;
6631         goto out;
6632     }
6633     if (Reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
6634         status = STATUS_IO_REPARSE_TAG_MISMATCH;
6635         goto out;
6636     }
6637 out:
6638     return status;
6639 }
6640 
6641 NTSTATUS nfs41_SetReparsePoint(
6642     IN OUT PRX_CONTEXT RxContext)
6643 {
6644     NTSTATUS status;
6645     UNICODE_STRING TargetName;
6646     __notnull XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
6647     __notnull PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)FsCtl->pInputBuffer;
6648     __notnull PNFS41_FOBX Fobx = NFS41GetFobxExtension(RxContext->pFobx);
6649     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
6650     __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
6651         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
6652     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
6653         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
6654     nfs41_updowncall_entry *entry;
6655 
6656 #ifdef DEBUG_SYMLINK
6657     DbgEn();
6658     print_debug_header(RxContext);
6659     print_reparse_buffer(Reparse);
6660 #endif
6661     status = check_nfs41_setreparse_args(RxContext);
6662     if (status) goto out;
6663 
6664     TargetName.MaximumLength = TargetName.Length =
6665         Reparse->SymbolicLinkReparseBuffer.PrintNameLength;
6666     TargetName.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[
6667         Reparse->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
6668 
6669     status = nfs41_UpcallCreate(NFS41_SYMLINK, &Fobx->sec_ctx,
6670         VNetRootContext->session, Fobx->nfs41_open_state,
6671         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
6672     if (status) goto out;
6673 
6674     entry->u.Symlink.target = &TargetName;
6675     entry->u.Symlink.set = TRUE;
6676 
6677     status = nfs41_UpcallWaitForReply(entry, VNetRootContext->timeout);
6678     if (status) goto out;
6679 
6680     status = map_symlink_errors(entry->status);
6681     RxFreePool(entry);
6682 out:
6683 #ifdef DEBUG_SYMLINK
6684     DbgEx();
6685 #endif
6686     return status;
6687 }
6688 
6689 NTSTATUS check_nfs41_getreparse_args(
6690     PRX_CONTEXT RxContext)
6691 {
6692     NTSTATUS status = STATUS_SUCCESS;
6693     XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
6694     const USHORT HeaderLen = FIELD_OFFSET(REPARSE_DATA_BUFFER,
6695         SymbolicLinkReparseBuffer.PathBuffer);
6696 
6697     /* must have a filename longer than vnetroot name,
6698      * or it's trying to operate on the volume itself */
6699     if (is_root_directory(RxContext)) {
6700         status = STATUS_INVALID_PARAMETER;
6701         goto out;
6702     }
6703     /* ifs reparse tests expect STATUS_INVALID_PARAMETER,
6704      * but 'dir' passes a buffer here when querying symlinks
6705     if (FsCtl->pInputBuffer != NULL) {
6706         status = STATUS_INVALID_PARAMETER;
6707         goto out;
6708     } */
6709     if (!FsCtl->pOutputBuffer) {
6710         status = STATUS_INVALID_USER_BUFFER;
6711         goto out;
6712     }
6713     if (!BooleanFlagOn(RxContext->pFcb->Attributes,
6714             FILE_ATTRIBUTE_REPARSE_POINT)) {
6715         status = STATUS_NOT_A_REPARSE_POINT;
6716         DbgP("FILE_ATTRIBUTE_REPARSE_POINT is not set!\n");
6717         goto out;
6718     }
6719 
6720     if (FsCtl->OutputBufferLength < HeaderLen) {
6721         RxContext->InformationToReturn = HeaderLen;
6722         status = STATUS_BUFFER_TOO_SMALL;
6723         goto out;
6724     }
6725 out:
6726     return status;
6727 }
6728 
6729 NTSTATUS nfs41_GetReparsePoint(
6730     IN OUT PRX_CONTEXT RxContext)
6731 {
6732     NTSTATUS status;
6733     UNICODE_STRING TargetName;
6734     XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
6735     __notnull PNFS41_FOBX Fobx = NFS41GetFobxExtension(RxContext->pFobx);
6736     __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
6737     __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
6738         NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
6739     __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
6740         NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
6741     nfs41_updowncall_entry *entry;
6742     const USHORT HeaderLen = FIELD_OFFSET(REPARSE_DATA_BUFFER,
6743         SymbolicLinkReparseBuffer.PathBuffer);
6744 
6745 #ifdef DEBUG_SYMLINK
6746     DbgEn();
6747 #endif
6748     status = check_nfs41_getreparse_args(RxContext);
6749     if (status) goto out;
6750 
6751     TargetName.Buffer = (PWCH)((PBYTE)FsCtl->pOutputBuffer + HeaderLen);
6752     TargetName.MaximumLength = (USHORT)min(FsCtl->OutputBufferLength -
6753         HeaderLen, 0xFFFF);
6754 
6755     status = nfs41_UpcallCreate(NFS41_SYMLINK, &Fobx->sec_ctx,
6756         VNetRootContext->session, Fobx->nfs41_open_state,
6757         pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
6758     if (status) goto out;
6759 
6760     entry->u.Symlink.target = &TargetName;
6761     entry->u.Symlink.set = FALSE;
6762 
6763     status = nfs41_UpcallWaitForReply(entry, VNetRootContext->timeout);
6764     if (status) goto out;
6765 
6766     status = map_symlink_errors(entry->status);
6767     if (status == STATUS_SUCCESS) {
6768         /* fill in the output buffer */
6769         PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)
6770             FsCtl->pOutputBuffer;
6771         Reparse->ReparseTag = IO_REPARSE_TAG_SYMLINK;
6772         Reparse->ReparseDataLength = HeaderLen + TargetName.Length -
6773             REPARSE_DATA_BUFFER_HEADER_SIZE;
6774         Reparse->Reserved = 0;
6775         Reparse->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
6776         /* PrintName and SubstituteName point to the same string */
6777         Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
6778         Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength =
6779             TargetName.Length;
6780         Reparse->SymbolicLinkReparseBuffer.PrintNameOffset = 0;
6781         Reparse->SymbolicLinkReparseBuffer.PrintNameLength = TargetName.Length;
6782         print_reparse_buffer(Reparse);
6783 
6784         RxContext->IoStatusBlock.Information = HeaderLen + TargetName.Length;
6785     } else if (status == STATUS_BUFFER_TOO_SMALL) {
6786         RxContext->InformationToReturn = HeaderLen + TargetName.Length;
6787     }
6788     RxFreePool(entry);
6789 out:
6790 #ifdef DEBUG_SYMLINK
6791     DbgEx();
6792 #endif
6793     return status;
6794 }
6795 
6796 #ifdef __REACTOS__
6797 NTSTATUS NTAPI nfs41_FsCtl(
6798 #else
6799 NTSTATUS nfs41_FsCtl(
6800 #endif
6801     IN OUT PRX_CONTEXT RxContext)
6802 {
6803     NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
6804 #ifdef DEBUG_MISC
6805     DbgEn();
6806     print_debug_header(RxContext);
6807 #endif
6808     switch (RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode) {
6809     case FSCTL_SET_REPARSE_POINT:
6810         status = nfs41_SetReparsePoint(RxContext);
6811         break;
6812 
6813     case FSCTL_GET_REPARSE_POINT:
6814         status = nfs41_GetReparsePoint(RxContext);
6815         break;
6816 #ifdef DEBUG_MISC
6817     default:
6818         DbgP("FsControlCode: %d\n",
6819              RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode);
6820 #endif
6821     }
6822 #ifdef DEBUG_MISC
6823     DbgEx();
6824 #endif
6825     return status;
6826 }
6827 
6828 #ifdef __REACTOS__
6829 NTSTATUS NTAPI nfs41_CompleteBufferingStateChangeRequest(
6830 #else
6831 NTSTATUS nfs41_CompleteBufferingStateChangeRequest(
6832 #endif
6833     IN OUT PRX_CONTEXT RxContext,
6834     IN OUT PMRX_SRV_OPEN SrvOpen,
6835     IN PVOID pContext)
6836 {
6837     return STATUS_SUCCESS;
6838 }
6839 
6840 #ifdef __REACTOS__
6841 NTSTATUS NTAPI nfs41_FsdDispatch (
6842 #else
6843 NTSTATUS nfs41_FsdDispatch (
6844 #endif
6845     IN PDEVICE_OBJECT dev,
6846     IN PIRP Irp)
6847 {
6848 #ifdef DEBUG_FSDDISPATCH
6849     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
6850 #endif
6851     NTSTATUS status;
6852 
6853 #ifdef DEBUG_FSDDISPATCH
6854     DbgEn();
6855     DbgP("CURRENT IRP = %d.%d\n", IrpSp->MajorFunction, IrpSp->MinorFunction);
6856     if(IrpSp->FileObject)
6857         DbgP("FileOject %p Filename %wZ\n", IrpSp->FileObject,
6858                 &IrpSp->FileObject->FileName);
6859 #endif
6860 
6861     if (dev != (PDEVICE_OBJECT)nfs41_dev) {
6862         print_error("*** not ours ***\n");
6863         Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
6864         Irp->IoStatus.Information = 0;
6865         IoCompleteRequest(Irp, IO_NO_INCREMENT );
6866         status = STATUS_INVALID_DEVICE_REQUEST;
6867         goto out;
6868     }
6869 
6870     status = RxFsdDispatch((PRDBSS_DEVICE_OBJECT)dev,Irp);
6871     /* AGLO: 08/05/2009 - looks like RxFsdDispatch frees IrpSp */
6872 
6873 out:
6874 #ifdef DEBUG_FSDDISPATCH
6875     DbgP("IoStatus status = 0x%x info = 0x%x\n", Irp->IoStatus.Status,
6876          Irp->IoStatus.Information);
6877     DbgEx();
6878 #endif
6879     return status;
6880 }
6881 
6882 #ifdef __REACTOS__
6883 NTSTATUS NTAPI nfs41_Unimplemented(
6884 #else
6885 NTSTATUS nfs41_Unimplemented(
6886 #endif
6887     PRX_CONTEXT RxContext)
6888 {
6889     return STATUS_NOT_IMPLEMENTED;
6890 }
6891 
6892 #ifdef __REACTOS__
6893 NTSTATUS NTAPI nfs41_AreFilesAliased(
6894 #else
6895 NTSTATUS nfs41_AreFilesAliased(
6896 #endif
6897     PFCB a,
6898     PFCB b)
6899 {
6900     return STATUS_NOT_IMPLEMENTED;
6901 }
6902 
6903 NTSTATUS nfs41_init_ops()
6904 {
6905     DbgEn();
6906 
6907     ZeroAndInitializeNodeType(&nfs41_ops, RDBSS_NTC_MINIRDR_DISPATCH,
6908         sizeof(MINIRDR_DISPATCH));
6909 
6910     nfs41_ops.MRxFlags = (RDBSS_MANAGE_NET_ROOT_EXTENSION |
6911                             RDBSS_MANAGE_V_NET_ROOT_EXTENSION |
6912                             RDBSS_MANAGE_FCB_EXTENSION |
6913                             RDBSS_MANAGE_FOBX_EXTENSION);
6914 
6915     nfs41_ops.MRxSrvCallSize  = 0; // srvcall extension is not handled in rdbss
6916     nfs41_ops.MRxNetRootSize  = sizeof(NFS41_NETROOT_EXTENSION);
6917     nfs41_ops.MRxVNetRootSize = sizeof(NFS41_V_NET_ROOT_EXTENSION);
6918     nfs41_ops.MRxFcbSize      = sizeof(NFS41_FCB);
6919     nfs41_ops.MRxFobxSize     = sizeof(NFS41_FOBX);
6920 
6921     // Mini redirector cancel routine ..
6922 
6923     nfs41_ops.MRxCancel = NULL;
6924 
6925     //
6926     // Mini redirector Start/Stop. Each mini-rdr can be started or stopped
6927     // while the others continue to operate.
6928     //
6929 
6930     nfs41_ops.MRxStart                = nfs41_Start;
6931     nfs41_ops.MRxStop                 = nfs41_Stop;
6932     nfs41_ops.MRxDevFcbXXXControlFile = nfs41_DevFcbXXXControlFile;
6933 
6934     //
6935     // Mini redirector name resolution.
6936     //
6937 
6938     nfs41_ops.MRxCreateSrvCall       = nfs41_CreateSrvCall;
6939     nfs41_ops.MRxSrvCallWinnerNotify = nfs41_SrvCallWinnerNotify;
6940     nfs41_ops.MRxCreateVNetRoot      = nfs41_CreateVNetRoot;
6941     nfs41_ops.MRxExtractNetRootName  = nfs41_ExtractNetRootName;
6942     nfs41_ops.MRxFinalizeSrvCall     = nfs41_FinalizeSrvCall;
6943     nfs41_ops.MRxFinalizeNetRoot     = nfs41_FinalizeNetRoot;
6944     nfs41_ops.MRxFinalizeVNetRoot    = nfs41_FinalizeVNetRoot;
6945 
6946     //
6947     // File System Object Creation/Deletion.
6948     //
6949 
6950     nfs41_ops.MRxCreate            = nfs41_Create;
6951     nfs41_ops.MRxCollapseOpen      = nfs41_CollapseOpen;
6952     nfs41_ops.MRxShouldTryToCollapseThisOpen = nfs41_ShouldTryToCollapseThisOpen;
6953     nfs41_ops.MRxExtendForCache    = nfs41_ExtendForCache;
6954     nfs41_ops.MRxExtendForNonCache = nfs41_ExtendForCache;
6955     nfs41_ops.MRxCloseSrvOpen      = nfs41_CloseSrvOpen;
6956     nfs41_ops.MRxFlush             = nfs41_Flush;
6957     nfs41_ops.MRxDeallocateForFcb  = nfs41_DeallocateForFcb;
6958     nfs41_ops.MRxDeallocateForFobx = nfs41_DeallocateForFobx;
6959     nfs41_ops.MRxIsLockRealizable    = nfs41_IsLockRealizable;
6960 
6961     //
6962     // File System Objects query/Set
6963     //
6964 
6965     nfs41_ops.MRxQueryDirectory       = nfs41_QueryDirectory;
6966     nfs41_ops.MRxQueryVolumeInfo      = nfs41_QueryVolumeInformation;
6967     nfs41_ops.MRxQueryEaInfo          = nfs41_QueryEaInformation;
6968     nfs41_ops.MRxSetEaInfo            = nfs41_SetEaInformation;
6969     nfs41_ops.MRxQuerySdInfo          = nfs41_QuerySecurityInformation;
6970     nfs41_ops.MRxSetSdInfo            = nfs41_SetSecurityInformation;
6971     nfs41_ops.MRxQueryFileInfo        = nfs41_QueryFileInformation;
6972     nfs41_ops.MRxSetFileInfo          = nfs41_SetFileInformation;
6973 
6974     //
6975     // Buffering state change
6976     //
6977 
6978     nfs41_ops.MRxComputeNewBufferingState = nfs41_ComputeNewBufferingState;
6979 
6980     //
6981     // File System Object I/O
6982     //
6983 
6984     nfs41_ops.MRxLowIOSubmit[LOWIO_OP_READ]            = nfs41_Read;
6985     nfs41_ops.MRxLowIOSubmit[LOWIO_OP_WRITE]           = nfs41_Write;
6986     nfs41_ops.MRxLowIOSubmit[LOWIO_OP_SHAREDLOCK]      = nfs41_Lock;
6987     nfs41_ops.MRxLowIOSubmit[LOWIO_OP_EXCLUSIVELOCK]   = nfs41_Lock;
6988     nfs41_ops.MRxLowIOSubmit[LOWIO_OP_UNLOCK]          = nfs41_Unlock;
6989     nfs41_ops.MRxLowIOSubmit[LOWIO_OP_UNLOCK_MULTIPLE] = nfs41_Unlock;
6990     nfs41_ops.MRxLowIOSubmit[LOWIO_OP_FSCTL]           = nfs41_FsCtl;
6991 
6992     //
6993     // Miscellanous
6994     //
6995 
6996     nfs41_ops.MRxCompleteBufferingStateChangeRequest =
6997         nfs41_CompleteBufferingStateChangeRequest;
6998     nfs41_ops.MRxIsValidDirectory     = nfs41_IsValidDirectory;
6999 
7000     nfs41_ops.MRxTruncate = nfs41_Unimplemented;
7001     nfs41_ops.MRxZeroExtend = nfs41_Unimplemented;
7002     nfs41_ops.MRxAreFilesAliased = nfs41_AreFilesAliased;
7003     nfs41_ops.MRxQueryQuotaInfo = nfs41_Unimplemented;
7004     nfs41_ops.MRxSetQuotaInfo = nfs41_Unimplemented;
7005     nfs41_ops.MRxSetVolumeInfo = nfs41_Unimplemented;
7006 
7007     DbgR();
7008     return(STATUS_SUCCESS);
7009 }
7010 
7011 KSTART_ROUTINE fcbopen_main;
7012 #ifdef __REACTOS__
7013 VOID NTAPI fcbopen_main(PVOID ctx)
7014 #else
7015 VOID fcbopen_main(PVOID ctx)
7016 #endif
7017 {
7018     NTSTATUS status;
7019     LARGE_INTEGER timeout;
7020 
7021     DbgEn();
7022     timeout.QuadPart = RELATIVE(SECONDS(30));
7023     while(1) {
7024         PLIST_ENTRY pEntry;
7025         nfs41_fcb_list_entry *cur;
7026         status = KeDelayExecutionThread(KernelMode, TRUE, &timeout);
7027         ExAcquireFastMutex(&fcblistLock);
7028         pEntry = openlist.head.Flink;
7029         while (!IsListEmpty(&openlist.head)) {
7030             PNFS41_NETROOT_EXTENSION pNetRootContext;
7031             nfs41_updowncall_entry *entry;
7032             FILE_BASIC_INFORMATION binfo;
7033             PNFS41_FCB nfs41_fcb;
7034             cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
7035                     nfs41_fcb_list_entry, next);
7036 
7037 #ifdef DEBUG_TIME_BASED_COHERENCY
7038             DbgP("fcbopen_main: Checking attributes for fcb=%p "
7039                 "change_time=%llu skipping=%d\n", cur->fcb,
7040                 cur->ChangeTime, cur->skip);
7041 #endif
7042             if (cur->skip) goto out;
7043             pNetRootContext =
7044                 NFS41GetNetRootExtension(cur->fcb->pNetRoot);
7045             /* place an upcall for this srv_open */
7046             status = nfs41_UpcallCreate(NFS41_FILE_QUERY,
7047                 &cur->nfs41_fobx->sec_ctx, cur->session,
7048                 cur->nfs41_fobx->nfs41_open_state,
7049                 pNetRootContext->nfs41d_version, NULL, &entry);
7050             if (status) goto out;
7051 
7052             entry->u.QueryFile.InfoClass = FileBasicInformation;
7053             entry->buf = &binfo;
7054             entry->buf_len = sizeof(binfo);
7055 
7056             status = nfs41_UpcallWaitForReply(entry, UPCALL_TIMEOUT_DEFAULT);
7057             if (status) goto out;
7058 
7059             if (cur->ChangeTime != entry->ChangeTime) {
7060                 ULONG flag = DISABLE_CACHING;
7061                 PMRX_SRV_OPEN srv_open;
7062                 PLIST_ENTRY psrvEntry;
7063 #ifdef DEBUG_TIME_BASED_COHERENCY
7064                 DbgP("fcbopen_main: old ctime=%llu new_ctime=%llu\n",
7065                     cur->ChangeTime, entry->ChangeTime);
7066 #endif
7067                 cur->ChangeTime = entry->ChangeTime;
7068                 cur->skip = TRUE;
7069                 psrvEntry = &cur->fcb->SrvOpenList;
7070                 psrvEntry = psrvEntry->Flink;
7071                 while (!IsListEmpty(&cur->fcb->SrvOpenList)) {
7072                     srv_open = (PMRX_SRV_OPEN)CONTAINING_RECORD(psrvEntry,
7073                             MRX_SRV_OPEN, SrvOpenQLinks);
7074                     if (srv_open->DesiredAccess &
7075                             (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)) {
7076 #ifdef DEBUG_TIME_BASED_COHERENCY
7077                         DbgP("fcbopen_main: ************ Invalidate the cache %wZ"
7078                              "************\n", srv_open->pAlreadyPrefixedName);
7079 #endif
7080                         RxIndicateChangeOfBufferingStateForSrvOpen(
7081                             cur->fcb->pNetRoot->pSrvCall, srv_open,
7082                             srv_open->Key, ULongToPtr(flag));
7083                     }
7084                     if (psrvEntry->Flink == &cur->fcb->SrvOpenList) {
7085 #ifdef DEBUG_TIME_BASED_COHERENCY
7086                         DbgP("fcbopen_main: reached end of srvopen for fcb %p\n",
7087                             cur->fcb);
7088 #endif
7089                         break;
7090                     }
7091                     psrvEntry = psrvEntry->Flink;
7092                 };
7093             }
7094             nfs41_fcb = (PNFS41_FCB)cur->fcb->Context;
7095             nfs41_fcb->changeattr = entry->ChangeTime;
7096             RxFreePool(entry);
7097 out:
7098             if (pEntry->Flink == &openlist.head) {
7099 #ifdef DEBUG_TIME_BASED_COHERENCY
7100                 DbgP("fcbopen_main: reached end of the fcb list\n");
7101 #endif
7102                 break;
7103             }
7104             pEntry = pEntry->Flink;
7105         }
7106         ExReleaseFastMutex(&fcblistLock);
7107     }
7108     DbgEx();
7109 }
7110 
7111 #ifdef __REACTOS__
7112 NTSTATUS NTAPI DriverEntry(
7113 #else
7114 NTSTATUS DriverEntry(
7115 #endif
7116     IN PDRIVER_OBJECT drv,
7117     IN PUNICODE_STRING path)
7118 {
7119     NTSTATUS status;
7120     ULONG flags = 0, i;
7121     UNICODE_STRING dev_name, user_dev_name;
7122     PNFS41_DEVICE_EXTENSION dev_exts;
7123     TIME_FIELDS jan_1_1970 = {1970, 1, 1, 0, 0, 0, 0, 0};
7124     ACCESS_MASK mask = 0;
7125     OBJECT_ATTRIBUTES oattrs;
7126 
7127     DbgEn();
7128 
7129     status = RxDriverEntry(drv, path);
7130     if (status != STATUS_SUCCESS) {
7131         print_error("RxDriverEntry failed: %08lx\n", status);
7132         goto out;
7133     }
7134 
7135     RtlInitUnicodeString(&dev_name, NFS41_DEVICE_NAME);
7136     SetFlag(flags, RX_REGISTERMINI_FLAG_DONT_PROVIDE_MAILSLOTS);
7137 
7138     status = nfs41_init_ops();
7139     if (status != STATUS_SUCCESS) {
7140         print_error("nfs41_init_ops failed to initialize dispatch table\n");
7141         goto out;
7142     }
7143 
7144     DbgP("calling RxRegisterMinirdr\n");
7145     status = RxRegisterMinirdr(&nfs41_dev, drv, &nfs41_ops, flags, &dev_name,
7146                 sizeof(NFS41_DEVICE_EXTENSION),
7147                 FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_REMOTE_DEVICE);
7148     if (status != STATUS_SUCCESS) {
7149         print_error("RxRegisterMinirdr failed: %08lx\n", status);
7150         goto out;
7151     }
7152 #ifndef __REACTOS__
7153     nfs41_dev->Flags |= DO_BUFFERED_IO;
7154 #endif
7155 
7156     dev_exts = (PNFS41_DEVICE_EXTENSION)
7157         ((PBYTE)(nfs41_dev) + sizeof(RDBSS_DEVICE_OBJECT));
7158 
7159     RxDefineNode(dev_exts, NFS41_DEVICE_EXTENSION);
7160     dev_exts->DeviceObject = nfs41_dev;
7161     nfs41_create_volume_info((PFILE_FS_VOLUME_INFORMATION)dev_exts->VolAttrs,
7162         &dev_exts->VolAttrsLen);
7163 
7164     RtlInitUnicodeString(&user_dev_name, NFS41_SHADOW_DEVICE_NAME);
7165     DbgP("calling IoCreateSymbolicLink %wZ %wZ\n", &user_dev_name, &dev_name);
7166     status = IoCreateSymbolicLink(&user_dev_name, &dev_name);
7167     if (status != STATUS_SUCCESS) {
7168         print_error("Device name IoCreateSymbolicLink failed: %08lx\n", status);
7169         goto out_unregister;
7170     }
7171 
7172     KeInitializeEvent(&upcallEvent, SynchronizationEvent, FALSE );
7173     ExInitializeFastMutex(&upcallLock);
7174     ExInitializeFastMutex(&downcallLock);
7175     ExInitializeFastMutex(&xidLock);
7176     ExInitializeFastMutex(&openOwnerLock);
7177     ExInitializeFastMutex(&fcblistLock);
7178     InitializeListHead(&upcall.head);
7179     InitializeListHead(&downcall.head);
7180     InitializeListHead(&openlist.head);
7181     InitializeObjectAttributes(&oattrs, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
7182     status = PsCreateSystemThread(&dev_exts->openlistHandle, mask,
7183         &oattrs, NULL, NULL, &fcbopen_main, NULL);
7184     if (status != STATUS_SUCCESS)
7185         goto out_unregister;
7186 
7187     drv->DriverUnload = nfs41_driver_unload;
7188 
7189     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
7190         drv->MajorFunction[i] = (PDRIVER_DISPATCH)nfs41_FsdDispatch;
7191 
7192     RtlTimeFieldsToTime(&jan_1_1970, &unix_time_diff);
7193 
7194 out_unregister:
7195     if (status != STATUS_SUCCESS)
7196         RxUnregisterMinirdr(nfs41_dev);
7197 out:
7198     DbgEx();
7199     return status;
7200 }
7201 
7202 #ifdef __REACTOS__
7203 VOID NTAPI nfs41_driver_unload(IN PDRIVER_OBJECT drv)
7204 #else
7205 VOID nfs41_driver_unload(IN PDRIVER_OBJECT drv)
7206 #endif
7207 {
7208     PRX_CONTEXT RxContext;
7209     NTSTATUS    status;
7210     UNICODE_STRING dev_name, pipe_name;
7211 
7212     DbgEn();
7213 
7214     RxContext = RxCreateRxContext(NULL, nfs41_dev, RX_CONTEXT_FLAG_IN_FSP);
7215     if (RxContext == NULL) {
7216         status = STATUS_INSUFFICIENT_RESOURCES;
7217         goto unload;
7218     }
7219     status = RxStopMinirdr(RxContext, &RxContext->PostRequest);
7220     RxDereferenceAndDeleteRxContext(RxContext);
7221 
7222 unload:
7223     RtlInitUnicodeString(&dev_name, NFS41_SHADOW_DEVICE_NAME);
7224     status = IoDeleteSymbolicLink(&dev_name);
7225     if (status != STATUS_SUCCESS) {
7226         print_error("couldn't delete device symbolic link\n");
7227     }
7228     RtlInitUnicodeString(&pipe_name, NFS41_SHADOW_PIPE_NAME);
7229     status = IoDeleteSymbolicLink(&pipe_name);
7230     if (status != STATUS_SUCCESS) {
7231         print_error("couldn't delete pipe symbolic link\n");
7232     }
7233     RxUnload(drv);
7234 
7235     DbgP("driver unloaded %p\n", drv);
7236     DbgR();
7237 }
7238