xref: /reactos/drivers/filesystems/btrfs/create.c (revision 139a3d66)
1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #ifndef __REACTOS__
19 #include <sys/stat.h>
20 #endif /* __REACTOS__ */
21 #include "btrfs_drv.h"
22 #include "crc32c.h"
23 #include <ntddstor.h>
24 
25 extern PDEVICE_OBJECT master_devobj;
26 extern tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp;
27 extern tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter;
28 extern tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
29 
30 static const WCHAR datastring[] = L"::$DATA";
31 
32 static const char root_dir[] = "$Root";
33 static const WCHAR root_dir_utf16[] = L"$Root";
34 
35 // Windows 10
36 #define ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED   0x0002
37 #define ATOMIC_CREATE_ECP_IN_FLAG_OP_FLAGS_SPECIFIED        0x0080
38 #define ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT               0x0100
39 
40 #define ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET        0x0002
41 #define ATOMIC_CREATE_ECP_OUT_FLAG_OP_FLAGS_HONORED         0x0080
42 
43 #define ATOMIC_CREATE_ECP_IN_OP_FLAG_CASE_SENSITIVE_FLAGS_SPECIFIED       1
44 #define ATOMIC_CREATE_ECP_OUT_OP_FLAG_CASE_SENSITIVE_FLAGS_SET            1
45 
46 typedef struct _FILE_TIMESTAMPS {
47     LARGE_INTEGER CreationTime;
48     LARGE_INTEGER LastAccessTime;
49     LARGE_INTEGER LastWriteTime;
50     LARGE_INTEGER ChangeTime;
51 } FILE_TIMESTAMPS, *PFILE_TIMESTAMPS;
52 
53 typedef struct _ATOMIC_CREATE_ECP_CONTEXT {
54     USHORT Size;
55     USHORT InFlags;
56     USHORT OutFlags;
57     USHORT ReparseBufferLength;
58     PREPARSE_DATA_BUFFER ReparseBuffer;
59     LONGLONG FileSize;
60     LONGLONG ValidDataLength;
61     PFILE_TIMESTAMPS FileTimestamps;
62     ULONG FileAttributes;
63     ULONG UsnSourceInfo;
64     USN Usn;
65     ULONG SuppressFileAttributeInheritanceMask;
66     ULONG InOpFlags;
67     ULONG OutOpFlags;
68     ULONG InGenFlags;
69     ULONG OutGenFlags;
70     ULONG CaseSensitiveFlagsMask;
71     ULONG InCaseSensitiveFlags;
72     ULONG OutCaseSensitiveFlags;
73 } ATOMIC_CREATE_ECP_CONTEXT, *PATOMIC_CREATE_ECP_CONTEXT;
74 
75 static const GUID GUID_ECP_ATOMIC_CREATE = { 0x4720bd83, 0x52ac, 0x4104, { 0xa1, 0x30, 0xd1, 0xec, 0x6a, 0x8c, 0xc8, 0xe5 } };
76 static const GUID GUID_ECP_QUERY_ON_CREATE = { 0x1aca62e9, 0xabb4, 0x4ff2, { 0xbb, 0x5c, 0x1c, 0x79, 0x02, 0x5e, 0x41, 0x7f } };
77 static const GUID GUID_ECP_CREATE_REDIRECTION = { 0x188d6bd6, 0xa126, 0x4fa8, { 0xbd, 0xf2, 0x1c, 0xcd, 0xf8, 0x96, 0xf3, 0xe0 } };
78 
79 fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) {
80     fcb* fcb;
81 
82     if (pool_type == NonPagedPool) {
83         fcb = ExAllocatePoolWithTag(pool_type, sizeof(struct _fcb), ALLOC_TAG);
84         if (!fcb) {
85             ERR("out of memory\n");
86             return NULL;
87         }
88     } else {
89         fcb = ExAllocateFromPagedLookasideList(&Vcb->fcb_lookaside);
90         if (!fcb) {
91             ERR("out of memory\n");
92             return NULL;
93         }
94     }
95 
96 #ifdef DEBUG_FCB_REFCOUNTS
97     WARN("allocating fcb %p\n", fcb);
98 #endif
99     RtlZeroMemory(fcb, sizeof(struct _fcb));
100     fcb->pool_type = pool_type;
101 
102     fcb->Header.NodeTypeCode = BTRFS_NODE_TYPE_FCB;
103     fcb->Header.NodeByteSize = sizeof(struct _fcb);
104 
105     fcb->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fcb_np_lookaside);
106     if (!fcb->nonpaged) {
107         ERR("out of memory\n");
108 
109         if (pool_type == NonPagedPool)
110             ExFreePool(fcb);
111         else
112             ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
113 
114         return NULL;
115     }
116     RtlZeroMemory(fcb->nonpaged, sizeof(struct _fcb_nonpaged));
117 
118     ExInitializeResourceLite(&fcb->nonpaged->paging_resource);
119     fcb->Header.PagingIoResource = &fcb->nonpaged->paging_resource;
120 
121     ExInitializeFastMutex(&fcb->nonpaged->HeaderMutex);
122     FsRtlSetupAdvancedHeader(&fcb->Header, &fcb->nonpaged->HeaderMutex);
123 
124     fcb->refcount = 1;
125 #ifdef DEBUG_FCB_REFCOUNTS
126     WARN("fcb %p: refcount now %i\n", fcb, fcb->refcount);
127 #endif
128 
129     ExInitializeResourceLite(&fcb->nonpaged->resource);
130     fcb->Header.Resource = &fcb->nonpaged->resource;
131 
132     ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
133 
134     FsRtlInitializeFileLock(&fcb->lock, NULL, NULL);
135     FsRtlInitializeOplock(fcb_oplock(fcb));
136 
137     InitializeListHead(&fcb->extents);
138     InitializeListHead(&fcb->hardlinks);
139     InitializeListHead(&fcb->xattrs);
140 
141     InitializeListHead(&fcb->dir_children_index);
142     InitializeListHead(&fcb->dir_children_hash);
143     InitializeListHead(&fcb->dir_children_hash_uc);
144 
145     return fcb;
146 }
147 
148 file_ref* create_fileref(device_extension* Vcb) {
149     file_ref* fr;
150 
151     fr = ExAllocateFromPagedLookasideList(&Vcb->fileref_lookaside);
152     if (!fr) {
153         ERR("out of memory\n");
154         return NULL;
155     }
156 
157     RtlZeroMemory(fr, sizeof(file_ref));
158 
159     fr->nonpaged = ExAllocateFromNPagedLookasideList(&Vcb->fileref_np_lookaside);
160     if (!fr->nonpaged) {
161         ERR("out of memory\n");
162         ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
163         return NULL;
164     }
165 
166     fr->refcount = 1;
167 
168 #ifdef DEBUG_FCB_REFCOUNTS
169     WARN("fileref %p: refcount now 1\n", fr);
170 #endif
171 
172     InitializeListHead(&fr->children);
173 
174     ExInitializeResourceLite(&fr->nonpaged->fileref_lock);
175 
176     return fr;
177 }
178 
179 NTSTATUS find_file_in_dir(PUNICODE_STRING filename, fcb* fcb, root** subvol, uint64_t* inode, dir_child** pdc, bool case_sensitive) {
180     NTSTATUS Status;
181     UNICODE_STRING fnus;
182     uint32_t hash;
183     LIST_ENTRY* le;
184     uint8_t c;
185     bool locked = false;
186 
187     if (!case_sensitive) {
188         Status = RtlUpcaseUnicodeString(&fnus, filename, true);
189 
190         if (!NT_SUCCESS(Status)) {
191             ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
192             return Status;
193         }
194     } else
195         fnus = *filename;
196 
197     hash = calc_crc32c(0xffffffff, (uint8_t*)fnus.Buffer, fnus.Length);
198 
199     c = hash >> 24;
200 
201     if (!ExIsResourceAcquiredSharedLite(&fcb->nonpaged->dir_children_lock)) {
202         ExAcquireResourceSharedLite(&fcb->nonpaged->dir_children_lock, true);
203         locked = true;
204     }
205 
206     if (case_sensitive) {
207         if (!fcb->hash_ptrs[c]) {
208             Status = STATUS_OBJECT_NAME_NOT_FOUND;
209             goto end;
210         }
211 
212         le = fcb->hash_ptrs[c];
213         while (le != &fcb->dir_children_hash) {
214             dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
215 
216             if (dc->hash == hash) {
217                 if (dc->name.Length == fnus.Length && RtlCompareMemory(dc->name.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
218                     if (dc->key.obj_type == TYPE_ROOT_ITEM) {
219                         LIST_ENTRY* le2;
220 
221                         *subvol = NULL;
222 
223                         le2 = fcb->Vcb->roots.Flink;
224                         while (le2 != &fcb->Vcb->roots) {
225                             root* r2 = CONTAINING_RECORD(le2, root, list_entry);
226 
227                             if (r2->id == dc->key.obj_id) {
228                                 *subvol = r2;
229                                 break;
230                             }
231 
232                             le2 = le2->Flink;
233                         }
234 
235                         *inode = SUBVOL_ROOT_INODE;
236                     } else {
237                         *subvol = fcb->subvol;
238                         *inode = dc->key.obj_id;
239                     }
240 
241                     *pdc = dc;
242 
243                     Status = STATUS_SUCCESS;
244                     goto end;
245                 }
246             } else if (dc->hash > hash) {
247                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
248                 goto end;
249             }
250 
251             le = le->Flink;
252         }
253     } else {
254         if (!fcb->hash_ptrs_uc[c]) {
255             Status = STATUS_OBJECT_NAME_NOT_FOUND;
256             goto end;
257         }
258 
259         le = fcb->hash_ptrs_uc[c];
260         while (le != &fcb->dir_children_hash_uc) {
261             dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
262 
263             if (dc->hash_uc == hash) {
264                 if (dc->name_uc.Length == fnus.Length && RtlCompareMemory(dc->name_uc.Buffer, fnus.Buffer, fnus.Length) == fnus.Length) {
265                     if (dc->key.obj_type == TYPE_ROOT_ITEM) {
266                         LIST_ENTRY* le2;
267 
268                         *subvol = NULL;
269 
270                         le2 = fcb->Vcb->roots.Flink;
271                         while (le2 != &fcb->Vcb->roots) {
272                             root* r2 = CONTAINING_RECORD(le2, root, list_entry);
273 
274                             if (r2->id == dc->key.obj_id) {
275                                 *subvol = r2;
276                                 break;
277                             }
278 
279                             le2 = le2->Flink;
280                         }
281 
282                         *inode = SUBVOL_ROOT_INODE;
283                     } else {
284                         *subvol = fcb->subvol;
285                         *inode = dc->key.obj_id;
286                     }
287 
288                     *pdc = dc;
289 
290                     Status = STATUS_SUCCESS;
291                     goto end;
292                 }
293             } else if (dc->hash_uc > hash) {
294                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
295                 goto end;
296             }
297 
298             le = le->Flink;
299         }
300     }
301 
302     Status = STATUS_OBJECT_NAME_NOT_FOUND;
303 
304 end:
305     if (locked)
306         ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
307 
308     if (!case_sensitive)
309         ExFreePool(fnus.Buffer);
310 
311     return Status;
312 }
313 
314 static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING path, LIST_ENTRY* parts, bool* stream) {
315     ULONG len, i;
316     bool has_stream;
317     WCHAR* buf;
318     name_bit* nb;
319     NTSTATUS Status;
320 
321     len = path->Length / sizeof(WCHAR);
322     if (len > 0 && (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\'))
323         len--;
324 
325     if (len == 0 || (path->Buffer[len - 1] == '/' || path->Buffer[len - 1] == '\\')) {
326         WARN("zero-length filename part\n");
327         return STATUS_OBJECT_NAME_INVALID;
328     }
329 
330     has_stream = false;
331     for (i = 0; i < len; i++) {
332         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
333             has_stream = false;
334         } else if (path->Buffer[i] == ':') {
335             has_stream = true;
336         }
337     }
338 
339     buf = path->Buffer;
340 
341     for (i = 0; i < len; i++) {
342         if (path->Buffer[i] == '/' || path->Buffer[i] == '\\') {
343             if (buf[0] == '/' || buf[0] == '\\') {
344                 WARN("zero-length filename part\n");
345                 Status = STATUS_OBJECT_NAME_INVALID;
346                 goto cleanup;
347             }
348 
349             nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
350             if (!nb) {
351                 ERR("out of memory\n");
352                 Status = STATUS_INSUFFICIENT_RESOURCES;
353                 goto cleanup;
354             }
355 
356             nb->us.Buffer = buf;
357             nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
358             InsertTailList(parts, &nb->list_entry);
359 
360             buf = &path->Buffer[i+1];
361         }
362     }
363 
364     nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
365     if (!nb) {
366         ERR("out of memory\n");
367         Status = STATUS_INSUFFICIENT_RESOURCES;
368         goto cleanup;
369     }
370 
371     nb->us.Buffer = buf;
372     nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
373     InsertTailList(parts, &nb->list_entry);
374 
375     if (has_stream) {
376         static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
377         UNICODE_STRING dsus;
378 
379         dsus.Buffer = (WCHAR*)datasuf;
380         dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
381 
382         for (i = 0; i < nb->us.Length / sizeof(WCHAR); i++) {
383             if (nb->us.Buffer[i] == ':') {
384                 name_bit* nb2;
385 
386                 if (nb->us.Buffer[i+1] == 0) {
387                     WARN("zero-length stream name\n");
388                     Status = STATUS_OBJECT_NAME_INVALID;
389                     goto cleanup;
390                 }
391 
392                 nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
393                 if (!nb2) {
394                     ERR("out of memory\n");
395                     Status = STATUS_INSUFFICIENT_RESOURCES;
396                     goto cleanup;
397                 }
398 
399                 nb2->us.Buffer = &nb->us.Buffer[i+1];
400                 nb2->us.Length = nb2->us.MaximumLength = (uint16_t)(nb->us.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR));
401                 InsertTailList(parts, &nb2->list_entry);
402 
403                 nb->us.Length = (uint16_t)i * sizeof(WCHAR);
404                 nb->us.MaximumLength = nb->us.Length;
405 
406                 nb = nb2;
407 
408                 break;
409             }
410         }
411 
412         // FIXME - should comparison be case-insensitive?
413         // remove :$DATA suffix
414         if (nb->us.Length >= dsus.Length && RtlCompareMemory(&nb->us.Buffer[(nb->us.Length - dsus.Length)/sizeof(WCHAR)], dsus.Buffer, dsus.Length) == dsus.Length)
415             nb->us.Length -= dsus.Length;
416 
417         if (nb->us.Length == 0) {
418             RemoveTailList(parts);
419             ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
420 
421             has_stream = false;
422         }
423     }
424 
425     // if path is just stream name, remove first empty item
426     if (has_stream && path->Length >= sizeof(WCHAR) && path->Buffer[0] == ':') {
427         name_bit *nb1 = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
428 
429         ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb1);
430     }
431 
432     *stream = has_stream;
433 
434     return STATUS_SUCCESS;
435 
436 cleanup:
437     while (!IsListEmpty(parts)) {
438         nb = CONTAINING_RECORD(RemoveHeadList(parts), name_bit, list_entry);
439 
440         ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
441     }
442 
443     return Status;
444 }
445 
446 NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, void* csum, uint64_t start, uint64_t length, PIRP Irp) {
447     NTSTATUS Status;
448     KEY searchkey;
449     traverse_ptr tp, next_tp;
450     uint64_t i, j;
451     bool b;
452     void* ptr = csum;
453 
454     searchkey.obj_id = EXTENT_CSUM_ID;
455     searchkey.obj_type = TYPE_EXTENT_CSUM;
456     searchkey.offset = start;
457 
458     Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, Irp);
459     if (!NT_SUCCESS(Status)) {
460         ERR("error - find_item returned %08lx\n", Status);
461         return Status;
462     }
463 
464     i = 0;
465     do {
466         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
467             ULONG readlen;
468 
469             if (start < tp.item->key.offset)
470                 j = 0;
471             else
472                 j = ((start - tp.item->key.offset) / Vcb->superblock.sector_size) + i;
473 
474             if (j * Vcb->csum_size > tp.item->size || tp.item->key.offset > start + (i * Vcb->superblock.sector_size)) {
475                 ERR("checksum not found for %I64x\n", start + (i * Vcb->superblock.sector_size));
476                 return STATUS_INTERNAL_ERROR;
477             }
478 
479             readlen = (ULONG)min((tp.item->size / Vcb->csum_size) - j, length - i);
480             RtlCopyMemory(ptr, tp.item->data + (j * Vcb->csum_size), readlen * Vcb->csum_size);
481 
482             ptr = (uint8_t*)ptr + (readlen * Vcb->csum_size);
483             i += readlen;
484 
485             if (i == length)
486                 break;
487         }
488 
489         b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
490 
491         if (b)
492             tp = next_tp;
493     } while (b);
494 
495     if (i < length) {
496         ERR("could not read checksums: offset %I64x, length %I64x sectors\n", start, length);
497         return STATUS_INTERNAL_ERROR;
498     }
499 
500     return STATUS_SUCCESS;
501 }
502 
503 NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, bool ignore_size, PIRP Irp) {
504     KEY searchkey;
505     traverse_ptr tp, next_tp;
506     NTSTATUS Status;
507     ULONG num_children = 0;
508     uint64_t max_index = 2;
509 
510     fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
511     if (!fcb->hash_ptrs) {
512         ERR("out of memory\n");
513         return STATUS_INSUFFICIENT_RESOURCES;
514     }
515 
516     RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
517 
518     fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
519     if (!fcb->hash_ptrs_uc) {
520         ERR("out of memory\n");
521         return STATUS_INSUFFICIENT_RESOURCES;
522     }
523 
524     RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
525 
526     if (!ignore_size && fcb->inode_item.st_size == 0)
527         return STATUS_SUCCESS;
528 
529     searchkey.obj_id = fcb->inode;
530     searchkey.obj_type = TYPE_DIR_INDEX;
531     searchkey.offset = 2;
532 
533     Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, false, Irp);
534     if (!NT_SUCCESS(Status)) {
535         ERR("find_item returned %08lx\n", Status);
536         return Status;
537     }
538 
539     if (keycmp(tp.item->key, searchkey) == -1) {
540         if (find_next_item(Vcb, &tp, &next_tp, false, Irp)) {
541             tp = next_tp;
542             TRACE("moving on to %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
543         }
544     }
545 
546     while (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
547         DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
548         dir_child* dc;
549         ULONG utf16len;
550 
551         if (tp.item->size < sizeof(DIR_ITEM)) {
552             WARN("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
553             goto cont;
554         }
555 
556         if (di->n == 0) {
557             WARN("(%I64x,%x,%I64x): DIR_ITEM name length is zero\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
558             goto cont;
559         }
560 
561         Status = utf8_to_utf16(NULL, 0, &utf16len, di->name, di->n);
562         if (!NT_SUCCESS(Status)) {
563             ERR("utf8_to_utf16 1 returned %08lx\n", Status);
564             goto cont;
565         }
566 
567         dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
568         if (!dc) {
569             ERR("out of memory\n");
570             return STATUS_INSUFFICIENT_RESOURCES;
571         }
572 
573         dc->key = di->key;
574         dc->index = tp.item->key.offset;
575         dc->type = di->type;
576         dc->fileref = NULL;
577         dc->root_dir = false;
578 
579         max_index = dc->index;
580 
581         dc->utf8.MaximumLength = dc->utf8.Length = di->n;
582         dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, di->n, ALLOC_TAG);
583         if (!dc->utf8.Buffer) {
584             ERR("out of memory\n");
585             ExFreePool(dc);
586             return STATUS_INSUFFICIENT_RESOURCES;
587         }
588 
589         RtlCopyMemory(dc->utf8.Buffer, di->name, di->n);
590 
591         dc->name.MaximumLength = dc->name.Length = (uint16_t)utf16len;
592         dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
593         if (!dc->name.Buffer) {
594             ERR("out of memory\n");
595             ExFreePool(dc->utf8.Buffer);
596             ExFreePool(dc);
597             return STATUS_INSUFFICIENT_RESOURCES;
598         }
599 
600         Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, di->name, di->n);
601         if (!NT_SUCCESS(Status)) {
602             ERR("utf8_to_utf16 2 returned %08lx\n", Status);
603             ExFreePool(dc->utf8.Buffer);
604             ExFreePool(dc->name.Buffer);
605             ExFreePool(dc);
606             goto cont;
607         }
608 
609         Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
610         if (!NT_SUCCESS(Status)) {
611             ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
612             ExFreePool(dc->utf8.Buffer);
613             ExFreePool(dc->name.Buffer);
614             ExFreePool(dc);
615             goto cont;
616         }
617 
618         dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
619         dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
620 
621         InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
622 
623         insert_dir_child_into_hash_lists(fcb, dc);
624 
625         num_children++;
626 
627 cont:
628         if (find_next_item(Vcb, &tp, &next_tp, false, Irp))
629             tp = next_tp;
630         else
631             break;
632     }
633 
634     if (!Vcb->options.no_root_dir && fcb->inode == SUBVOL_ROOT_INODE) {
635         root* top_subvol;
636 
637         if (Vcb->root_fileref && Vcb->root_fileref->fcb)
638             top_subvol = Vcb->root_fileref->fcb->subvol;
639         else
640             top_subvol = find_default_subvol(Vcb, NULL);
641 
642         if (fcb->subvol == top_subvol && top_subvol->id != BTRFS_ROOT_FSTREE) {
643             dir_child* dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
644             if (!dc) {
645                 ERR("out of memory\n");
646                 return STATUS_INSUFFICIENT_RESOURCES;
647             }
648 
649             dc->key.obj_id = BTRFS_ROOT_FSTREE;
650             dc->key.obj_type = TYPE_ROOT_ITEM;
651             dc->key.offset = 0;
652             dc->index = max_index + 1;
653             dc->type = BTRFS_TYPE_DIRECTORY;
654             dc->fileref = NULL;
655             dc->root_dir = true;
656 
657             dc->utf8.MaximumLength = dc->utf8.Length = sizeof(root_dir) - sizeof(char);
658             dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir) - sizeof(char), ALLOC_TAG);
659             if (!dc->utf8.Buffer) {
660                 ERR("out of memory\n");
661                 ExFreePool(dc);
662                 return STATUS_INSUFFICIENT_RESOURCES;
663             }
664 
665             RtlCopyMemory(dc->utf8.Buffer, root_dir, sizeof(root_dir) - sizeof(char));
666 
667             dc->name.MaximumLength = dc->name.Length = sizeof(root_dir_utf16) - sizeof(WCHAR);
668             dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(root_dir_utf16) - sizeof(WCHAR), ALLOC_TAG);
669             if (!dc->name.Buffer) {
670                 ERR("out of memory\n");
671                 ExFreePool(dc->utf8.Buffer);
672                 ExFreePool(dc);
673                 return STATUS_INSUFFICIENT_RESOURCES;
674             }
675 
676             RtlCopyMemory(dc->name.Buffer, root_dir_utf16, sizeof(root_dir_utf16) - sizeof(WCHAR));
677 
678             Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
679             if (!NT_SUCCESS(Status)) {
680                 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
681                 ExFreePool(dc->utf8.Buffer);
682                 ExFreePool(dc->name.Buffer);
683                 ExFreePool(dc);
684                 goto cont;
685             }
686 
687             dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
688             dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
689 
690             InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
691 
692             insert_dir_child_into_hash_lists(fcb, dc);
693         }
694     }
695 
696     return STATUS_SUCCESS;
697 }
698 
699 NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
700                   root* subvol, uint64_t inode, uint8_t type, PANSI_STRING utf8, bool always_add_hl, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp) {
701     KEY searchkey;
702     traverse_ptr tp, next_tp;
703     NTSTATUS Status;
704     fcb *fcb, *deleted_fcb = NULL;
705     bool atts_set = false, sd_set = false, no_data;
706     LIST_ENTRY* lastle = NULL;
707     EXTENT_DATA* ed = NULL;
708     uint64_t fcbs_version;
709     uint32_t hash;
710 
711     hash = calc_crc32c(0xffffffff, (uint8_t*)&inode, sizeof(uint64_t));
712 
713     acquire_fcb_lock_shared(Vcb);
714 
715     if (subvol->fcbs_ptrs[hash >> 24]) {
716         LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
717 
718         while (le != &subvol->fcbs) {
719             fcb = CONTAINING_RECORD(le, struct _fcb, list_entry);
720 
721             if (fcb->inode == inode) {
722                 if (!fcb->ads) {
723                     if (fcb->deleted)
724                         deleted_fcb = fcb;
725                     else {
726 #ifdef DEBUG_FCB_REFCOUNTS
727                         LONG rc = InterlockedIncrement(&fcb->refcount);
728 
729                         WARN("fcb %p: refcount now %i (subvol %I64x, inode %I64x)\n", fcb, rc, fcb->subvol->id, fcb->inode);
730 #else
731                         InterlockedIncrement(&fcb->refcount);
732 #endif
733 
734                         *pfcb = fcb;
735                         release_fcb_lock(Vcb);
736                         return STATUS_SUCCESS;
737                     }
738                 }
739             } else if (fcb->hash > hash) {
740                 if (deleted_fcb) {
741                     InterlockedIncrement(&deleted_fcb->refcount);
742                     *pfcb = deleted_fcb;
743                     release_fcb_lock(Vcb);
744                     return STATUS_SUCCESS;
745                 }
746 
747                 lastle = le->Blink;
748                 fcbs_version = subvol->fcbs_version;
749 
750                 break;
751             }
752 
753             le = le->Flink;
754         }
755     }
756 
757     release_fcb_lock(Vcb);
758 
759     if (deleted_fcb) {
760         InterlockedIncrement(&deleted_fcb->refcount);
761         *pfcb = deleted_fcb;
762         return STATUS_SUCCESS;
763     }
764 
765     fcb = create_fcb(Vcb, pooltype);
766     if (!fcb) {
767         ERR("out of memory\n");
768         return STATUS_INSUFFICIENT_RESOURCES;
769     }
770 
771     fcb->Vcb = Vcb;
772 
773     fcb->subvol = subvol;
774     fcb->inode = inode;
775     fcb->hash = hash;
776     fcb->type = type;
777 
778     searchkey.obj_id = inode;
779     searchkey.obj_type = TYPE_INODE_ITEM;
780     searchkey.offset = 0xffffffffffffffff;
781 
782     Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
783     if (!NT_SUCCESS(Status)) {
784         ERR("error - find_item returned %08lx\n", Status);
785         reap_fcb(fcb);
786         return Status;
787     }
788 
789     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
790         WARN("couldn't find INODE_ITEM for inode %I64x in subvol %I64x\n", inode, subvol->id);
791         reap_fcb(fcb);
792         return STATUS_INVALID_PARAMETER;
793     }
794 
795     if (tp.item->size > 0)
796         RtlCopyMemory(&fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
797 
798     if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already
799         if ((fcb->inode_item.st_mode & __S_IFDIR) == __S_IFDIR)
800             fcb->type = BTRFS_TYPE_DIRECTORY;
801         else if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR)
802             fcb->type = BTRFS_TYPE_CHARDEV;
803         else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK)
804             fcb->type = BTRFS_TYPE_BLOCKDEV;
805         else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO)
806             fcb->type = BTRFS_TYPE_FIFO;
807         else if ((fcb->inode_item.st_mode & __S_IFLNK) == __S_IFLNK)
808             fcb->type = BTRFS_TYPE_SYMLINK;
809         else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK)
810             fcb->type = BTRFS_TYPE_SOCKET;
811         else
812             fcb->type = BTRFS_TYPE_FILE;
813     }
814 
815     no_data = fcb->inode_item.st_size == 0 || (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK);
816 
817     while (find_next_item(Vcb, &tp, &next_tp, false, Irp)) {
818         tp = next_tp;
819 
820         if (tp.item->key.obj_id > inode)
821             break;
822 
823         if ((no_data && tp.item->key.obj_type > TYPE_XATTR_ITEM) || tp.item->key.obj_type > TYPE_EXTENT_DATA)
824             break;
825 
826         if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_REF) {
827             ULONG len;
828             INODE_REF* ir;
829 
830             len = tp.item->size;
831             ir = (INODE_REF*)tp.item->data;
832 
833             while (len >= sizeof(INODE_REF) - 1) {
834                 hardlink* hl;
835                 ULONG stringlen;
836 
837                 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
838                 if (!hl) {
839                     ERR("out of memory\n");
840                     reap_fcb(fcb);
841                     return STATUS_INSUFFICIENT_RESOURCES;
842                 }
843 
844                 hl->parent = tp.item->key.offset;
845                 hl->index = ir->index;
846 
847                 hl->utf8.Length = hl->utf8.MaximumLength = ir->n;
848 
849                 if (hl->utf8.Length > 0) {
850                     hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
851                     RtlCopyMemory(hl->utf8.Buffer, ir->name, ir->n);
852                 }
853 
854                 Status = utf8_to_utf16(NULL, 0, &stringlen, ir->name, ir->n);
855                 if (!NT_SUCCESS(Status)) {
856                     ERR("utf8_to_utf16 1 returned %08lx\n", Status);
857                     ExFreePool(hl);
858                     reap_fcb(fcb);
859                     return Status;
860                 }
861 
862                 hl->name.Length = hl->name.MaximumLength = (uint16_t)stringlen;
863 
864                 if (stringlen == 0)
865                     hl->name.Buffer = NULL;
866                 else {
867                     hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
868 
869                     if (!hl->name.Buffer) {
870                         ERR("out of memory\n");
871                         ExFreePool(hl);
872                         reap_fcb(fcb);
873                         return STATUS_INSUFFICIENT_RESOURCES;
874                     }
875 
876                     Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
877                     if (!NT_SUCCESS(Status)) {
878                         ERR("utf8_to_utf16 2 returned %08lx\n", Status);
879                         ExFreePool(hl->name.Buffer);
880                         ExFreePool(hl);
881                         reap_fcb(fcb);
882                         return Status;
883                     }
884                 }
885 
886                 InsertTailList(&fcb->hardlinks, &hl->list_entry);
887 
888                 len -= sizeof(INODE_REF) - 1 + ir->n;
889                 ir = (INODE_REF*)&ir->name[ir->n];
890             }
891         } else if ((always_add_hl || fcb->inode_item.st_nlink > 1) && tp.item->key.obj_type == TYPE_INODE_EXTREF) {
892             ULONG len;
893             INODE_EXTREF* ier;
894 
895             len = tp.item->size;
896             ier = (INODE_EXTREF*)tp.item->data;
897 
898             while (len >= sizeof(INODE_EXTREF) - 1) {
899                 hardlink* hl;
900                 ULONG stringlen;
901 
902                 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
903                 if (!hl) {
904                     ERR("out of memory\n");
905                     reap_fcb(fcb);
906                     return STATUS_INSUFFICIENT_RESOURCES;
907                 }
908 
909                 hl->parent = ier->dir;
910                 hl->index = ier->index;
911 
912                 hl->utf8.Length = hl->utf8.MaximumLength = ier->n;
913 
914                 if (hl->utf8.Length > 0) {
915                     hl->utf8.Buffer = ExAllocatePoolWithTag(pooltype, hl->utf8.MaximumLength, ALLOC_TAG);
916                     RtlCopyMemory(hl->utf8.Buffer, ier->name, ier->n);
917                 }
918 
919                 Status = utf8_to_utf16(NULL, 0, &stringlen, ier->name, ier->n);
920                 if (!NT_SUCCESS(Status)) {
921                     ERR("utf8_to_utf16 1 returned %08lx\n", Status);
922                     ExFreePool(hl);
923                     reap_fcb(fcb);
924                     return Status;
925                 }
926 
927                 hl->name.Length = hl->name.MaximumLength = (uint16_t)stringlen;
928 
929                 if (stringlen == 0)
930                     hl->name.Buffer = NULL;
931                 else {
932                     hl->name.Buffer = ExAllocatePoolWithTag(pooltype, hl->name.MaximumLength, ALLOC_TAG);
933 
934                     if (!hl->name.Buffer) {
935                         ERR("out of memory\n");
936                         ExFreePool(hl);
937                         reap_fcb(fcb);
938                         return STATUS_INSUFFICIENT_RESOURCES;
939                     }
940 
941                     Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
942                     if (!NT_SUCCESS(Status)) {
943                         ERR("utf8_to_utf16 2 returned %08lx\n", Status);
944                         ExFreePool(hl->name.Buffer);
945                         ExFreePool(hl);
946                         reap_fcb(fcb);
947                         return Status;
948                     }
949                 }
950 
951                 InsertTailList(&fcb->hardlinks, &hl->list_entry);
952 
953                 len -= sizeof(INODE_EXTREF) - 1 + ier->n;
954                 ier = (INODE_EXTREF*)&ier->name[ier->n];
955             }
956         } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
957             ULONG len;
958             DIR_ITEM* di;
959 
960             static const char xapref[] = "user.";
961 
962             if (tp.item->size < offsetof(DIR_ITEM, name[0])) {
963                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(DIR_ITEM, name[0]));
964                 continue;
965             }
966 
967             len = tp.item->size;
968             di = (DIR_ITEM*)tp.item->data;
969 
970             do {
971                 if (len < offsetof(DIR_ITEM, name[0]) + di->m + di->n)
972                     break;
973 
974                 if (tp.item->key.offset == EA_REPARSE_HASH && di->n == sizeof(EA_REPARSE) - 1 && RtlCompareMemory(EA_REPARSE, di->name, di->n) == di->n) {
975                     if (di->m > 0) {
976                         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
977                         if (!fcb->reparse_xattr.Buffer) {
978                             ERR("out of memory\n");
979                             reap_fcb(fcb);
980                             return STATUS_INSUFFICIENT_RESOURCES;
981                         }
982 
983                         RtlCopyMemory(fcb->reparse_xattr.Buffer, &di->name[di->n], di->m);
984                     } else
985                         fcb->reparse_xattr.Buffer = NULL;
986 
987                     fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = di->m;
988                 } else if (tp.item->key.offset == EA_EA_HASH && di->n == sizeof(EA_EA) - 1 && RtlCompareMemory(EA_EA, di->name, di->n) == di->n) {
989                     if (di->m > 0) {
990                         ULONG offset;
991 
992                         Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)&di->name[di->n], di->m, &offset);
993 
994                         if (!NT_SUCCESS(Status))
995                             WARN("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
996                         else {
997                             FILE_FULL_EA_INFORMATION* eainfo;
998 
999                             fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
1000                             if (!fcb->ea_xattr.Buffer) {
1001                                 ERR("out of memory\n");
1002                                 reap_fcb(fcb);
1003                                 return STATUS_INSUFFICIENT_RESOURCES;
1004                             }
1005 
1006                             RtlCopyMemory(fcb->ea_xattr.Buffer, &di->name[di->n], di->m);
1007 
1008                             fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = di->m;
1009 
1010                             fcb->ealen = 4;
1011 
1012                             // calculate ealen
1013                             eainfo = (FILE_FULL_EA_INFORMATION*)&di->name[di->n];
1014                             do {
1015                                 fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
1016 
1017                                 if (eainfo->NextEntryOffset == 0)
1018                                     break;
1019 
1020                                 eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset);
1021                             } while (true);
1022                         }
1023                     }
1024                 } else if (tp.item->key.offset == EA_DOSATTRIB_HASH && di->n == sizeof(EA_DOSATTRIB) - 1 && RtlCompareMemory(EA_DOSATTRIB, di->name, di->n) == di->n) {
1025                     if (di->m > 0) {
1026                         if (get_file_attributes_from_xattr(&di->name[di->n], di->m, &fcb->atts)) {
1027                             atts_set = true;
1028 
1029                             if (fcb->type == BTRFS_TYPE_DIRECTORY)
1030                                 fcb->atts |= FILE_ATTRIBUTE_DIRECTORY;
1031                             else if (fcb->type == BTRFS_TYPE_SYMLINK)
1032                                 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
1033 
1034                             if (fcb->type != BTRFS_TYPE_DIRECTORY)
1035                                 fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY;
1036 
1037                             if (inode == SUBVOL_ROOT_INODE) {
1038                                 if (subvol->root_item.flags & BTRFS_SUBVOL_READONLY)
1039                                     fcb->atts |= FILE_ATTRIBUTE_READONLY;
1040                                 else
1041                                     fcb->atts &= ~FILE_ATTRIBUTE_READONLY;
1042                             }
1043                         }
1044                     }
1045                 } else if (tp.item->key.offset == EA_NTACL_HASH && di->n == sizeof(EA_NTACL) - 1 && RtlCompareMemory(EA_NTACL, di->name, di->n) == di->n) {
1046                     if (di->m > 0) {
1047                         fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m, ALLOC_TAG);
1048                         if (!fcb->sd) {
1049                             ERR("out of memory\n");
1050                             reap_fcb(fcb);
1051                             return STATUS_INSUFFICIENT_RESOURCES;
1052                         }
1053 
1054                         RtlCopyMemory(fcb->sd, &di->name[di->n], di->m);
1055 
1056                         // We have to test against our copy rather than the source, as RtlValidRelativeSecurityDescriptor
1057                         // will fail if the ACLs aren't 32-bit aligned.
1058                         if (!RtlValidRelativeSecurityDescriptor(fcb->sd, di->m, 0))
1059                             ExFreePool(fcb->sd);
1060                         else
1061                             sd_set = true;
1062                     }
1063                 } else if (tp.item->key.offset == EA_PROP_COMPRESSION_HASH && di->n == sizeof(EA_PROP_COMPRESSION) - 1 && RtlCompareMemory(EA_PROP_COMPRESSION, di->name, di->n) == di->n) {
1064                     if (di->m > 0) {
1065                         static const char lzo[] = "lzo";
1066                         static const char zlib[] = "zlib";
1067                         static const char zstd[] = "zstd";
1068 
1069                         if (di->m == sizeof(lzo) - 1 && RtlCompareMemory(&di->name[di->n], lzo, di->m) == di->m)
1070                             fcb->prop_compression = PropCompression_LZO;
1071                         else if (di->m == sizeof(zlib) - 1 && RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m)
1072                             fcb->prop_compression = PropCompression_Zlib;
1073                         else if (di->m == sizeof(zstd) - 1 && RtlCompareMemory(&di->name[di->n], zstd, di->m) == di->m)
1074                             fcb->prop_compression = PropCompression_ZSTD;
1075                         else
1076                             fcb->prop_compression = PropCompression_None;
1077                     }
1078                 } else if (tp.item->key.offset == EA_CASE_SENSITIVE_HASH && di->n == sizeof(EA_CASE_SENSITIVE) - 1 && RtlCompareMemory(EA_CASE_SENSITIVE, di->name, di->n) == di->n) {
1079                     if (di->m > 0) {
1080                         fcb->case_sensitive = di->m == 1 && di->name[di->n] == '1';
1081                         fcb->case_sensitive_set = true;
1082                     }
1083                 } else if (di->n > sizeof(xapref) - 1 && RtlCompareMemory(xapref, di->name, sizeof(xapref) - 1) == sizeof(xapref) - 1) {
1084                     dir_child* dc;
1085                     ULONG utf16len;
1086 
1087                     Status = utf8_to_utf16(NULL, 0, &utf16len, &di->name[sizeof(xapref) - 1], di->n + 1 - sizeof(xapref));
1088                     if (!NT_SUCCESS(Status)) {
1089                         ERR("utf8_to_utf16 1 returned %08lx\n", Status);
1090                         reap_fcb(fcb);
1091                         return Status;
1092                     }
1093 
1094                     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
1095                     if (!dc) {
1096                         ERR("out of memory\n");
1097                         reap_fcb(fcb);
1098                         return STATUS_INSUFFICIENT_RESOURCES;
1099                     }
1100 
1101                     RtlZeroMemory(dc, sizeof(dir_child));
1102 
1103                     dc->utf8.MaximumLength = dc->utf8.Length = di->n + 1 - sizeof(xapref);
1104                     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
1105                     if (!dc->utf8.Buffer) {
1106                         ERR("out of memory\n");
1107                         ExFreePool(dc);
1108                         reap_fcb(fcb);
1109                         return STATUS_INSUFFICIENT_RESOURCES;
1110                     }
1111 
1112                     RtlCopyMemory(dc->utf8.Buffer, &di->name[sizeof(xapref) - 1], dc->utf8.Length);
1113 
1114                     dc->name.MaximumLength = dc->name.Length = (uint16_t)utf16len;
1115                     dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, dc->name.MaximumLength, ALLOC_TAG);
1116                     if (!dc->name.Buffer) {
1117                         ERR("out of memory\n");
1118                         ExFreePool(dc->utf8.Buffer);
1119                         ExFreePool(dc);
1120                         reap_fcb(fcb);
1121                         return STATUS_INSUFFICIENT_RESOURCES;
1122                     }
1123 
1124                     Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, dc->utf8.Buffer, dc->utf8.Length);
1125                     if (!NT_SUCCESS(Status)) {
1126                         ERR("utf8_to_utf16 2 returned %08lx\n", Status);
1127                         ExFreePool(dc->utf8.Buffer);
1128                         ExFreePool(dc->name.Buffer);
1129                         ExFreePool(dc);
1130                         reap_fcb(fcb);
1131                         return Status;
1132                     }
1133 
1134                     Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
1135                     if (!NT_SUCCESS(Status)) {
1136                         ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
1137                         ExFreePool(dc->utf8.Buffer);
1138                         ExFreePool(dc->name.Buffer);
1139                         ExFreePool(dc);
1140                         reap_fcb(fcb);
1141                         return Status;
1142                     }
1143 
1144                     dc->size = di->m;
1145 
1146                     InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
1147                 } else {
1148                     xattr* xa;
1149 
1150                     xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + di->m + di->n, ALLOC_TAG);
1151                     if (!xa) {
1152                         ERR("out of memory\n");
1153                         reap_fcb(fcb);
1154                         return STATUS_INSUFFICIENT_RESOURCES;
1155                     }
1156 
1157                     xa->namelen = di->n;
1158                     xa->valuelen = di->m;
1159                     xa->dirty = false;
1160                     RtlCopyMemory(xa->data, di->name, di->m + di->n);
1161 
1162                     InsertTailList(&fcb->xattrs, &xa->list_entry);
1163                 }
1164 
1165                 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
1166 
1167                 if (len < offsetof(DIR_ITEM, name[0]))
1168                     break;
1169 
1170                 di = (DIR_ITEM*)&di->name[di->m + di->n];
1171             } while (true);
1172         } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
1173             extent* ext;
1174             bool unique = false;
1175 
1176             ed = (EXTENT_DATA*)tp.item->data;
1177 
1178             if (tp.item->size < sizeof(EXTENT_DATA)) {
1179                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1180                     tp.item->size, sizeof(EXTENT_DATA));
1181 
1182                 reap_fcb(fcb);
1183                 return STATUS_INTERNAL_ERROR;
1184             }
1185 
1186             if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
1187                 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
1188 
1189                 if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
1190                     ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1191                         tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
1192 
1193                     reap_fcb(fcb);
1194                     return STATUS_INTERNAL_ERROR;
1195                 }
1196 
1197                 if (ed2->address == 0 || ed2->size == 0) // sparse
1198                     continue;
1199 
1200                 if (ed2->size != 0 && is_tree_unique(Vcb, tp.tree, Irp))
1201                     unique = is_extent_unique(Vcb, ed2->address, ed2->size, Irp);
1202             }
1203 
1204             ext = ExAllocatePoolWithTag(pooltype, offsetof(extent, extent_data) + tp.item->size, ALLOC_TAG);
1205             if (!ext) {
1206                 ERR("out of memory\n");
1207                 reap_fcb(fcb);
1208                 return STATUS_INSUFFICIENT_RESOURCES;
1209             }
1210 
1211             ext->offset = tp.item->key.offset;
1212             RtlCopyMemory(&ext->extent_data, tp.item->data, tp.item->size);
1213             ext->datalen = tp.item->size;
1214             ext->unique = unique;
1215             ext->ignore = false;
1216             ext->inserted = false;
1217             ext->csum = NULL;
1218 
1219             InsertTailList(&fcb->extents, &ext->list_entry);
1220         }
1221     }
1222 
1223     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
1224         Status = load_dir_children(Vcb, fcb, false, Irp);
1225         if (!NT_SUCCESS(Status)) {
1226             ERR("load_dir_children returned %08lx\n", Status);
1227             reap_fcb(fcb);
1228             return Status;
1229         }
1230     }
1231 
1232     if (no_data) {
1233         fcb->Header.AllocationSize.QuadPart = 0;
1234         fcb->Header.FileSize.QuadPart = 0;
1235         fcb->Header.ValidDataLength.QuadPart = 0;
1236     } else {
1237         if (ed && ed->type == EXTENT_TYPE_INLINE)
1238             fcb->Header.AllocationSize.QuadPart = fcb->inode_item.st_size;
1239         else
1240             fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
1241 
1242         fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
1243         fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
1244     }
1245 
1246     if (!atts_set)
1247         fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, utf8 && utf8->Buffer[0] == '.', true, Irp);
1248 
1249     if (!sd_set)
1250         fcb_get_sd(fcb, parent, false, Irp);
1251 
1252     acquire_fcb_lock_exclusive(Vcb);
1253 
1254     if (lastle && subvol->fcbs_version == fcbs_version) {
1255         InsertHeadList(lastle, &fcb->list_entry);
1256 
1257         if (!subvol->fcbs_ptrs[hash >> 24] || CONTAINING_RECORD(subvol->fcbs_ptrs[hash >> 24], struct _fcb, list_entry)->hash > hash)
1258             subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
1259     } else {
1260         lastle = NULL;
1261 
1262         if (subvol->fcbs_ptrs[hash >> 24]) {
1263             LIST_ENTRY* le = subvol->fcbs_ptrs[hash >> 24];
1264 
1265             while (le != &subvol->fcbs) {
1266                 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
1267 
1268                 if (fcb2->inode == inode) {
1269                     if (!fcb2->ads) {
1270                         if (fcb2->deleted)
1271                             deleted_fcb = fcb2;
1272                         else {
1273 #ifdef DEBUG_FCB_REFCOUNTS
1274                             LONG rc = InterlockedIncrement(&fcb2->refcount);
1275 
1276                             WARN("fcb %p: refcount now %i (subvol %I64x, inode %I64x)\n", fcb2, rc, fcb2->subvol->id, fcb2->inode);
1277 #else
1278                             InterlockedIncrement(&fcb2->refcount);
1279 #endif
1280 
1281                             *pfcb = fcb2;
1282                             reap_fcb(fcb);
1283                             release_fcb_lock(Vcb);
1284                             return STATUS_SUCCESS;
1285                         }
1286                     }
1287                 } else if (fcb2->hash > hash) {
1288                     if (deleted_fcb) {
1289                         InterlockedIncrement(&deleted_fcb->refcount);
1290                         *pfcb = deleted_fcb;
1291                         reap_fcb(fcb);
1292                         release_fcb_lock(Vcb);
1293                         return STATUS_SUCCESS;
1294                     }
1295 
1296                     lastle = le->Blink;
1297                     break;
1298                 }
1299 
1300                 le = le->Flink;
1301             }
1302         }
1303 
1304         if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT && fcb->reparse_xattr.Length == 0) {
1305             fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
1306 
1307             if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
1308                 fcb->atts_changed = true;
1309                 mark_fcb_dirty(fcb);
1310             }
1311         }
1312 
1313         if (!lastle) {
1314             uint8_t c = hash >> 24;
1315 
1316             if (c != 0xff) {
1317                 uint8_t d = c + 1;
1318 
1319                 do {
1320                     if (subvol->fcbs_ptrs[d]) {
1321                         lastle = subvol->fcbs_ptrs[d]->Blink;
1322                         break;
1323                     }
1324 
1325                     d++;
1326                 } while (d != 0);
1327             }
1328         }
1329 
1330         if (lastle) {
1331             InsertHeadList(lastle, &fcb->list_entry);
1332 
1333             if (lastle == &subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
1334                 subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
1335         } else {
1336             InsertTailList(&subvol->fcbs, &fcb->list_entry);
1337 
1338             if (fcb->list_entry.Blink == &subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (hash >> 24))
1339                 subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry;
1340         }
1341     }
1342 
1343     if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE && fcb->subvol != Vcb->root_fileref->fcb->subvol)
1344         fcb->atts |= FILE_ATTRIBUTE_HIDDEN;
1345 
1346     subvol->fcbs_version++;
1347 
1348     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
1349 
1350     release_fcb_lock(Vcb);
1351 
1352     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
1353 
1354     *pfcb = fcb;
1355 
1356     return STATUS_SUCCESS;
1357 }
1358 
1359 static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
1360                                 dir_child* dc, fcb* parent, fcb** pfcb, PIRP Irp) {
1361     fcb* fcb;
1362     uint8_t* xattrdata;
1363     uint16_t xattrlen, overhead;
1364     NTSTATUS Status;
1365     KEY searchkey;
1366     traverse_ptr tp;
1367     static const char xapref[] = "user.";
1368     ANSI_STRING xattr;
1369     uint32_t crc32;
1370 
1371     xattr.Length = sizeof(xapref) - 1 + dc->utf8.Length;
1372     xattr.MaximumLength = xattr.Length + 1;
1373     xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG);
1374     if (!xattr.Buffer) {
1375         ERR("out of memory\n");
1376         return STATUS_INSUFFICIENT_RESOURCES;
1377     }
1378 
1379     RtlCopyMemory(xattr.Buffer, xapref, sizeof(xapref) - 1);
1380     RtlCopyMemory(&xattr.Buffer[sizeof(xapref) - 1], dc->utf8.Buffer, dc->utf8.Length);
1381     xattr.Buffer[xattr.Length] = 0;
1382 
1383     fcb = create_fcb(Vcb, PagedPool);
1384     if (!fcb) {
1385         ERR("out of memory\n");
1386         ExFreePool(xattr.Buffer);
1387         return STATUS_INSUFFICIENT_RESOURCES;
1388     }
1389 
1390     fcb->Vcb = Vcb;
1391 
1392     crc32 = calc_crc32c(0xfffffffe, (uint8_t*)xattr.Buffer, xattr.Length);
1393 
1394     if (!get_xattr(Vcb, parent->subvol, parent->inode, xattr.Buffer, crc32, &xattrdata, &xattrlen, Irp)) {
1395         ERR("get_xattr failed\n");
1396         reap_fcb(fcb);
1397         ExFreePool(xattr.Buffer);
1398         return STATUS_INTERNAL_ERROR;
1399     }
1400 
1401     fcb->subvol = parent->subvol;
1402     fcb->inode = parent->inode;
1403     fcb->type = parent->type;
1404     fcb->ads = true;
1405     fcb->adshash = crc32;
1406     fcb->adsxattr = xattr;
1407 
1408     // find XATTR_ITEM overhead and hence calculate maximum length
1409 
1410     searchkey.obj_id = parent->inode;
1411     searchkey.obj_type = TYPE_XATTR_ITEM;
1412     searchkey.offset = crc32;
1413 
1414     Status = find_item(Vcb, parent->subvol, &tp, &searchkey, false, Irp);
1415     if (!NT_SUCCESS(Status)) {
1416         ERR("find_item returned %08lx\n", Status);
1417         reap_fcb(fcb);
1418         return Status;
1419     }
1420 
1421     if (keycmp(tp.item->key, searchkey)) {
1422         ERR("error - could not find key for xattr\n");
1423         reap_fcb(fcb);
1424         return STATUS_INTERNAL_ERROR;
1425     }
1426 
1427     if (tp.item->size < xattrlen) {
1428         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, xattrlen);
1429         reap_fcb(fcb);
1430         return STATUS_INTERNAL_ERROR;
1431     }
1432 
1433     overhead = tp.item->size - xattrlen;
1434 
1435     fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - overhead;
1436 
1437     fcb->adsdata.Buffer = (char*)xattrdata;
1438     fcb->adsdata.Length = fcb->adsdata.MaximumLength = xattrlen;
1439 
1440     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
1441     fcb->Header.AllocationSize.QuadPart = xattrlen;
1442     fcb->Header.FileSize.QuadPart = xattrlen;
1443     fcb->Header.ValidDataLength.QuadPart = xattrlen;
1444 
1445     TRACE("stream found: size = %x, hash = %08x\n", xattrlen, fcb->adshash);
1446 
1447     *pfcb = fcb;
1448 
1449     return STATUS_SUCCESS;
1450 }
1451 
1452 NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb,
1453                             _In_ file_ref* sf, _In_ PUNICODE_STRING name, _In_ bool case_sensitive, _In_ bool lastpart, _In_ bool streampart,
1454                             _In_ POOL_TYPE pooltype, _Out_ file_ref** psf2, _In_opt_ PIRP Irp) {
1455     NTSTATUS Status;
1456     file_ref* sf2;
1457 
1458     if (sf->fcb == Vcb->dummy_fcb)
1459         return STATUS_OBJECT_NAME_NOT_FOUND;
1460 
1461     if (streampart) {
1462         bool locked = false;
1463         LIST_ENTRY* le;
1464         UNICODE_STRING name_uc;
1465         dir_child* dc = NULL;
1466         fcb* fcb;
1467         struct _fcb* duff_fcb = NULL;
1468         file_ref* duff_fr = NULL;
1469 
1470         if (!case_sensitive) {
1471             Status = RtlUpcaseUnicodeString(&name_uc, name, true);
1472             if (!NT_SUCCESS(Status)) {
1473                 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
1474                 return Status;
1475             }
1476         }
1477 
1478         if (!ExIsResourceAcquiredSharedLite(&sf->fcb->nonpaged->dir_children_lock)) {
1479             ExAcquireResourceSharedLite(&sf->fcb->nonpaged->dir_children_lock, true);
1480             locked = true;
1481         }
1482 
1483         le = sf->fcb->dir_children_index.Flink;
1484         while (le != &sf->fcb->dir_children_index) {
1485             dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
1486 
1487             if (dc2->index == 0) {
1488                 if ((case_sensitive && dc2->name.Length == name->Length && RtlCompareMemory(dc2->name.Buffer, name->Buffer, dc2->name.Length) == dc2->name.Length) ||
1489                     (!case_sensitive && dc2->name_uc.Length == name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length)
1490                 ) {
1491                     dc = dc2;
1492                     break;
1493                 }
1494             } else
1495                 break;
1496 
1497             le = le->Flink;
1498         }
1499 
1500         if (!dc) {
1501             if (locked)
1502                 ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1503 
1504             if (!case_sensitive)
1505                 ExFreePool(name_uc.Buffer);
1506 
1507             return STATUS_OBJECT_NAME_NOT_FOUND;
1508         }
1509 
1510         if (dc->fileref) {
1511             if (locked)
1512                 ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1513 
1514             if (!case_sensitive)
1515                 ExFreePool(name_uc.Buffer);
1516 
1517             increase_fileref_refcount(dc->fileref);
1518             *psf2 = dc->fileref;
1519             return STATUS_SUCCESS;
1520         }
1521 
1522         if (locked)
1523             ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1524 
1525         if (!case_sensitive)
1526             ExFreePool(name_uc.Buffer);
1527 
1528         Status = open_fcb_stream(Vcb, dc, sf->fcb, &fcb, Irp);
1529         if (!NT_SUCCESS(Status)) {
1530             ERR("open_fcb_stream returned %08lx\n", Status);
1531             return Status;
1532         }
1533 
1534         fcb->hash = sf->fcb->hash;
1535 
1536         acquire_fcb_lock_exclusive(Vcb);
1537 
1538         if (sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
1539             le = sf->fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
1540 
1541             while (le != &sf->fcb->subvol->fcbs) {
1542                 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
1543 
1544                 if (fcb2->inode == fcb->inode) {
1545                     if (fcb2->ads && fcb2->adshash == fcb->adshash) { // FIXME - handle hash collisions
1546                         duff_fcb = fcb;
1547                         fcb = fcb2;
1548                         break;
1549                     }
1550                 } else if (fcb2->hash > fcb->hash)
1551                     break;
1552 
1553                 le = le->Flink;
1554             }
1555         }
1556 
1557         if (!duff_fcb) {
1558             InsertHeadList(&sf->fcb->list_entry, &fcb->list_entry);
1559             InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
1560             fcb->subvol->fcbs_version++;
1561         }
1562 
1563         release_fcb_lock(Vcb);
1564 
1565         if (duff_fcb) {
1566             reap_fcb(duff_fcb);
1567             InterlockedIncrement(&fcb->refcount);
1568         }
1569 
1570         sf2 = create_fileref(Vcb);
1571         if (!sf2) {
1572             ERR("out of memory\n");
1573             free_fcb(fcb);
1574             return STATUS_INSUFFICIENT_RESOURCES;
1575         }
1576 
1577         ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, true);
1578 
1579         if (dc->fileref) {
1580             duff_fr = sf2;
1581             sf2 = dc->fileref;
1582             increase_fileref_refcount(sf2);
1583         } else {
1584             sf2->fcb = fcb;
1585             sf2->parent = (struct _file_ref*)sf;
1586             sf2->dc = dc;
1587             dc->fileref = sf2;
1588             increase_fileref_refcount(sf);
1589             InsertTailList(&sf->children, &sf2->list_entry);
1590         }
1591 
1592         ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1593 
1594         if (duff_fr)
1595             reap_fileref(Vcb, duff_fr);
1596     } else {
1597         root* subvol;
1598         uint64_t inode;
1599         dir_child* dc;
1600 
1601         Status = find_file_in_dir(name, sf->fcb, &subvol, &inode, &dc, case_sensitive);
1602         if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
1603             TRACE("could not find %.*S\n", (int)(name->Length / sizeof(WCHAR)), name->Buffer);
1604 
1605             return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
1606         } else if (!NT_SUCCESS(Status)) {
1607             ERR("find_file_in_dir returned %08lx\n", Status);
1608             return Status;
1609         } else {
1610             fcb* fcb;
1611             file_ref* duff_fr = NULL;
1612 
1613             if (dc->fileref) {
1614                 if (!lastpart && dc->type != BTRFS_TYPE_DIRECTORY) {
1615                     TRACE("passed path including file as subdirectory\n");
1616                     return STATUS_OBJECT_PATH_NOT_FOUND;
1617                 }
1618 
1619                 InterlockedIncrement(&dc->fileref->refcount);
1620                 *psf2 = dc->fileref;
1621                 return STATUS_SUCCESS;
1622             }
1623 
1624             if (!subvol || (subvol != Vcb->root_fileref->fcb->subvol && inode == SUBVOL_ROOT_INODE && subvol->parent != sf->fcb->subvol->id && !dc->root_dir)) {
1625                 fcb = Vcb->dummy_fcb;
1626                 InterlockedIncrement(&fcb->refcount);
1627             } else {
1628                 Status = open_fcb(Vcb, subvol, inode, dc->type, &dc->utf8, false, sf->fcb, &fcb, pooltype, Irp);
1629 
1630                 if (!NT_SUCCESS(Status)) {
1631                     ERR("open_fcb returned %08lx\n", Status);
1632                     return Status;
1633                 }
1634             }
1635 
1636             if (dc->type != BTRFS_TYPE_DIRECTORY && !lastpart && !(fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
1637                 TRACE("passed path including file as subdirectory\n");
1638                 free_fcb(fcb);
1639                 return STATUS_OBJECT_PATH_NOT_FOUND;
1640             }
1641 
1642             sf2 = create_fileref(Vcb);
1643             if (!sf2) {
1644                 ERR("out of memory\n");
1645                 free_fcb(fcb);
1646                 return STATUS_INSUFFICIENT_RESOURCES;
1647             }
1648 
1649             sf2->fcb = fcb;
1650 
1651             if (dc->type == BTRFS_TYPE_DIRECTORY)
1652                 fcb->fileref = sf2;
1653 
1654             ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, true);
1655 
1656             if (!dc->fileref) {
1657                 sf2->parent = (struct _file_ref*)sf;
1658                 sf2->dc = dc;
1659                 dc->fileref = sf2;
1660                 InsertTailList(&sf->children, &sf2->list_entry);
1661                 increase_fileref_refcount(sf);
1662             } else {
1663                 duff_fr = sf2;
1664                 sf2 = dc->fileref;
1665                 increase_fileref_refcount(sf2);
1666             }
1667 
1668             ExReleaseResourceLite(&sf->fcb->nonpaged->dir_children_lock);
1669 
1670             if (duff_fr)
1671                 reap_fileref(Vcb, duff_fr);
1672         }
1673     }
1674 
1675     *psf2 = sf2;
1676 
1677     return STATUS_SUCCESS;
1678 }
1679 
1680 NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Out_ file_ref** pfr,
1681                       _In_ PUNICODE_STRING fnus, _In_opt_ file_ref* related, _In_ bool parent, _Out_opt_ USHORT* parsed, _Out_opt_ ULONG* fn_offset, _In_ POOL_TYPE pooltype,
1682                       _In_ bool case_sensitive, _In_opt_ PIRP Irp) {
1683     UNICODE_STRING fnus2;
1684     file_ref *dir, *sf, *sf2;
1685     LIST_ENTRY parts;
1686     bool has_stream;
1687     NTSTATUS Status;
1688     LIST_ENTRY* le;
1689 
1690     TRACE("(%p, %p, %p, %u, %p)\n", Vcb, pfr, related, parent, parsed);
1691 
1692     if (Vcb->removing || Vcb->locked)
1693         return STATUS_ACCESS_DENIED;
1694 
1695     fnus2 = *fnus;
1696 
1697     if (fnus2.Length < sizeof(WCHAR) && !related) {
1698         ERR("error - fnus was too short\n");
1699         return STATUS_INTERNAL_ERROR;
1700     }
1701 
1702     if (related && fnus->Length == 0) {
1703         increase_fileref_refcount(related);
1704 
1705         *pfr = related;
1706         return STATUS_SUCCESS;
1707     }
1708 
1709     if (related) {
1710         dir = related;
1711     } else {
1712         if (fnus2.Buffer[0] != '\\') {
1713             ERR("error - filename %.*S did not begin with \\\n", (int)(fnus2.Length / sizeof(WCHAR)), fnus2.Buffer);
1714             return STATUS_OBJECT_PATH_NOT_FOUND;
1715         }
1716 
1717         // if path starts with two backslashes, ignore one of them
1718         if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\') {
1719             fnus2.Buffer++;
1720             fnus2.Length -= sizeof(WCHAR);
1721             fnus2.MaximumLength -= sizeof(WCHAR);
1722         }
1723 
1724         if (fnus2.Length == sizeof(WCHAR)) {
1725             if (Vcb->root_fileref->open_count == 0 && !(Vcb->Vpb->Flags & VPB_MOUNTED)) // don't allow root to be opened on unmounted FS
1726                 return STATUS_DEVICE_NOT_READY;
1727 
1728             increase_fileref_refcount(Vcb->root_fileref);
1729             *pfr = Vcb->root_fileref;
1730 
1731             if (fn_offset)
1732                 *fn_offset = 0;
1733 
1734             return STATUS_SUCCESS;
1735         } else if (fnus2.Length >= 2 * sizeof(WCHAR) && fnus2.Buffer[1] == '\\')
1736             return STATUS_OBJECT_NAME_INVALID;
1737 
1738         dir = Vcb->root_fileref;
1739 
1740         fnus2.Buffer++;
1741         fnus2.Length -= sizeof(WCHAR);
1742         fnus2.MaximumLength -= sizeof(WCHAR);
1743     }
1744 
1745     if (dir->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
1746         WARN("passed related fileref which isn't a directory (fnus = %.*S)\n",
1747              (int)(fnus->Length / sizeof(WCHAR)), fnus->Buffer);
1748         return STATUS_OBJECT_PATH_NOT_FOUND;
1749     }
1750 
1751     InitializeListHead(&parts);
1752 
1753     if (fnus->Length != 0 &&
1754         (fnus->Length != sizeof(datastring) - sizeof(WCHAR) || RtlCompareMemory(fnus->Buffer, datastring, sizeof(datastring) - sizeof(WCHAR)) != sizeof(datastring) - sizeof(WCHAR))) {
1755         Status = split_path(Vcb, &fnus2, &parts, &has_stream);
1756         if (!NT_SUCCESS(Status)) {
1757             ERR("split_path returned %08lx\n", Status);
1758             return Status;
1759         }
1760     }
1761 
1762     sf = dir;
1763     increase_fileref_refcount(dir);
1764 
1765     if (parent && !IsListEmpty(&parts)) {
1766         name_bit* nb;
1767 
1768         nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
1769         ExFreePool(nb);
1770 
1771         if (has_stream && !IsListEmpty(&parts)) {
1772             nb = CONTAINING_RECORD(RemoveTailList(&parts), name_bit, list_entry);
1773             ExFreePool(nb);
1774 
1775             has_stream = false;
1776         }
1777     }
1778 
1779     if (IsListEmpty(&parts)) {
1780         Status = STATUS_SUCCESS;
1781         *pfr = dir;
1782 
1783         if (fn_offset)
1784             *fn_offset = 0;
1785 
1786         goto end2;
1787     }
1788 
1789     le = parts.Flink;
1790     do {
1791         name_bit* nb = CONTAINING_RECORD(le, name_bit, list_entry);
1792         bool lastpart = le->Flink == &parts || (has_stream && le->Flink->Flink == &parts);
1793         bool streampart = has_stream && le->Flink == &parts;
1794         bool cs = case_sensitive;
1795 
1796         if (!cs) {
1797             if (streampart && sf->parent)
1798                 cs = sf->parent->fcb->case_sensitive;
1799             else
1800                 cs = sf->fcb->case_sensitive;
1801         }
1802 
1803         Status = open_fileref_child(Vcb, sf, &nb->us, cs, lastpart, streampart, pooltype, &sf2, Irp);
1804 
1805         if (!NT_SUCCESS(Status)) {
1806             if (Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_NAME_NOT_FOUND)
1807                 TRACE("open_fileref_child returned %08lx\n", Status);
1808             else
1809                 ERR("open_fileref_child returned %08lx\n", Status);
1810 
1811             goto end;
1812         }
1813 
1814         if (le->Flink == &parts) { // last entry
1815             if (fn_offset) {
1816                 if (has_stream)
1817                     nb = CONTAINING_RECORD(le->Blink, name_bit, list_entry);
1818 
1819                 *fn_offset = (ULONG)(nb->us.Buffer - fnus->Buffer);
1820             }
1821 
1822             break;
1823         }
1824 
1825         if (sf2->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
1826             Status = STATUS_REPARSE;
1827 
1828             if (parsed) {
1829                 name_bit* nb2 = CONTAINING_RECORD(le->Flink, name_bit, list_entry);
1830 
1831                 *parsed = (USHORT)(nb2->us.Buffer - fnus->Buffer - 1) * sizeof(WCHAR);
1832             }
1833 
1834             break;
1835         }
1836 
1837         free_fileref(sf);
1838         sf = sf2;
1839 
1840         le = le->Flink;
1841     } while (le != &parts);
1842 
1843     if (Status != STATUS_REPARSE)
1844         Status = STATUS_SUCCESS;
1845     *pfr = sf2;
1846 
1847 end:
1848     free_fileref(sf);
1849 
1850     while (!IsListEmpty(&parts)) {
1851         name_bit* nb = CONTAINING_RECORD(RemoveHeadList(&parts), name_bit, list_entry);
1852         ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
1853     }
1854 
1855 end2:
1856     TRACE("returning %08lx\n", Status);
1857 
1858     return Status;
1859 }
1860 
1861 NTSTATUS add_dir_child(fcb* fcb, uint64_t inode, bool subvol, PANSI_STRING utf8, PUNICODE_STRING name, uint8_t type, dir_child** pdc) {
1862     NTSTATUS Status;
1863     dir_child* dc;
1864     bool locked;
1865 
1866     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
1867     if (!dc) {
1868         ERR("out of memory\n");
1869         return STATUS_INSUFFICIENT_RESOURCES;
1870     }
1871 
1872     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG);
1873     if (!dc->utf8.Buffer) {
1874         ERR("out of memory\n");
1875         ExFreePool(dc);
1876         return STATUS_INSUFFICIENT_RESOURCES;
1877     }
1878 
1879     dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG);
1880     if (!dc->name.Buffer) {
1881         ERR("out of memory\n");
1882         ExFreePool(dc->utf8.Buffer);
1883         ExFreePool(dc);
1884         return STATUS_INSUFFICIENT_RESOURCES;
1885     }
1886 
1887     dc->key.obj_id = inode;
1888     dc->key.obj_type = subvol ? TYPE_ROOT_ITEM : TYPE_INODE_ITEM;
1889     dc->key.offset = subvol ? 0xffffffffffffffff : 0;
1890     dc->type = type;
1891     dc->fileref = NULL;
1892 
1893     dc->utf8.Length = dc->utf8.MaximumLength = utf8->Length;
1894     RtlCopyMemory(dc->utf8.Buffer, utf8->Buffer, utf8->Length);
1895 
1896     dc->name.Length = dc->name.MaximumLength = name->Length;
1897     RtlCopyMemory(dc->name.Buffer, name->Buffer, name->Length);
1898 
1899     Status = RtlUpcaseUnicodeString(&dc->name_uc, name, true);
1900     if (!NT_SUCCESS(Status)) {
1901         ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
1902         ExFreePool(dc->utf8.Buffer);
1903         ExFreePool(dc->name.Buffer);
1904         ExFreePool(dc);
1905         return Status;
1906     }
1907 
1908     dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length);
1909     dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length);
1910 
1911     locked = ExIsResourceAcquiredExclusive(&fcb->nonpaged->dir_children_lock);
1912 
1913     if (!locked)
1914         ExAcquireResourceExclusiveLite(&fcb->nonpaged->dir_children_lock, true);
1915 
1916     if (IsListEmpty(&fcb->dir_children_index))
1917         dc->index = 2;
1918     else {
1919         dir_child* dc2 = CONTAINING_RECORD(fcb->dir_children_index.Blink, dir_child, list_entry_index);
1920 
1921         dc->index = max(2, dc2->index + 1);
1922     }
1923 
1924     InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
1925 
1926     insert_dir_child_into_hash_lists(fcb, dc);
1927 
1928     if (!locked)
1929         ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
1930 
1931     *pdc = dc;
1932 
1933     return STATUS_SUCCESS;
1934 }
1935 
1936 uint32_t inherit_mode(fcb* parfcb, bool is_dir) {
1937     uint32_t mode;
1938 
1939     if (!parfcb)
1940         return 0755;
1941 
1942     mode = parfcb->inode_item.st_mode & ~S_IFDIR;
1943     mode &= ~S_ISVTX; // clear sticky bit
1944     mode &= ~S_ISUID; // clear setuid bit
1945 
1946     if (!is_dir)
1947         mode &= ~S_ISGID; // if not directory, clear setgid bit
1948 
1949     return mode;
1950 }
1951 
1952 static NTSTATUS file_create_parse_ea(fcb* fcb, FILE_FULL_EA_INFORMATION* ea) {
1953     NTSTATUS Status;
1954     LIST_ENTRY ealist, *le;
1955     uint16_t size = 0;
1956     char* buf;
1957 
1958     InitializeListHead(&ealist);
1959 
1960     do {
1961         STRING s;
1962         bool found = false;
1963 
1964         s.Length = s.MaximumLength = ea->EaNameLength;
1965         s.Buffer = ea->EaName;
1966 
1967         RtlUpperString(&s, &s);
1968 
1969         le = ealist.Flink;
1970         while (le != &ealist) {
1971             ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
1972 
1973             if (item->name.Length == s.Length && RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
1974                 item->flags = ea->Flags;
1975                 item->value.Length = item->value.MaximumLength = ea->EaValueLength;
1976                 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
1977                 found = true;
1978                 break;
1979             }
1980 
1981             le = le->Flink;
1982         }
1983 
1984         if (!found) {
1985             ea_item* item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
1986             if (!item) {
1987                 ERR("out of memory\n");
1988                 Status = STATUS_INSUFFICIENT_RESOURCES;
1989                 goto end;
1990             }
1991 
1992             item->name.Length = item->name.MaximumLength = ea->EaNameLength;
1993             item->name.Buffer = ea->EaName;
1994 
1995             item->value.Length = item->value.MaximumLength = ea->EaValueLength;
1996             item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
1997 
1998             item->flags = ea->Flags;
1999 
2000             InsertTailList(&ealist, &item->list_entry);
2001         }
2002 
2003         if (ea->NextEntryOffset == 0)
2004             break;
2005 
2006         ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
2007     } while (true);
2008 
2009     // handle LXSS values
2010     le = ealist.Flink;
2011     while (le != &ealist) {
2012         LIST_ENTRY* le2 = le->Flink;
2013         ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
2014 
2015         if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) {
2016             if (item->value.Length < sizeof(uint32_t)) {
2017                 ERR("uid value was shorter than expected\n");
2018                 Status = STATUS_INVALID_PARAMETER;
2019                 goto end;
2020             }
2021 
2022             RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(uint32_t));
2023             fcb->sd_dirty = true;
2024             fcb->sd_deleted = false;
2025 
2026             RemoveEntryList(&item->list_entry);
2027             ExFreePool(item);
2028         } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) {
2029             if (item->value.Length < sizeof(uint32_t)) {
2030                 ERR("gid value was shorter than expected\n");
2031                 Status = STATUS_INVALID_PARAMETER;
2032                 goto end;
2033             }
2034 
2035             RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t));
2036 
2037             RemoveEntryList(&item->list_entry);
2038             ExFreePool(item);
2039         } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
2040             uint32_t allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID;
2041             uint32_t val;
2042 
2043             if (item->value.Length < sizeof(uint32_t)) {
2044                 ERR("mode value was shorter than expected\n");
2045                 Status = STATUS_INVALID_PARAMETER;
2046                 goto end;
2047             }
2048 
2049             RtlCopyMemory(&val, item->value.Buffer, sizeof(uint32_t));
2050 
2051             if (fcb->type != BTRFS_TYPE_DIRECTORY)
2052                 allowed |= __S_IFIFO | __S_IFCHR | __S_IFBLK | __S_IFSOCK;
2053 
2054             fcb->inode_item.st_mode &= ~allowed;
2055             fcb->inode_item.st_mode |= val & allowed;
2056 
2057             if (fcb->type != BTRFS_TYPE_DIRECTORY) {
2058                 if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR)
2059                     fcb->type = BTRFS_TYPE_CHARDEV;
2060                 else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK)
2061                     fcb->type = BTRFS_TYPE_BLOCKDEV;
2062                 else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO)
2063                     fcb->type = BTRFS_TYPE_FIFO;
2064                 else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK)
2065                     fcb->type = BTRFS_TYPE_SOCKET;
2066             }
2067 
2068             RemoveEntryList(&item->list_entry);
2069             ExFreePool(item);
2070         } else if (item->name.Length == sizeof(lxdev) - 1 && RtlCompareMemory(item->name.Buffer, lxdev, item->name.Length) == item->name.Length) {
2071             uint32_t major, minor;
2072 
2073             if (item->value.Length < sizeof(uint64_t)) {
2074                 ERR("dev value was shorter than expected\n");
2075                 Status = STATUS_INVALID_PARAMETER;
2076                 goto end;
2077             }
2078 
2079             major = *(uint32_t*)item->value.Buffer;
2080             minor = *(uint32_t*)&item->value.Buffer[sizeof(uint32_t)];
2081 
2082             fcb->inode_item.st_rdev = (minor & 0xFFFFF) | ((major & 0xFFFFFFFFFFF) << 20);
2083 
2084             RemoveEntryList(&item->list_entry);
2085             ExFreePool(item);
2086         }
2087 
2088         le = le2;
2089     }
2090 
2091     if (fcb->type != BTRFS_TYPE_CHARDEV && fcb->type != BTRFS_TYPE_BLOCKDEV)
2092         fcb->inode_item.st_rdev = 0;
2093 
2094     if (IsListEmpty(&ealist))
2095         return STATUS_SUCCESS;
2096 
2097     le = ealist.Flink;
2098     while (le != &ealist) {
2099         ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
2100 
2101         if (size % 4 > 0)
2102             size += 4 - (size % 4);
2103 
2104         size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
2105 
2106         le = le->Flink;
2107     }
2108 
2109     buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
2110     if (!buf) {
2111         ERR("out of memory\n");
2112         Status = STATUS_INSUFFICIENT_RESOURCES;
2113         goto end;
2114     }
2115 
2116     fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
2117     fcb->ea_xattr.Buffer = buf;
2118 
2119     fcb->ealen = 4;
2120     ea = NULL;
2121 
2122     le = ealist.Flink;
2123     while (le != &ealist) {
2124         ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
2125 
2126         if (ea) {
2127             ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength;
2128 
2129             if (ea->NextEntryOffset % 4 > 0)
2130                 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
2131 
2132             ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset);
2133         } else
2134             ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
2135 
2136         ea->NextEntryOffset = 0;
2137         ea->Flags = item->flags;
2138         ea->EaNameLength = (UCHAR)item->name.Length;
2139         ea->EaValueLength = item->value.Length;
2140 
2141         RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
2142         ea->EaName[item->name.Length] = 0;
2143         RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);
2144 
2145         fcb->ealen += 5 + item->name.Length + item->value.Length;
2146 
2147         le = le->Flink;
2148     }
2149 
2150     fcb->ea_changed = true;
2151 
2152     Status = STATUS_SUCCESS;
2153 
2154 end:
2155     while (!IsListEmpty(&ealist)) {
2156         ea_item* item = CONTAINING_RECORD(RemoveHeadList(&ealist), ea_item, list_entry);
2157 
2158         ExFreePool(item);
2159     }
2160 
2161     return Status;
2162 }
2163 
2164 static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _In_ PUNICODE_STRING fpus,
2165                              _In_ file_ref* parfileref, _In_ ULONG options, _In_reads_bytes_opt_(ealen) FILE_FULL_EA_INFORMATION* ea, _In_ ULONG ealen,
2166                              _Out_ file_ref** pfr, bool case_sensitive, _In_ LIST_ENTRY* rollback) {
2167     NTSTATUS Status;
2168     fcb* fcb;
2169     ULONG utf8len;
2170     char* utf8 = NULL;
2171     uint64_t inode;
2172     uint8_t type;
2173     LARGE_INTEGER time;
2174     BTRFS_TIME now;
2175     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2176     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
2177     USHORT defda;
2178     file_ref* fileref;
2179     dir_child* dc;
2180     ANSI_STRING utf8as;
2181     LIST_ENTRY* lastle = NULL;
2182     file_ref* existing_fileref = NULL;
2183 #ifdef DEBUG_FCB_REFCOUNTS
2184     LONG rc;
2185 #endif
2186 
2187     if (parfileref->fcb == Vcb->dummy_fcb)
2188         return STATUS_ACCESS_DENIED;
2189 
2190     if (options & FILE_DIRECTORY_FILE && IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_TEMPORARY)
2191         return STATUS_INVALID_PARAMETER;
2192 
2193     Status = utf16_to_utf8(NULL, 0, &utf8len, fpus->Buffer, fpus->Length);
2194     if (!NT_SUCCESS(Status)) {
2195         ERR("utf16_to_utf8 returned %08lx\n", Status);
2196         return Status;
2197     }
2198 
2199     utf8 = ExAllocatePoolWithTag(pool_type, utf8len + 1, ALLOC_TAG);
2200     if (!utf8) {
2201         ERR("out of memory\n");
2202         return STATUS_INSUFFICIENT_RESOURCES;
2203     }
2204 
2205     Status = utf16_to_utf8(utf8, utf8len, &utf8len, fpus->Buffer, fpus->Length);
2206     if (!NT_SUCCESS(Status)) {
2207         ERR("utf16_to_utf8 returned %08lx\n", Status);
2208         ExFreePool(utf8);
2209         return Status;
2210     }
2211 
2212     utf8[utf8len] = 0;
2213 
2214     KeQuerySystemTime(&time);
2215     win_time_to_unix(time, &now);
2216 
2217     TRACE("create file %.*S\n", (int)(fpus->Length / sizeof(WCHAR)), fpus->Buffer);
2218     ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2219     TRACE("parfileref->fcb->inode_item.st_size (inode %I64x) was %I64x\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
2220     parfileref->fcb->inode_item.st_size += utf8len * 2;
2221     TRACE("parfileref->fcb->inode_item.st_size (inode %I64x) now %I64x\n", parfileref->fcb->inode, parfileref->fcb->inode_item.st_size);
2222     parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
2223     parfileref->fcb->inode_item.sequence++;
2224     parfileref->fcb->inode_item.st_ctime = now;
2225     parfileref->fcb->inode_item.st_mtime = now;
2226     ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2227 
2228     parfileref->fcb->inode_item_changed = true;
2229     mark_fcb_dirty(parfileref->fcb);
2230 
2231     inode = InterlockedIncrement64(&parfileref->fcb->subvol->lastinode);
2232 
2233     type = options & FILE_DIRECTORY_FILE ? BTRFS_TYPE_DIRECTORY : BTRFS_TYPE_FILE;
2234 
2235     // FIXME - link FILE_ATTRIBUTE_READONLY to st_mode
2236 
2237     TRACE("requested attributes = %x\n", IrpSp->Parameters.Create.FileAttributes);
2238 
2239     defda = 0;
2240 
2241     if (utf8[0] == '.')
2242         defda |= FILE_ATTRIBUTE_HIDDEN;
2243 
2244     if (options & FILE_DIRECTORY_FILE) {
2245         defda |= FILE_ATTRIBUTE_DIRECTORY;
2246         IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2247     } else
2248         IrpSp->Parameters.Create.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
2249 
2250     if (!(IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
2251         IrpSp->Parameters.Create.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
2252         defda |= FILE_ATTRIBUTE_ARCHIVE;
2253     }
2254 
2255     TRACE("defda = %x\n", defda);
2256 
2257     if (IrpSp->Parameters.Create.FileAttributes == FILE_ATTRIBUTE_NORMAL)
2258         IrpSp->Parameters.Create.FileAttributes = defda;
2259 
2260     fcb = create_fcb(Vcb, pool_type);
2261     if (!fcb) {
2262         ERR("out of memory\n");
2263         ExFreePool(utf8);
2264 
2265         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2266         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2267         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2268 
2269         return STATUS_INSUFFICIENT_RESOURCES;
2270     }
2271 
2272     fcb->Vcb = Vcb;
2273 
2274     if (IrpSp->Flags & SL_OPEN_PAGING_FILE)
2275         fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
2276 
2277     fcb->inode_item.generation = Vcb->superblock.generation;
2278     fcb->inode_item.transid = Vcb->superblock.generation;
2279     fcb->inode_item.st_size = 0;
2280     fcb->inode_item.st_blocks = 0;
2281     fcb->inode_item.block_group = 0;
2282     fcb->inode_item.st_nlink = 1;
2283     fcb->inode_item.st_gid = GID_NOBODY; // FIXME?
2284     fcb->inode_item.st_mode = inherit_mode(parfileref->fcb, type == BTRFS_TYPE_DIRECTORY); // use parent's permissions by default
2285     fcb->inode_item.st_rdev = 0;
2286     fcb->inode_item.flags = 0;
2287     fcb->inode_item.sequence = 1;
2288     fcb->inode_item.st_atime = now;
2289     fcb->inode_item.st_ctime = now;
2290     fcb->inode_item.st_mtime = now;
2291     fcb->inode_item.otime = now;
2292 
2293     if (type == BTRFS_TYPE_DIRECTORY)
2294         fcb->inode_item.st_mode |= S_IFDIR;
2295     else {
2296         fcb->inode_item.st_mode |= S_IFREG;
2297         fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
2298     }
2299 
2300     if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
2301         fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS;
2302     } else {
2303         // inherit nodatacow flag from parent directory
2304         if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
2305             fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
2306 
2307             if (type != BTRFS_TYPE_DIRECTORY)
2308                 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
2309         }
2310 
2311         if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
2312             fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
2313     }
2314 
2315     fcb->prop_compression = parfileref->fcb->prop_compression;
2316     fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
2317 
2318     fcb->inode_item_changed = true;
2319 
2320     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2321     fcb->Header.AllocationSize.QuadPart = 0;
2322     fcb->Header.FileSize.QuadPart = 0;
2323     fcb->Header.ValidDataLength.QuadPart = 0;
2324 
2325     fcb->atts = IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
2326     fcb->atts_changed = fcb->atts != defda;
2327 
2328 #ifdef DEBUG_FCB_REFCOUNTS
2329     rc = InterlockedIncrement(&parfileref->fcb->refcount);
2330     WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
2331 #else
2332     InterlockedIncrement(&parfileref->fcb->refcount);
2333 #endif
2334     fcb->subvol = parfileref->fcb->subvol;
2335     fcb->inode = inode;
2336     fcb->type = type;
2337     fcb->created = true;
2338     fcb->deleted = true;
2339 
2340     fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&inode, sizeof(uint64_t));
2341 
2342     acquire_fcb_lock_exclusive(Vcb);
2343 
2344     if (fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
2345         LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
2346 
2347         while (le != &fcb->subvol->fcbs) {
2348             struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
2349 
2350             if (fcb2->hash > fcb->hash) {
2351                 lastle = le->Blink;
2352                 break;
2353             }
2354 
2355             le = le->Flink;
2356         }
2357     }
2358 
2359     if (!lastle) {
2360         uint8_t c = fcb->hash >> 24;
2361 
2362         if (c != 0xff) {
2363             uint8_t d = c + 1;
2364 
2365             do {
2366                 if (fcb->subvol->fcbs_ptrs[d]) {
2367                     lastle = fcb->subvol->fcbs_ptrs[d]->Blink;
2368                     break;
2369                 }
2370 
2371                 d++;
2372             } while (d != 0);
2373         }
2374     }
2375 
2376     if (lastle) {
2377         InsertHeadList(lastle, &fcb->list_entry);
2378 
2379         if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
2380             fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
2381     } else {
2382         InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
2383 
2384         if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
2385             fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
2386     }
2387 
2388     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2389 
2390     fcb->subvol->fcbs_version++;
2391 
2392     release_fcb_lock(Vcb);
2393 
2394     mark_fcb_dirty(fcb);
2395 
2396     Status = fcb_get_new_sd(fcb, parfileref, IrpSp->Parameters.Create.SecurityContext->AccessState);
2397 
2398     if (!NT_SUCCESS(Status)) {
2399         ERR("fcb_get_new_sd returned %08lx\n", Status);
2400         free_fcb(fcb);
2401 
2402         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2403         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2404         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2405 
2406         ExFreePool(utf8);
2407 
2408         return Status;
2409     }
2410 
2411     fcb->sd_dirty = true;
2412 
2413     if (ea && ealen > 0) {
2414         Status = file_create_parse_ea(fcb, ea);
2415         if (!NT_SUCCESS(Status)) {
2416             ERR("file_create_parse_ea returned %08lx\n", Status);
2417             free_fcb(fcb);
2418 
2419             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2420             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2421             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2422 
2423             ExFreePool(utf8);
2424 
2425             return Status;
2426         }
2427     }
2428 
2429     fileref = create_fileref(Vcb);
2430     if (!fileref) {
2431         ERR("out of memory\n");
2432         free_fcb(fcb);
2433 
2434         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2435         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2436         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2437 
2438         ExFreePool(utf8);
2439 
2440         return STATUS_INSUFFICIENT_RESOURCES;
2441     }
2442 
2443     fileref->fcb = fcb;
2444 
2445     if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb)) {
2446         Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
2447 
2448         if (!NT_SUCCESS(Status)) {
2449             ERR("extend_file returned %08lx\n", Status);
2450             reap_fileref(Vcb, fileref);
2451 
2452             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2453             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2454             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2455 
2456             ExFreePool(utf8);
2457 
2458             return Status;
2459         }
2460     }
2461 
2462     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2463         fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
2464         if (!fcb->hash_ptrs) {
2465             ERR("out of memory\n");
2466             reap_fileref(Vcb, fileref);
2467 
2468             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2469             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2470             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2471 
2472             ExFreePool(utf8);
2473 
2474             return STATUS_INSUFFICIENT_RESOURCES;
2475         }
2476 
2477         RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
2478 
2479         fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
2480         if (!fcb->hash_ptrs_uc) {
2481             ERR("out of memory\n");
2482             reap_fileref(Vcb, fileref);
2483 
2484             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2485             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2486             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2487 
2488             ExFreePool(utf8);
2489 
2490             return STATUS_INSUFFICIENT_RESOURCES;
2491         }
2492 
2493         RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
2494     }
2495 
2496     fcb->deleted = false;
2497 
2498     fileref->created = true;
2499 
2500     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2501     fcb->subvol->root_item.ctime = now;
2502 
2503     utf8as.Buffer = utf8;
2504     utf8as.Length = utf8as.MaximumLength = (uint16_t)utf8len;
2505 
2506     ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
2507 
2508     // check again doesn't already exist
2509     if (case_sensitive) {
2510         uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpus->Buffer, fpus->Length);
2511 
2512         if (parfileref->fcb->hash_ptrs[dc_hash >> 24]) {
2513             LIST_ENTRY* le = parfileref->fcb->hash_ptrs[dc_hash >> 24];
2514             while (le != &parfileref->fcb->dir_children_hash) {
2515                 dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
2516 
2517                 if (dc->hash == dc_hash && dc->name.Length == fpus->Length && RtlCompareMemory(dc->name.Buffer, fpus->Buffer, fpus->Length) == fpus->Length) {
2518                     existing_fileref = dc->fileref;
2519                     break;
2520                 } else if (dc->hash > dc_hash)
2521                     break;
2522 
2523                 le = le->Flink;
2524             }
2525         }
2526     } else {
2527         UNICODE_STRING fpusuc;
2528 #ifdef __REACTOS__
2529         UINT32 dc_hash;
2530 #endif
2531 
2532         Status = RtlUpcaseUnicodeString(&fpusuc, fpus, true);
2533         if (!NT_SUCCESS(Status)) {
2534             ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2535             ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2536             reap_fileref(Vcb, fileref);
2537 
2538             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2539             parfileref->fcb->inode_item.st_size -= utf8len * 2;
2540             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2541 
2542             ExFreePool(utf8);
2543 
2544             return Status;
2545         }
2546 
2547 #ifndef __REACTOS__
2548         uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpusuc.Buffer, fpusuc.Length);
2549 #else
2550         dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpusuc.Buffer, fpusuc.Length);
2551 #endif // __REACTOS__
2552 
2553         if (parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]) {
2554             LIST_ENTRY* le = parfileref->fcb->hash_ptrs_uc[dc_hash >> 24];
2555             while (le != &parfileref->fcb->dir_children_hash_uc) {
2556                 dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
2557 
2558                 if (dc->hash_uc == dc_hash && dc->name.Length == fpusuc.Length && RtlCompareMemory(dc->name.Buffer, fpusuc.Buffer, fpusuc.Length) == fpusuc.Length) {
2559                     existing_fileref = dc->fileref;
2560                     break;
2561                 } else if (dc->hash_uc > dc_hash)
2562                     break;
2563 
2564                 le = le->Flink;
2565             }
2566         }
2567 
2568         ExFreePool(fpusuc.Buffer);
2569     }
2570 
2571     if (existing_fileref) {
2572         ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2573         reap_fileref(Vcb, fileref);
2574 
2575         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2576         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2577         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2578 
2579         ExFreePool(utf8);
2580 
2581         increase_fileref_refcount(existing_fileref);
2582         *pfr = existing_fileref;
2583 
2584         return STATUS_OBJECT_NAME_COLLISION;
2585     }
2586 
2587     Status = add_dir_child(parfileref->fcb, fcb->inode, false, &utf8as, fpus, fcb->type, &dc);
2588     if (!NT_SUCCESS(Status)) {
2589         ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2590         ERR("add_dir_child returned %08lx\n", Status);
2591         reap_fileref(Vcb, fileref);
2592 
2593         ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2594         parfileref->fcb->inode_item.st_size -= utf8len * 2;
2595         ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2596 
2597         ExFreePool(utf8);
2598 
2599         return Status;
2600     }
2601 
2602     fileref->parent = parfileref;
2603     fileref->dc = dc;
2604     dc->fileref = fileref;
2605 
2606     if (type == BTRFS_TYPE_DIRECTORY)
2607         fileref->fcb->fileref = fileref;
2608 
2609     InsertTailList(&parfileref->children, &fileref->list_entry);
2610     ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2611 
2612     ExFreePool(utf8);
2613 
2614     mark_fileref_dirty(fileref);
2615     increase_fileref_refcount(parfileref);
2616 
2617     *pfr = fileref;
2618 
2619     TRACE("created new file in subvol %I64x, inode %I64x\n", fcb->subvol->id, fcb->inode);
2620 
2621     return STATUS_SUCCESS;
2622 }
2623 
2624 static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
2625                               file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream, PIRP Irp,
2626                               ULONG options, POOL_TYPE pool_type, bool case_sensitive, LIST_ENTRY* rollback) {
2627     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2628     file_ref *fileref, *newpar, *parfileref;
2629     fcb* fcb;
2630     static const char xapref[] = "user.";
2631     static const WCHAR DOSATTRIB[] = L"DOSATTRIB";
2632     static const WCHAR EA[] = L"EA";
2633     static const WCHAR reparse[] = L"reparse";
2634     static const WCHAR casesensitive_str[] = L"casesensitive";
2635     LARGE_INTEGER time;
2636     BTRFS_TIME now;
2637     ULONG utf8len, overhead;
2638     NTSTATUS Status;
2639     KEY searchkey;
2640     traverse_ptr tp;
2641     dir_child* dc;
2642     dir_child* existing_dc = NULL;
2643     ACCESS_MASK granted_access;
2644 #ifdef DEBUG_FCB_REFCOUNTS
2645     LONG rc;
2646 #endif
2647 #ifdef __REACTOS__
2648     LIST_ENTRY* le;
2649 #endif
2650 
2651     TRACE("fpus = %.*S\n", (int)(fpus->Length / sizeof(WCHAR)), fpus->Buffer);
2652     TRACE("stream = %.*S\n", (int)(stream->Length / sizeof(WCHAR)), stream->Buffer);
2653 
2654     parfileref = *pparfileref;
2655 
2656     if (parfileref->fcb == Vcb->dummy_fcb)
2657         return STATUS_ACCESS_DENIED;
2658 
2659     Status = open_fileref(Vcb, &newpar, fpus, parfileref, false, NULL, NULL, PagedPool, case_sensitive, Irp);
2660 
2661     if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
2662         UNICODE_STRING fpus2;
2663 
2664         if (!is_file_name_valid(fpus, false, true))
2665             return STATUS_OBJECT_NAME_INVALID;
2666 
2667         fpus2.Length = fpus2.MaximumLength = fpus->Length;
2668         fpus2.Buffer = ExAllocatePoolWithTag(pool_type, fpus2.MaximumLength, ALLOC_TAG);
2669 
2670         if (!fpus2.Buffer) {
2671             ERR("out of memory\n");
2672             return STATUS_INSUFFICIENT_RESOURCES;
2673         }
2674 
2675         RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
2676 
2677         SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2678 
2679         if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
2680                            true, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
2681                            IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
2682                            &granted_access, &Status)) {
2683             SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2684             return Status;
2685         }
2686 
2687         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2688 
2689         Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, case_sensitive, rollback);
2690 
2691         if (!NT_SUCCESS(Status)) {
2692             ERR("file_create2 returned %08lx\n", Status);
2693             ExFreePool(fpus2.Buffer);
2694             return Status;
2695         } else if (Status != STATUS_OBJECT_NAME_COLLISION) {
2696             send_notification_fileref(newpar, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
2697             queue_notification_fcb(newpar->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2698         }
2699 
2700         ExFreePool(fpus2.Buffer);
2701     } else if (!NT_SUCCESS(Status)) {
2702         ERR("open_fileref returned %08lx\n", Status);
2703         return Status;
2704     }
2705 
2706     parfileref = newpar;
2707     *pparfileref = parfileref;
2708 
2709     if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK && parfileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
2710         WARN("parent not file, directory, or symlink\n");
2711         free_fileref(parfileref);
2712         return STATUS_INVALID_PARAMETER;
2713     }
2714 
2715     if (options & FILE_DIRECTORY_FILE) {
2716         WARN("tried to create directory as stream\n");
2717         free_fileref(parfileref);
2718         return STATUS_INVALID_PARAMETER;
2719     }
2720 
2721     if (parfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) {
2722         free_fileref(parfileref);
2723         return STATUS_ACCESS_DENIED;
2724     }
2725 
2726     SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2727 
2728     if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
2729                        true, FILE_WRITE_DATA, 0, NULL, IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
2730                        &granted_access, &Status)) {
2731         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2732         free_fileref(parfileref);
2733         return Status;
2734     }
2735 
2736     SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2737 
2738     if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
2739         (stream->Length == sizeof(EA) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
2740         (stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length) ||
2741         (stream->Length == sizeof(casesensitive_str) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, casesensitive_str, stream->Length) == stream->Length)) {
2742         free_fileref(parfileref);
2743         return STATUS_OBJECT_NAME_INVALID;
2744     }
2745 
2746     fcb = create_fcb(Vcb, pool_type);
2747     if (!fcb) {
2748         ERR("out of memory\n");
2749         free_fileref(parfileref);
2750         return STATUS_INSUFFICIENT_RESOURCES;
2751     }
2752 
2753     fcb->Vcb = Vcb;
2754 
2755     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2756     fcb->Header.AllocationSize.QuadPart = 0;
2757     fcb->Header.FileSize.QuadPart = 0;
2758     fcb->Header.ValidDataLength.QuadPart = 0;
2759 
2760 #ifdef DEBUG_FCB_REFCOUNTS
2761     rc = InterlockedIncrement(&parfileref->fcb->refcount);
2762     WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
2763 #else
2764     InterlockedIncrement(&parfileref->fcb->refcount);
2765 #endif
2766     fcb->subvol = parfileref->fcb->subvol;
2767     fcb->inode = parfileref->fcb->inode;
2768     fcb->type = parfileref->fcb->type;
2769 
2770     fcb->ads = true;
2771 
2772     Status = utf16_to_utf8(NULL, 0, &utf8len, stream->Buffer, stream->Length);
2773     if (!NT_SUCCESS(Status)) {
2774         ERR("utf16_to_utf8 1 returned %08lx\n", Status);
2775         reap_fcb(fcb);
2776         free_fileref(parfileref);
2777         return Status;
2778     }
2779 
2780     fcb->adsxattr.Length = (uint16_t)utf8len + sizeof(xapref) - 1;
2781     fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
2782     fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type, fcb->adsxattr.MaximumLength, ALLOC_TAG);
2783     if (!fcb->adsxattr.Buffer) {
2784         ERR("out of memory\n");
2785         reap_fcb(fcb);
2786         free_fileref(parfileref);
2787         return STATUS_INSUFFICIENT_RESOURCES;
2788     }
2789 
2790     RtlCopyMemory(fcb->adsxattr.Buffer, xapref, sizeof(xapref) - 1);
2791 
2792     Status = utf16_to_utf8(&fcb->adsxattr.Buffer[sizeof(xapref) - 1], utf8len, &utf8len, stream->Buffer, stream->Length);
2793     if (!NT_SUCCESS(Status)) {
2794         ERR("utf16_to_utf8 2 returned %08lx\n", Status);
2795         reap_fcb(fcb);
2796         free_fileref(parfileref);
2797         return Status;
2798     }
2799 
2800     fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0;
2801 
2802     TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
2803 
2804     fcb->adshash = calc_crc32c(0xfffffffe, (uint8_t*)fcb->adsxattr.Buffer, fcb->adsxattr.Length);
2805     TRACE("adshash = %08x\n", fcb->adshash);
2806 
2807     searchkey.obj_id = parfileref->fcb->inode;
2808     searchkey.obj_type = TYPE_XATTR_ITEM;
2809     searchkey.offset = fcb->adshash;
2810 
2811     Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, false, Irp);
2812     if (!NT_SUCCESS(Status)) {
2813         ERR("find_item returned %08lx\n", Status);
2814         reap_fcb(fcb);
2815         free_fileref(parfileref);
2816         return Status;
2817     }
2818 
2819     if (!keycmp(tp.item->key, searchkey))
2820         overhead = tp.item->size;
2821     else
2822         overhead = 0;
2823 
2824     fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
2825 
2826     if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) {
2827         WARN("not enough room for new DIR_ITEM (%Iu + %lu > %lu)", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
2828         reap_fcb(fcb);
2829         free_fileref(parfileref);
2830         return STATUS_DISK_FULL;
2831     } else
2832         fcb->adsmaxlen -= overhead + utf8len + sizeof(xapref) - 1;
2833 
2834     fcb->created = true;
2835     fcb->deleted = true;
2836 
2837     acquire_fcb_lock_exclusive(Vcb);
2838     InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
2839     InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2840     parfileref->fcb->subvol->fcbs_version++;
2841     release_fcb_lock(Vcb);
2842 
2843     mark_fcb_dirty(fcb);
2844 
2845     fileref = create_fileref(Vcb);
2846     if (!fileref) {
2847         ERR("out of memory\n");
2848         free_fcb(fcb);
2849         free_fileref(parfileref);
2850         return STATUS_INSUFFICIENT_RESOURCES;
2851     }
2852 
2853     fileref->fcb = fcb;
2854 
2855     dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG);
2856     if (!dc) {
2857         ERR("out of memory\n");
2858         reap_fileref(Vcb, fileref);
2859         free_fileref(parfileref);
2860         return STATUS_INSUFFICIENT_RESOURCES;
2861     }
2862 
2863     RtlZeroMemory(dc, sizeof(dir_child));
2864 
2865     dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length + 1 - sizeof(xapref);
2866     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
2867     if (!dc->utf8.Buffer) {
2868         ERR("out of memory\n");
2869         ExFreePool(dc);
2870         reap_fileref(Vcb, fileref);
2871         free_fileref(parfileref);
2872         return STATUS_INSUFFICIENT_RESOURCES;
2873     }
2874 
2875     RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[sizeof(xapref) - 1], fcb->adsxattr.Length + 1 - sizeof(xapref));
2876 
2877     dc->name.MaximumLength = dc->name.Length = stream->Length;
2878     dc->name.Buffer = ExAllocatePoolWithTag(pool_type, dc->name.MaximumLength, ALLOC_TAG);
2879     if (!dc->name.Buffer) {
2880         ERR("out of memory\n");
2881         ExFreePool(dc->utf8.Buffer);
2882         ExFreePool(dc);
2883         reap_fileref(Vcb, fileref);
2884         free_fileref(parfileref);
2885         return STATUS_INSUFFICIENT_RESOURCES;
2886     }
2887 
2888     RtlCopyMemory(dc->name.Buffer, stream->Buffer, stream->Length);
2889 
2890     Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
2891     if (!NT_SUCCESS(Status)) {
2892         ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2893         ExFreePool(dc->utf8.Buffer);
2894         ExFreePool(dc->name.Buffer);
2895         ExFreePool(dc);
2896         reap_fileref(Vcb, fileref);
2897         free_fileref(parfileref);
2898         return Status;
2899     }
2900 
2901     KeQuerySystemTime(&time);
2902     win_time_to_unix(time, &now);
2903 
2904     ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
2905 
2906 #ifndef __REACTOS__
2907     LIST_ENTRY* le = parfileref->fcb->dir_children_index.Flink;
2908 #else
2909     le = parfileref->fcb->dir_children_index.Flink;
2910 #endif
2911     while (le != &parfileref->fcb->dir_children_index) {
2912         dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
2913 
2914         if (dc2->index == 0) {
2915             if ((case_sensitive && dc2->name.Length == dc->name.Length && RtlCompareMemory(dc2->name.Buffer, dc->name.Buffer, dc2->name.Length) == dc2->name.Length) ||
2916                 (!case_sensitive && dc2->name_uc.Length == dc->name_uc.Length && RtlCompareMemory(dc2->name_uc.Buffer, dc->name_uc.Buffer, dc2->name_uc.Length) == dc2->name_uc.Length)
2917             ) {
2918                 existing_dc = dc2;
2919                 break;
2920             }
2921         } else
2922             break;
2923 
2924         le = le->Flink;
2925     }
2926 
2927     if (existing_dc) {
2928         ExFreePool(dc->utf8.Buffer);
2929         ExFreePool(dc->name.Buffer);
2930         ExFreePool(dc);
2931         reap_fileref(Vcb, fileref);
2932         free_fileref(parfileref);
2933 
2934         increase_fileref_refcount(existing_dc->fileref);
2935         *pfileref = existing_dc->fileref;
2936 
2937         return STATUS_OBJECT_NAME_COLLISION;
2938     }
2939 
2940     dc->fileref = fileref;
2941     fileref->dc = dc;
2942     fileref->parent = (struct _file_ref*)parfileref;
2943     fcb->deleted = false;
2944 
2945     InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
2946 
2947     InsertTailList(&parfileref->children, &fileref->list_entry);
2948 
2949     ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2950 
2951     mark_fileref_dirty(fileref);
2952 
2953     parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
2954     parfileref->fcb->inode_item.sequence++;
2955     parfileref->fcb->inode_item.st_ctime = now;
2956     parfileref->fcb->inode_item_changed = true;
2957 
2958     mark_fcb_dirty(parfileref->fcb);
2959 
2960     parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2961     parfileref->fcb->subvol->root_item.ctime = now;
2962 
2963     increase_fileref_refcount(parfileref);
2964 
2965     *pfileref = fileref;
2966 
2967     send_notification_fileref(parfileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_ADDED_STREAM, &fileref->dc->name);
2968 
2969     return STATUS_SUCCESS;
2970 }
2971 
2972 // LXSS programs can be distinguished by the fact they have a NULL PEB.
2973 #ifdef _AMD64_
2974 static __inline bool called_from_lxss() {
2975     NTSTATUS Status;
2976     PROCESS_BASIC_INFORMATION pbi;
2977     ULONG retlen;
2978 
2979     Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
2980 
2981     if (!NT_SUCCESS(Status)) {
2982         ERR("ZwQueryInformationProcess returned %08lx\n", Status);
2983         return false;
2984     }
2985 
2986     return !pbi.PebBaseAddress;
2987 }
2988 #else
2989 #define called_from_lxss() false
2990 #endif
2991 
2992 static NTSTATUS file_create(PIRP Irp, _Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
2993                             PFILE_OBJECT FileObject, file_ref* related, bool loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options,
2994                             file_ref** existing_fileref, LIST_ENTRY* rollback) {
2995     NTSTATUS Status;
2996     file_ref *fileref, *parfileref = NULL;
2997     ULONG i, j;
2998     ccb* ccb;
2999     static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
3000     UNICODE_STRING dsus, fpus, stream;
3001     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3002     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
3003     ECP_LIST* ecp_list;
3004     ATOMIC_CREATE_ECP_CONTEXT* acec = NULL;
3005 #ifdef DEBUG_FCB_REFCOUNTS
3006     LONG oc;
3007 #endif
3008 
3009     TRACE("(%p, %p, %p, %.*S, %lx, %lx)\n", Irp, Vcb, FileObject, (int)(fnus->Length / sizeof(WCHAR)), fnus->Buffer, disposition, options);
3010 
3011     if (Vcb->readonly)
3012         return STATUS_MEDIA_WRITE_PROTECTED;
3013 
3014     if (options & FILE_DELETE_ON_CLOSE && IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_READONLY)
3015         return STATUS_CANNOT_DELETE;
3016 
3017     if (fFsRtlGetEcpListFromIrp && fFsRtlGetNextExtraCreateParameter) {
3018         if (NT_SUCCESS(fFsRtlGetEcpListFromIrp(Irp, &ecp_list)) && ecp_list) {
3019             void* ctx = NULL;
3020             GUID type;
3021             ULONG ctxsize;
3022 
3023             do {
3024                 Status = fFsRtlGetNextExtraCreateParameter(ecp_list, ctx, &type, &ctx, &ctxsize);
3025 
3026                 if (NT_SUCCESS(Status)) {
3027                     if (RtlCompareMemory(&type, &GUID_ECP_ATOMIC_CREATE, sizeof(GUID)) == sizeof(GUID)) {
3028                         if (ctxsize >= sizeof(ATOMIC_CREATE_ECP_CONTEXT))
3029                             acec = ctx;
3030                         else {
3031                             ERR("GUID_ECP_ATOMIC_CREATE context was too short: %lu bytes, expected %Iu\n", ctxsize,
3032                                 sizeof(ATOMIC_CREATE_ECP_CONTEXT));
3033                         }
3034                     } else if (RtlCompareMemory(&type, &GUID_ECP_QUERY_ON_CREATE, sizeof(GUID)) == sizeof(GUID))
3035                         WARN("unhandled ECP GUID_ECP_QUERY_ON_CREATE\n");
3036                     else if (RtlCompareMemory(&type, &GUID_ECP_CREATE_REDIRECTION, sizeof(GUID)) == sizeof(GUID))
3037                         WARN("unhandled ECP GUID_ECP_CREATE_REDIRECTION\n");
3038                     else {
3039                         WARN("unhandled ECP {%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", type.Data1, type.Data2,
3040                              type.Data3, type.Data4[0], type.Data4[1], type.Data4[2], type.Data4[3], type.Data4[4], type.Data4[5],
3041                              type.Data4[6], type.Data4[7]);
3042                     }
3043                 }
3044             } while (NT_SUCCESS(Status));
3045         }
3046     }
3047 
3048     dsus.Buffer = (WCHAR*)datasuf;
3049     dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
3050     fpus.Buffer = NULL;
3051 
3052     if (!loaded_related) {
3053         Status = open_fileref(Vcb, &parfileref, fnus, related, true, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
3054 
3055         if (!NT_SUCCESS(Status))
3056             goto end;
3057     } else
3058         parfileref = related;
3059 
3060     if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
3061         Status = STATUS_OBJECT_PATH_NOT_FOUND;
3062         goto end;
3063     }
3064 
3065     if (is_subvol_readonly(parfileref->fcb->subvol, Irp)) {
3066         Status = STATUS_ACCESS_DENIED;
3067         goto end;
3068     }
3069 
3070     i = (fnus->Length / sizeof(WCHAR))-1;
3071     while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
3072 
3073     j = i;
3074 
3075     while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
3076 
3077     fpus.MaximumLength = (USHORT)((j - i + 2) * sizeof(WCHAR));
3078     fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG);
3079     if (!fpus.Buffer) {
3080         ERR("out of memory\n");
3081         Status = STATUS_INSUFFICIENT_RESOURCES;
3082         goto end;
3083     }
3084 
3085     fpus.Length = (USHORT)((j - i + 1) * sizeof(WCHAR));
3086 
3087     RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
3088     fpus.Buffer[j - i + 1] = 0;
3089 
3090     if (fpus.Length > dsus.Length) { // check for :$DATA suffix
3091         UNICODE_STRING lb;
3092 
3093         lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
3094         lb.Length = lb.MaximumLength = dsus.Length;
3095 
3096         TRACE("lb = %.*S\n", (int)(lb.Length/sizeof(WCHAR)), lb.Buffer);
3097 
3098         if (FsRtlAreNamesEqual(&dsus, &lb, true, NULL)) {
3099             TRACE("ignoring :$DATA suffix\n");
3100 
3101             fpus.Length -= lb.Length;
3102 
3103             if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
3104                 fpus.Length -= sizeof(WCHAR);
3105 
3106             TRACE("fpus = %.*S\n", (int)(fpus.Length / sizeof(WCHAR)), fpus.Buffer);
3107         }
3108     }
3109 
3110     stream.Length = 0;
3111 
3112     for (i = 0; i < fpus.Length / sizeof(WCHAR); i++) {
3113         if (fpus.Buffer[i] == ':') {
3114             stream.Length = (USHORT)(fpus.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR));
3115             stream.Buffer = &fpus.Buffer[i+1];
3116             fpus.Buffer[i] = 0;
3117             fpus.Length = (USHORT)(i * sizeof(WCHAR));
3118             break;
3119         }
3120     }
3121 
3122     if (stream.Length > 0) {
3123         Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
3124         if (!NT_SUCCESS(Status)) {
3125             ERR("create_stream returned %08lx\n", Status);
3126             goto end;
3127         }
3128 
3129         IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess,
3130                          FileObject, &fileref->fcb->share_access);
3131     } else {
3132         ACCESS_MASK granted_access;
3133 
3134         if (!is_file_name_valid(&fpus, false, false)) {
3135             Status = STATUS_OBJECT_NAME_INVALID;
3136             goto end;
3137         }
3138 
3139         SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3140 
3141         if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
3142                            true, options & FILE_DIRECTORY_FILE ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL,
3143                            IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
3144                            &granted_access, &Status)) {
3145             SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3146             goto end;
3147         }
3148 
3149         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3150 
3151         if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
3152             ULONG offset;
3153 
3154             Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
3155             if (!NT_SUCCESS(Status)) {
3156                 ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
3157                 goto end;
3158             }
3159         }
3160 
3161         Status = file_create2(Irp, Vcb, &fpus, parfileref, options, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength,
3162                               &fileref, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
3163 
3164         if (Status == STATUS_OBJECT_NAME_COLLISION) {
3165             *existing_fileref = fileref;
3166             goto end;
3167         } else if (!NT_SUCCESS(Status)) {
3168             ERR("file_create2 returned %08lx\n", Status);
3169             goto end;
3170         }
3171 
3172         IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
3173 
3174         send_notification_fileref(fileref, options & FILE_DIRECTORY_FILE ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL);
3175         queue_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
3176     }
3177 
3178     FileObject->FsContext = fileref->fcb;
3179 
3180     ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
3181     if (!ccb) {
3182         ERR("out of memory\n");
3183         Status = STATUS_INSUFFICIENT_RESOURCES;
3184         fileref->deleted = true;
3185         fileref->fcb->deleted = true;
3186 
3187         if (stream.Length == 0) {
3188             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
3189             parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2;
3190             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
3191         }
3192 
3193         free_fileref(fileref);
3194         goto end;
3195     }
3196 
3197     RtlZeroMemory(ccb, sizeof(*ccb));
3198 
3199     ccb->fileref = fileref;
3200 
3201     ccb->NodeType = BTRFS_NODE_TYPE_CCB;
3202     ccb->NodeSize = sizeof(*ccb);
3203     ccb->disposition = disposition;
3204     ccb->options = options;
3205     ccb->query_dir_offset = 0;
3206     RtlInitUnicodeString(&ccb->query_string, NULL);
3207     ccb->has_wildcard = false;
3208     ccb->specific_file = false;
3209     ccb->access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
3210     ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
3211     ccb->reserving = false;
3212     ccb->lxss = called_from_lxss();
3213 
3214 #ifdef DEBUG_FCB_REFCOUNTS
3215     oc = InterlockedIncrement(&fileref->open_count);
3216     ERR("fileref %p: open_count now %i\n", fileref, oc);
3217 #else
3218     InterlockedIncrement(&fileref->open_count);
3219 #endif
3220     InterlockedIncrement(&Vcb->open_files);
3221 
3222     FileObject->FsContext2 = ccb;
3223 
3224     FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
3225 
3226     // FIXME - ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT
3227     if (acec && acec->InFlags & ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED) {
3228         if (acec->ReparseBufferLength > sizeof(uint32_t) && *(uint32_t*)acec->ReparseBuffer == IO_REPARSE_TAG_SYMLINK) {
3229             fileref->fcb->inode_item.st_mode &= ~(__S_IFIFO | __S_IFCHR | __S_IFBLK | __S_IFSOCK);
3230             fileref->fcb->type = BTRFS_TYPE_FILE;
3231         }
3232 
3233         if (fileref->fcb->type == BTRFS_TYPE_SOCKET || fileref->fcb->type == BTRFS_TYPE_FIFO ||
3234             fileref->fcb->type == BTRFS_TYPE_CHARDEV || fileref->fcb->type == BTRFS_TYPE_BLOCKDEV) {
3235             // NOP. If called from LXSS, humour it - we hardcode the values elsewhere.
3236         } else {
3237             Status = set_reparse_point2(fileref->fcb, acec->ReparseBuffer, acec->ReparseBufferLength, NULL, NULL, Irp, rollback);
3238             if (!NT_SUCCESS(Status)) {
3239                 ERR("set_reparse_point2 returned %08lx\n", Status);
3240                 fileref->deleted = true;
3241                 fileref->fcb->deleted = true;
3242 
3243                 if (stream.Length == 0) {
3244                     ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
3245                     parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2;
3246                     ExReleaseResourceLite(parfileref->fcb->Header.Resource);
3247                 }
3248 
3249                 free_fileref(fileref);
3250                 return Status;
3251             }
3252         }
3253 
3254         acec->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET;
3255     }
3256 
3257     if (acec && acec->InFlags & ATOMIC_CREATE_ECP_IN_FLAG_OP_FLAGS_SPECIFIED) {
3258         if (acec->InOpFlags & ATOMIC_CREATE_ECP_IN_OP_FLAG_CASE_SENSITIVE_FLAGS_SPECIFIED && fileref->fcb->atts & FILE_ATTRIBUTE_DIRECTORY) {
3259             if ((acec->InCaseSensitiveFlags & acec->CaseSensitiveFlagsMask) & FILE_CS_FLAG_CASE_SENSITIVE_DIR) {
3260                 acec->OutCaseSensitiveFlags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
3261                 fileref->fcb->case_sensitive = true;
3262                 ccb->case_sensitive = true;
3263             }
3264 
3265             acec->OutOpFlags |= ATOMIC_CREATE_ECP_OUT_OP_FLAG_CASE_SENSITIVE_FLAGS_SET;
3266         }
3267 
3268         acec->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_OP_FLAGS_HONORED;
3269     }
3270 
3271     fileref->dc->type = fileref->fcb->type;
3272 
3273 end:
3274     if (fpus.Buffer)
3275         ExFreePool(fpus.Buffer);
3276 
3277     if (parfileref && !loaded_related)
3278         free_fileref(parfileref);
3279 
3280     return Status;
3281 }
3282 
3283 static __inline void debug_create_options(ULONG RequestedOptions) {
3284     if (RequestedOptions != 0) {
3285         ULONG options = RequestedOptions;
3286 
3287         TRACE("requested options:\n");
3288 
3289         if (options & FILE_DIRECTORY_FILE) {
3290             TRACE("    FILE_DIRECTORY_FILE\n");
3291             options &= ~FILE_DIRECTORY_FILE;
3292         }
3293 
3294         if (options & FILE_WRITE_THROUGH) {
3295             TRACE("    FILE_WRITE_THROUGH\n");
3296             options &= ~FILE_WRITE_THROUGH;
3297         }
3298 
3299         if (options & FILE_SEQUENTIAL_ONLY) {
3300             TRACE("    FILE_SEQUENTIAL_ONLY\n");
3301             options &= ~FILE_SEQUENTIAL_ONLY;
3302         }
3303 
3304         if (options & FILE_NO_INTERMEDIATE_BUFFERING) {
3305             TRACE("    FILE_NO_INTERMEDIATE_BUFFERING\n");
3306             options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
3307         }
3308 
3309         if (options & FILE_SYNCHRONOUS_IO_ALERT) {
3310             TRACE("    FILE_SYNCHRONOUS_IO_ALERT\n");
3311             options &= ~FILE_SYNCHRONOUS_IO_ALERT;
3312         }
3313 
3314         if (options & FILE_SYNCHRONOUS_IO_NONALERT) {
3315             TRACE("    FILE_SYNCHRONOUS_IO_NONALERT\n");
3316             options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
3317         }
3318 
3319         if (options & FILE_NON_DIRECTORY_FILE) {
3320             TRACE("    FILE_NON_DIRECTORY_FILE\n");
3321             options &= ~FILE_NON_DIRECTORY_FILE;
3322         }
3323 
3324         if (options & FILE_CREATE_TREE_CONNECTION) {
3325             TRACE("    FILE_CREATE_TREE_CONNECTION\n");
3326             options &= ~FILE_CREATE_TREE_CONNECTION;
3327         }
3328 
3329         if (options & FILE_COMPLETE_IF_OPLOCKED) {
3330             TRACE("    FILE_COMPLETE_IF_OPLOCKED\n");
3331             options &= ~FILE_COMPLETE_IF_OPLOCKED;
3332         }
3333 
3334         if (options & FILE_NO_EA_KNOWLEDGE) {
3335             TRACE("    FILE_NO_EA_KNOWLEDGE\n");
3336             options &= ~FILE_NO_EA_KNOWLEDGE;
3337         }
3338 
3339         if (options & FILE_OPEN_REMOTE_INSTANCE) {
3340             TRACE("    FILE_OPEN_REMOTE_INSTANCE\n");
3341             options &= ~FILE_OPEN_REMOTE_INSTANCE;
3342         }
3343 
3344         if (options & FILE_RANDOM_ACCESS) {
3345             TRACE("    FILE_RANDOM_ACCESS\n");
3346             options &= ~FILE_RANDOM_ACCESS;
3347         }
3348 
3349         if (options & FILE_DELETE_ON_CLOSE) {
3350             TRACE("    FILE_DELETE_ON_CLOSE\n");
3351             options &= ~FILE_DELETE_ON_CLOSE;
3352         }
3353 
3354         if (options & FILE_OPEN_BY_FILE_ID) {
3355             TRACE("    FILE_OPEN_BY_FILE_ID\n");
3356             options &= ~FILE_OPEN_BY_FILE_ID;
3357         }
3358 
3359         if (options & FILE_OPEN_FOR_BACKUP_INTENT) {
3360             TRACE("    FILE_OPEN_FOR_BACKUP_INTENT\n");
3361             options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
3362         }
3363 
3364         if (options & FILE_NO_COMPRESSION) {
3365             TRACE("    FILE_NO_COMPRESSION\n");
3366             options &= ~FILE_NO_COMPRESSION;
3367         }
3368 
3369 #if NTDDI_VERSION >= NTDDI_WIN7
3370         if (options & FILE_OPEN_REQUIRING_OPLOCK) {
3371             TRACE("    FILE_OPEN_REQUIRING_OPLOCK\n");
3372             options &= ~FILE_OPEN_REQUIRING_OPLOCK;
3373         }
3374 
3375         if (options & FILE_DISALLOW_EXCLUSIVE) {
3376             TRACE("    FILE_DISALLOW_EXCLUSIVE\n");
3377             options &= ~FILE_DISALLOW_EXCLUSIVE;
3378         }
3379 #endif
3380 
3381         if (options & FILE_RESERVE_OPFILTER) {
3382             TRACE("    FILE_RESERVE_OPFILTER\n");
3383             options &= ~FILE_RESERVE_OPFILTER;
3384         }
3385 
3386         if (options & FILE_OPEN_REPARSE_POINT) {
3387             TRACE("    FILE_OPEN_REPARSE_POINT\n");
3388             options &= ~FILE_OPEN_REPARSE_POINT;
3389         }
3390 
3391         if (options & FILE_OPEN_NO_RECALL) {
3392             TRACE("    FILE_OPEN_NO_RECALL\n");
3393             options &= ~FILE_OPEN_NO_RECALL;
3394         }
3395 
3396         if (options & FILE_OPEN_FOR_FREE_SPACE_QUERY) {
3397             TRACE("    FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
3398             options &= ~FILE_OPEN_FOR_FREE_SPACE_QUERY;
3399         }
3400 
3401         if (options)
3402             TRACE("    unknown options: %lx\n", options);
3403     } else {
3404         TRACE("requested options: (none)\n");
3405     }
3406 }
3407 
3408 static NTSTATUS get_reparse_block(fcb* fcb, uint8_t** data) {
3409     NTSTATUS Status;
3410 
3411     if (fcb->type == BTRFS_TYPE_FILE || fcb->type == BTRFS_TYPE_SYMLINK) {
3412         ULONG size, bytes_read, i;
3413 
3414         if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) {
3415             WARN("file was too short to be a reparse point\n");
3416             return STATUS_INVALID_PARAMETER;
3417         }
3418 
3419         // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
3420         size = (ULONG)min(0x10007, fcb->inode_item.st_size);
3421 
3422         if (size == 0)
3423             return STATUS_INVALID_PARAMETER;
3424 
3425         *data = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
3426         if (!*data) {
3427             ERR("out of memory\n");
3428             return STATUS_INSUFFICIENT_RESOURCES;
3429         }
3430 
3431         Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
3432         if (!NT_SUCCESS(Status)) {
3433             ERR("read_file_fcb returned %08lx\n", Status);
3434             ExFreePool(*data);
3435             return Status;
3436         }
3437 
3438         if (fcb->type == BTRFS_TYPE_SYMLINK) {
3439             ULONG stringlen, reqlen;
3440             uint16_t subnamelen, printnamelen;
3441             REPARSE_DATA_BUFFER* rdb;
3442 
3443             Status = utf8_to_utf16(NULL, 0, &stringlen, (char*)*data, bytes_read);
3444             if (!NT_SUCCESS(Status)) {
3445                 ERR("utf8_to_utf16 1 returned %08lx\n", Status);
3446                 ExFreePool(*data);
3447                 return Status;
3448             }
3449 
3450             subnamelen = printnamelen = (USHORT)stringlen;
3451 
3452             reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
3453 
3454             rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
3455 
3456             if (!rdb) {
3457                 ERR("out of memory\n");
3458                 ExFreePool(*data);
3459                 return STATUS_INSUFFICIENT_RESOURCES;
3460             }
3461 
3462             rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
3463             rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
3464             rdb->Reserved = 0;
3465 
3466             rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
3467             rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
3468             rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
3469             rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
3470             rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
3471 
3472             Status = utf8_to_utf16(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
3473                                     stringlen, &stringlen, (char*)*data, size);
3474 
3475             if (!NT_SUCCESS(Status)) {
3476                 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
3477                 ExFreePool(rdb);
3478                 ExFreePool(*data);
3479                 return Status;
3480             }
3481 
3482             for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
3483                 if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
3484                     rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
3485             }
3486 
3487             RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
3488                         &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
3489                         rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
3490 
3491             ExFreePool(*data);
3492 
3493             *data = (uint8_t*)rdb;
3494         } else {
3495             Status = fFsRtlValidateReparsePointBuffer(bytes_read, (REPARSE_DATA_BUFFER*)*data);
3496             if (!NT_SUCCESS(Status)) {
3497                 ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status);
3498                 ExFreePool(*data);
3499                 return Status;
3500             }
3501         }
3502     } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3503         if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length == 0)
3504             return STATUS_INTERNAL_ERROR;
3505 
3506         if (fcb->reparse_xattr.Length < sizeof(ULONG)) {
3507             WARN("xattr was too short to be a reparse point\n");
3508             return STATUS_INTERNAL_ERROR;
3509         }
3510 
3511         Status = fFsRtlValidateReparsePointBuffer(fcb->reparse_xattr.Length, (REPARSE_DATA_BUFFER*)fcb->reparse_xattr.Buffer);
3512         if (!NT_SUCCESS(Status)) {
3513             ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status);
3514             return Status;
3515         }
3516 
3517         *data = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.Length, ALLOC_TAG);
3518         if (!*data) {
3519             ERR("out of memory\n");
3520             return STATUS_INSUFFICIENT_RESOURCES;
3521         }
3522 
3523         RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
3524     } else
3525         return STATUS_INVALID_PARAMETER;
3526 
3527     return STATUS_SUCCESS;
3528 }
3529 
3530 static void fcb_load_csums(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, PIRP Irp) {
3531     LIST_ENTRY* le;
3532     NTSTATUS Status;
3533 
3534     if (fcb->csum_loaded)
3535         return;
3536 
3537     if (IsListEmpty(&fcb->extents) || fcb->inode_item.flags & BTRFS_INODE_NODATASUM)
3538         goto end;
3539 
3540     le = fcb->extents.Flink;
3541     while (le != &fcb->extents) {
3542         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3543 
3544         if (!ext->ignore && ext->extent_data.type == EXTENT_TYPE_REGULAR) {
3545             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
3546             uint64_t len;
3547 
3548             len = (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) / Vcb->superblock.sector_size;
3549 
3550             ext->csum = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(len * Vcb->csum_size), ALLOC_TAG);
3551             if (!ext->csum) {
3552                 ERR("out of memory\n");
3553                 goto end;
3554             }
3555 
3556             Status = load_csum(Vcb, ext->csum, ed2->address + (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
3557 
3558             if (!NT_SUCCESS(Status)) {
3559                 ERR("load_csum returned %08lx\n", Status);
3560                 goto end;
3561             }
3562         }
3563 
3564         le = le->Flink;
3565     }
3566 
3567 end:
3568     fcb->csum_loaded = true;
3569 }
3570 
3571 static NTSTATUS open_file2(device_extension* Vcb, ULONG RequestedDisposition, POOL_TYPE pool_type, file_ref* fileref, ACCESS_MASK* granted_access,
3572                            PFILE_OBJECT FileObject, UNICODE_STRING* fn, ULONG options, PIRP Irp, LIST_ENTRY* rollback) {
3573     NTSTATUS Status;
3574     file_ref* sf;
3575     bool readonly;
3576     ccb* ccb;
3577     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3578 
3579     if (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) {
3580         LARGE_INTEGER zero;
3581 
3582         if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY || is_subvol_readonly(fileref->fcb->subvol, Irp)) {
3583             free_fileref(fileref);
3584 
3585             return STATUS_ACCESS_DENIED;
3586         }
3587 
3588         if (Vcb->readonly) {
3589             free_fileref(fileref);
3590 
3591             return STATUS_MEDIA_WRITE_PROTECTED;
3592         }
3593 
3594         zero.QuadPart = 0;
3595         if (!MmCanFileBeTruncated(&fileref->fcb->nonpaged->segment_object, &zero)) {
3596             free_fileref(fileref);
3597 
3598             return STATUS_USER_MAPPED_FILE;
3599         }
3600     }
3601 
3602     if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess != 0) {
3603         SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3604 
3605         if (!SeAccessCheck((fileref->fcb->ads || fileref->fcb == Vcb->dummy_fcb) ? fileref->parent->fcb->sd : fileref->fcb->sd,
3606                             &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
3607                             true, IrpSp->Parameters.Create.SecurityContext->DesiredAccess, 0, NULL,
3608                             IoGetFileObjectGenericMapping(), IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode,
3609                             granted_access, &Status)) {
3610             SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3611             TRACE("SeAccessCheck failed, returning %08lx\n", Status);
3612 
3613             free_fileref(fileref);
3614 
3615             return Status;
3616         }
3617 
3618         SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3619     } else
3620         *granted_access = 0;
3621 
3622     TRACE("deleted = %s\n", fileref->deleted ? "true" : "false");
3623 
3624     sf = fileref;
3625     while (sf) {
3626         if (sf->delete_on_close) {
3627             TRACE("could not open as deletion pending\n");
3628 
3629             free_fileref(fileref);
3630 
3631             return STATUS_DELETE_PENDING;
3632         }
3633         sf = sf->parent;
3634     }
3635 
3636     readonly = (!fileref->fcb->ads && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) || (fileref->fcb->ads && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) ||
3637                 is_subvol_readonly(fileref->fcb->subvol, Irp) || fileref->fcb == Vcb->dummy_fcb || Vcb->readonly;
3638 
3639     if (options & FILE_DELETE_ON_CLOSE && (fileref == Vcb->root_fileref || readonly)) {
3640         free_fileref(fileref);
3641 
3642         return STATUS_CANNOT_DELETE;
3643     }
3644 
3645     if (readonly) {
3646         ACCESS_MASK allowed;
3647 
3648         allowed = READ_CONTROL | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
3649                     FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
3650                     FILE_TRAVERSE;
3651 
3652         if (!Vcb->readonly && (fileref->fcb == Vcb->dummy_fcb || fileref->fcb->inode == SUBVOL_ROOT_INODE))
3653             allowed |= DELETE;
3654 
3655         if (fileref->fcb != Vcb->dummy_fcb && !is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
3656             allowed |= DELETE | WRITE_OWNER | WRITE_DAC | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
3657 
3658             if (!fileref->fcb->ads && fileref->fcb->type == BTRFS_TYPE_DIRECTORY)
3659                 allowed |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
3660         } else if (fileref->fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fileref->fcb->subvol, Irp) && !Vcb->readonly) {
3661             // We allow a subvolume root to be opened read-write even if its readonly flag is set, so it can be cleared
3662 
3663             allowed |= FILE_WRITE_ATTRIBUTES;
3664         }
3665 
3666         if (IrpSp->Parameters.Create.SecurityContext->DesiredAccess & MAXIMUM_ALLOWED) {
3667             *granted_access &= allowed;
3668             IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &= allowed;
3669         } else if (*granted_access & ~allowed) {
3670             free_fileref(fileref);
3671 
3672             return Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_ACCESS_DENIED;
3673         }
3674     }
3675 
3676     if ((fileref->fcb->type == BTRFS_TYPE_SYMLINK || fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) && !(options & FILE_OPEN_REPARSE_POINT))  {
3677         REPARSE_DATA_BUFFER* data;
3678 
3679         /* How reparse points work from the point of view of the filesystem appears to
3680             * undocumented. When returning STATUS_REPARSE, MSDN encourages us to return
3681             * IO_REPARSE in Irp->IoStatus.Information, but that means we have to do our own
3682             * translation. If we instead return the reparse tag in Information, and store
3683             * a pointer to the reparse data buffer in Irp->Tail.Overlay.AuxiliaryBuffer,
3684             * IopSymlinkProcessReparse will do the translation for us. */
3685 
3686         Status = get_reparse_block(fileref->fcb, (uint8_t**)&data);
3687         if (!NT_SUCCESS(Status)) {
3688             ERR("get_reparse_block returned %08lx\n", Status);
3689             Status = STATUS_SUCCESS;
3690         } else {
3691             Irp->IoStatus.Information = data->ReparseTag;
3692 
3693             if (fn->Buffer[(fn->Length / sizeof(WCHAR)) - 1] == '\\')
3694                 data->Reserved = sizeof(WCHAR);
3695 
3696             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
3697 
3698             free_fileref(fileref);
3699 
3700             return STATUS_REPARSE;
3701         }
3702     }
3703 
3704     if (fileref->fcb->type == BTRFS_TYPE_DIRECTORY && !fileref->fcb->ads) {
3705         if (options & FILE_NON_DIRECTORY_FILE && !(fileref->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT)) {
3706             free_fileref(fileref);
3707 
3708             return STATUS_FILE_IS_A_DIRECTORY;
3709         }
3710     } else if (options & FILE_DIRECTORY_FILE) {
3711         TRACE("returning STATUS_NOT_A_DIRECTORY (type = %u)\n", fileref->fcb->type);
3712 
3713         free_fileref(fileref);
3714 
3715         return STATUS_NOT_A_DIRECTORY;
3716     }
3717 
3718     if (fileref->open_count > 0) {
3719         Status = IoCheckShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access, false);
3720 
3721         if (!NT_SUCCESS(Status)) {
3722             if (Status == STATUS_SHARING_VIOLATION)
3723                 TRACE("IoCheckShareAccess failed, returning %08lx\n", Status);
3724             else
3725                 WARN("IoCheckShareAccess failed, returning %08lx\n", Status);
3726 
3727             free_fileref(fileref);
3728 
3729             return Status;
3730         }
3731 
3732         IoUpdateShareAccess(FileObject, &fileref->fcb->share_access);
3733     } else
3734         IoSetShareAccess(*granted_access, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
3735 
3736     if (*granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
3737         if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite)) {
3738 
3739             IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3740 
3741             free_fileref(fileref);
3742 
3743             return (options & FILE_DELETE_ON_CLOSE) ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION;
3744         }
3745     }
3746 
3747     // FIXME - this can block waiting for network IO, while we're holding fileref_lock and tree_lock
3748     Status = FsRtlCheckOplock(fcb_oplock(fileref->fcb), Irp, NULL, NULL, NULL);
3749     if (!NT_SUCCESS(Status)) {
3750         WARN("FsRtlCheckOplock returned %08lx\n", Status);
3751         free_fileref(fileref);
3752 
3753         return Status;
3754     }
3755 
3756     if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
3757         ULONG defda, oldatts, filter;
3758         LARGE_INTEGER time;
3759         BTRFS_TIME now;
3760 
3761         if ((RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF) && readonly) {
3762             WARN("cannot overwrite readonly file\n");
3763 
3764             IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3765 
3766             free_fileref(fileref);
3767 
3768             return STATUS_ACCESS_DENIED;
3769         }
3770 
3771         if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))) {
3772             IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3773 
3774             free_fileref(fileref);
3775 
3776             return STATUS_ACCESS_DENIED;
3777         }
3778 
3779         if (fileref->fcb->ads) {
3780             Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, false);
3781             if (!NT_SUCCESS(Status)) {
3782                 ERR("stream_set_end_of_file_information returned %08lx\n", Status);
3783 
3784                 IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3785 
3786                 free_fileref(fileref);
3787 
3788                 return Status;
3789             }
3790         } else {
3791             Status = truncate_file(fileref->fcb, 0, Irp, rollback);
3792             if (!NT_SUCCESS(Status)) {
3793                 ERR("truncate_file returned %08lx\n", Status);
3794 
3795                 IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3796 
3797                 free_fileref(fileref);
3798 
3799                 return Status;
3800             }
3801         }
3802 
3803         if (Irp->Overlay.AllocationSize.QuadPart > 0) {
3804             Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
3805 
3806             if (!NT_SUCCESS(Status)) {
3807                 ERR("extend_file returned %08lx\n", Status);
3808 
3809                 IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3810 
3811                 free_fileref(fileref);
3812 
3813                 return Status;
3814             }
3815         }
3816 
3817         if (!fileref->fcb->ads) {
3818             LIST_ENTRY* le;
3819 
3820             if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
3821                 ULONG offset;
3822                 FILE_FULL_EA_INFORMATION* eainfo;
3823 
3824                 Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
3825                 if (!NT_SUCCESS(Status)) {
3826                     ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
3827 
3828                     IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3829 
3830                     free_fileref(fileref);
3831 
3832                     return Status;
3833                 }
3834 
3835                 fileref->fcb->ealen = 4;
3836 
3837                 // capitalize EA name
3838                 eainfo = Irp->AssociatedIrp.SystemBuffer;
3839                 do {
3840                     STRING s;
3841 
3842                     s.Length = s.MaximumLength = eainfo->EaNameLength;
3843                     s.Buffer = eainfo->EaName;
3844 
3845                     RtlUpperString(&s, &s);
3846 
3847                     fileref->fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
3848 
3849                     if (eainfo->NextEntryOffset == 0)
3850                         break;
3851 
3852                     eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset);
3853                 } while (true);
3854 
3855                 if (fileref->fcb->ea_xattr.Buffer)
3856                     ExFreePool(fileref->fcb->ea_xattr.Buffer);
3857 
3858                 fileref->fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, IrpSp->Parameters.Create.EaLength, ALLOC_TAG);
3859                 if (!fileref->fcb->ea_xattr.Buffer) {
3860                     ERR("out of memory\n");
3861 
3862                     IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3863 
3864                     free_fileref(fileref);
3865 
3866                     return STATUS_INSUFFICIENT_RESOURCES;
3867                 }
3868 
3869                 fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = (USHORT)IrpSp->Parameters.Create.EaLength;
3870                 RtlCopyMemory(fileref->fcb->ea_xattr.Buffer, Irp->AssociatedIrp.SystemBuffer, fileref->fcb->ea_xattr.Length);
3871             } else {
3872                 if (fileref->fcb->ea_xattr.Length > 0) {
3873                     ExFreePool(fileref->fcb->ea_xattr.Buffer);
3874                     fileref->fcb->ea_xattr.Buffer = NULL;
3875                     fileref->fcb->ea_xattr.Length = fileref->fcb->ea_xattr.MaximumLength = 0;
3876 
3877                     fileref->fcb->ea_changed = true;
3878                     fileref->fcb->ealen = 0;
3879                 }
3880             }
3881 
3882             // remove streams and send notifications
3883             le = fileref->fcb->dir_children_index.Flink;
3884             while (le != &fileref->fcb->dir_children_index) {
3885                 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
3886                 LIST_ENTRY* le2 = le->Flink;
3887 
3888                 if (dc->index == 0) {
3889                     if (!dc->fileref) {
3890                         file_ref* fr2;
3891 
3892                         Status = open_fileref_child(Vcb, fileref, &dc->name, true, true, true, PagedPool, &fr2, NULL);
3893                         if (!NT_SUCCESS(Status))
3894                             WARN("open_fileref_child returned %08lx\n", Status);
3895                     }
3896 
3897                     if (dc->fileref) {
3898                         queue_notification_fcb(fileref, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM, &dc->name);
3899 
3900                         Status = delete_fileref(dc->fileref, NULL, false, NULL, rollback);
3901                         if (!NT_SUCCESS(Status)) {
3902                             ERR("delete_fileref returned %08lx\n", Status);
3903 
3904                             free_fileref(fileref);
3905 
3906                             return Status;
3907                         }
3908                     }
3909                 } else
3910                     break;
3911 
3912                 le = le2;
3913             }
3914         }
3915 
3916         KeQuerySystemTime(&time);
3917         win_time_to_unix(time, &now);
3918 
3919         filter = FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE;
3920 
3921         if (fileref->fcb->ads) {
3922             fileref->parent->fcb->inode_item.st_mtime = now;
3923             fileref->parent->fcb->inode_item_changed = true;
3924             mark_fcb_dirty(fileref->parent->fcb);
3925 
3926             queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED, &fileref->dc->name);
3927         } else {
3928             mark_fcb_dirty(fileref->fcb);
3929 
3930             oldatts = fileref->fcb->atts;
3931 
3932             defda = get_file_attributes(Vcb, fileref->fcb->subvol, fileref->fcb->inode, fileref->fcb->type,
3933                                         fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp);
3934 
3935             if (RequestedDisposition == FILE_SUPERSEDE)
3936                 fileref->fcb->atts = IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
3937             else
3938                 fileref->fcb->atts |= IrpSp->Parameters.Create.FileAttributes | FILE_ATTRIBUTE_ARCHIVE;
3939 
3940             if (fileref->fcb->atts != oldatts) {
3941                 fileref->fcb->atts_changed = true;
3942                 fileref->fcb->atts_deleted = IrpSp->Parameters.Create.FileAttributes == defda;
3943                 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
3944             }
3945 
3946             fileref->fcb->inode_item.transid = Vcb->superblock.generation;
3947             fileref->fcb->inode_item.sequence++;
3948             fileref->fcb->inode_item.st_ctime = now;
3949             fileref->fcb->inode_item.st_mtime = now;
3950             fileref->fcb->inode_item_changed = true;
3951 
3952             queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL);
3953         }
3954     } else {
3955         if (options & FILE_NO_EA_KNOWLEDGE && fileref->fcb->ea_xattr.Length > 0) {
3956             FILE_FULL_EA_INFORMATION* ffei = (FILE_FULL_EA_INFORMATION*)fileref->fcb->ea_xattr.Buffer;
3957 
3958             do {
3959                 if (ffei->Flags & FILE_NEED_EA) {
3960                     WARN("returning STATUS_ACCESS_DENIED as no EA knowledge\n");
3961 
3962                     IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3963 
3964                     free_fileref(fileref);
3965 
3966                     return STATUS_ACCESS_DENIED;
3967                 }
3968 
3969                 if (ffei->NextEntryOffset == 0)
3970                     break;
3971 
3972                 ffei = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ffei) + ffei->NextEntryOffset);
3973             } while (true);
3974         }
3975     }
3976 
3977     FileObject->FsContext = fileref->fcb;
3978 
3979     ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
3980     if (!ccb) {
3981         ERR("out of memory\n");
3982 
3983         IoRemoveShareAccess(FileObject, &fileref->fcb->share_access);
3984 
3985         free_fileref(fileref);
3986 
3987         return STATUS_INSUFFICIENT_RESOURCES;
3988     }
3989 
3990     RtlZeroMemory(ccb, sizeof(*ccb));
3991 
3992     ccb->NodeType = BTRFS_NODE_TYPE_CCB;
3993     ccb->NodeSize = sizeof(*ccb);
3994     ccb->disposition = RequestedDisposition;
3995     ccb->options = options;
3996     ccb->query_dir_offset = 0;
3997     RtlInitUnicodeString(&ccb->query_string, NULL);
3998     ccb->has_wildcard = false;
3999     ccb->specific_file = false;
4000     ccb->access = *granted_access;
4001     ccb->case_sensitive = IrpSp->Flags & SL_CASE_SENSITIVE;
4002     ccb->reserving = false;
4003     ccb->lxss = called_from_lxss();
4004 
4005     ccb->fileref = fileref;
4006 
4007     FileObject->FsContext2 = ccb;
4008     FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
4009 
4010     switch (RequestedDisposition) {
4011         case FILE_SUPERSEDE:
4012             Irp->IoStatus.Information = FILE_SUPERSEDED;
4013             break;
4014 
4015         case FILE_OPEN:
4016         case FILE_OPEN_IF:
4017             Irp->IoStatus.Information = FILE_OPENED;
4018             break;
4019 
4020         case FILE_OVERWRITE:
4021         case FILE_OVERWRITE_IF:
4022             Irp->IoStatus.Information = FILE_OVERWRITTEN;
4023             break;
4024     }
4025 
4026     // Make sure paging files don't have any extents marked as being prealloc,
4027     // as this would mean we'd have to lock exclusively when writing.
4028     if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
4029         LIST_ENTRY* le;
4030         bool changed = false;
4031 
4032         ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, true);
4033 
4034         le = fileref->fcb->extents.Flink;
4035 
4036         while (le != &fileref->fcb->extents) {
4037             extent* ext = CONTAINING_RECORD(le, extent, list_entry);
4038 
4039             if (ext->extent_data.type == EXTENT_TYPE_PREALLOC) {
4040                 ext->extent_data.type = EXTENT_TYPE_REGULAR;
4041                 changed = true;
4042             }
4043 
4044             le = le->Flink;
4045         }
4046 
4047         ExReleaseResourceLite(fileref->fcb->Header.Resource);
4048 
4049         if (changed) {
4050             fileref->fcb->extents_changed = true;
4051             mark_fcb_dirty(fileref->fcb);
4052         }
4053 
4054         fileref->fcb->Header.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
4055     }
4056 
4057 #ifdef DEBUG_FCB_REFCOUNTS
4058     LONG oc = InterlockedIncrement(&fileref->open_count);
4059     ERR("fileref %p: open_count now %i\n", fileref, oc);
4060 #else
4061     InterlockedIncrement(&fileref->open_count);
4062 #endif
4063     InterlockedIncrement(&Vcb->open_files);
4064 
4065     return STATUS_SUCCESS;
4066 }
4067 
4068 NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
4069                                root* subvol, uint64_t inode, file_ref** pfr, PIRP Irp) {
4070     NTSTATUS Status;
4071     fcb* fcb;
4072     uint64_t parent = 0;
4073     UNICODE_STRING name;
4074     bool hl_alloc = false;
4075     file_ref *parfr, *fr;
4076 
4077     Status = open_fcb(Vcb, subvol, inode, 0, NULL, true, NULL, &fcb, PagedPool, Irp);
4078     if (!NT_SUCCESS(Status)) {
4079         ERR("open_fcb returned %08lx\n", Status);
4080         return Status;
4081     }
4082 
4083     ExAcquireResourceSharedLite(fcb->Header.Resource, true);
4084 
4085     if (fcb->inode_item.st_nlink == 0 || fcb->deleted) {
4086         ExReleaseResourceLite(fcb->Header.Resource);
4087         free_fcb(fcb);
4088         return STATUS_OBJECT_NAME_NOT_FOUND;
4089     }
4090 
4091     if (fcb->fileref) {
4092         *pfr = fcb->fileref;
4093         increase_fileref_refcount(fcb->fileref);
4094         free_fcb(fcb);
4095         ExReleaseResourceLite(fcb->Header.Resource);
4096         return STATUS_SUCCESS;
4097     }
4098 
4099     if (IsListEmpty(&fcb->hardlinks)) {
4100         ExReleaseResourceLite(fcb->Header.Resource);
4101 
4102         ExAcquireResourceSharedLite(&Vcb->dirty_filerefs_lock, true);
4103 
4104         if (!IsListEmpty(&Vcb->dirty_filerefs)) {
4105             LIST_ENTRY* le = Vcb->dirty_filerefs.Flink;
4106             while (le != &Vcb->dirty_filerefs) {
4107                 fr = CONTAINING_RECORD(le, file_ref, list_entry_dirty);
4108 
4109                 if (fr->fcb == fcb) {
4110                     ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
4111                     increase_fileref_refcount(fr);
4112                     free_fcb(fcb);
4113                     *pfr = fr;
4114                     return STATUS_SUCCESS;
4115                 }
4116 
4117                 le = le->Flink;
4118             }
4119         }
4120 
4121         ExReleaseResourceLite(&Vcb->dirty_filerefs_lock);
4122 
4123         {
4124             KEY searchkey;
4125             traverse_ptr tp;
4126 
4127             searchkey.obj_id = fcb->inode;
4128             searchkey.obj_type = TYPE_INODE_REF;
4129             searchkey.offset = 0;
4130 
4131             Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
4132             if (!NT_SUCCESS(Status)) {
4133                 ERR("find_item returned %08lx\n", Status);
4134                 free_fcb(fcb);
4135                 return Status;
4136             }
4137 
4138             do {
4139                 traverse_ptr next_tp;
4140 
4141                 if (tp.item->key.obj_id > fcb->inode || (tp.item->key.obj_id == fcb->inode && tp.item->key.obj_type > TYPE_INODE_EXTREF))
4142                     break;
4143 
4144                 if (tp.item->key.obj_id == fcb->inode) {
4145                     if (tp.item->key.obj_type == TYPE_INODE_REF) {
4146                         INODE_REF* ir = (INODE_REF*)tp.item->data;
4147 #ifdef __REACTOS__
4148                         ULONG stringlen;
4149 #endif
4150 
4151                         if (tp.item->size < offsetof(INODE_REF, name[0]) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
4152                             ERR("INODE_REF was too short\n");
4153                             free_fcb(fcb);
4154                             return STATUS_INTERNAL_ERROR;
4155                         }
4156 
4157 #ifndef __REACTOS__
4158                         ULONG stringlen;
4159 #endif
4160 
4161                         Status = utf8_to_utf16(NULL, 0, &stringlen, ir->name, ir->n);
4162                         if (!NT_SUCCESS(Status)) {
4163                             ERR("utf8_to_utf16 1 returned %08lx\n", Status);
4164                             free_fcb(fcb);
4165                             return Status;
4166                         }
4167 
4168                         name.Length = name.MaximumLength = (uint16_t)stringlen;
4169 
4170                         if (stringlen == 0)
4171                             name.Buffer = NULL;
4172                         else {
4173                             name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
4174 
4175                             if (!name.Buffer) {
4176                                 ERR("out of memory\n");
4177                                 free_fcb(fcb);
4178                                 return STATUS_INSUFFICIENT_RESOURCES;
4179                             }
4180 
4181                             Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, ir->name, ir->n);
4182                             if (!NT_SUCCESS(Status)) {
4183                                 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
4184                                 ExFreePool(name.Buffer);
4185                                 free_fcb(fcb);
4186                                 return Status;
4187                             }
4188 
4189                             hl_alloc = true;
4190                         }
4191 
4192                         parent = tp.item->key.offset;
4193 
4194                         break;
4195                     } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
4196                         INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data;
4197 #ifdef __REACTOS__
4198                         ULONG stringlen;
4199 #endif
4200 
4201                         if (tp.item->size < offsetof(INODE_EXTREF, name[0]) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) {
4202                             ERR("INODE_EXTREF was too short\n");
4203                             free_fcb(fcb);
4204                             return STATUS_INTERNAL_ERROR;
4205                         }
4206 
4207 #ifndef __REACTOS__
4208                         ULONG stringlen;
4209 #endif
4210 
4211                         Status = utf8_to_utf16(NULL, 0, &stringlen, ier->name, ier->n);
4212                         if (!NT_SUCCESS(Status)) {
4213                             ERR("utf8_to_utf16 1 returned %08lx\n", Status);
4214                             free_fcb(fcb);
4215                             return Status;
4216                         }
4217 
4218                         name.Length = name.MaximumLength = (uint16_t)stringlen;
4219 
4220                         if (stringlen == 0)
4221                             name.Buffer = NULL;
4222                         else {
4223                             name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
4224 
4225                             if (!name.Buffer) {
4226                                 ERR("out of memory\n");
4227                                 free_fcb(fcb);
4228                                 return STATUS_INSUFFICIENT_RESOURCES;
4229                             }
4230 
4231                             Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, ier->name, ier->n);
4232                             if (!NT_SUCCESS(Status)) {
4233                                 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
4234                                 ExFreePool(name.Buffer);
4235                                 free_fcb(fcb);
4236                                 return Status;
4237                             }
4238 
4239                             hl_alloc = true;
4240                         }
4241 
4242                         parent = ier->dir;
4243 
4244                         break;
4245                     }
4246                 }
4247 
4248                 if (find_next_item(Vcb, &tp, &next_tp, false, Irp))
4249                     tp = next_tp;
4250                 else
4251                     break;
4252             } while (true);
4253         }
4254 
4255         if (parent == 0) {
4256             WARN("trying to open inode with no references\n");
4257             free_fcb(fcb);
4258             return STATUS_INVALID_PARAMETER;
4259         }
4260     } else {
4261         hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry);
4262 
4263         name = hl->name;
4264         parent = hl->parent;
4265 
4266         ExReleaseResourceLite(fcb->Header.Resource);
4267     }
4268 
4269     if (parent == inode) { // subvolume root
4270         KEY searchkey;
4271         traverse_ptr tp;
4272 
4273         searchkey.obj_id = subvol->id;
4274         searchkey.obj_type = TYPE_ROOT_BACKREF;
4275         searchkey.offset = 0xffffffffffffffff;
4276 
4277         Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
4278         if (!NT_SUCCESS(Status)) {
4279             ERR("find_item returned %08lx\n", Status);
4280             free_fcb(fcb);
4281             return Status;
4282         }
4283 
4284         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
4285             ROOT_REF* rr = (ROOT_REF*)tp.item->data;
4286             LIST_ENTRY* le;
4287             root* r = NULL;
4288             ULONG stringlen;
4289 
4290             if (tp.item->size < sizeof(ROOT_REF)) {
4291                 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF));
4292                 free_fcb(fcb);
4293                 return STATUS_INTERNAL_ERROR;
4294             }
4295 
4296             if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) {
4297                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_REF, name[0]) + rr->n);
4298                 free_fcb(fcb);
4299                 return STATUS_INTERNAL_ERROR;
4300             }
4301 
4302             le = Vcb->roots.Flink;
4303             while (le != &Vcb->roots) {
4304                 root* r2 = CONTAINING_RECORD(le, root, list_entry);
4305 
4306                 if (r2->id == tp.item->key.offset) {
4307                     r = r2;
4308                     break;
4309                 }
4310 
4311                 le = le->Flink;
4312             }
4313 
4314             if (!r) {
4315                 ERR("couldn't find subvol %I64x\n", tp.item->key.offset);
4316                 free_fcb(fcb);
4317                 return STATUS_INTERNAL_ERROR;
4318             }
4319 
4320             Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp);
4321             if (!NT_SUCCESS(Status)) {
4322                 ERR("open_fileref_by_inode returned %08lx\n", Status);
4323                 free_fcb(fcb);
4324                 return Status;
4325             }
4326 
4327             Status = utf8_to_utf16(NULL, 0, &stringlen, rr->name, rr->n);
4328             if (!NT_SUCCESS(Status)) {
4329                 ERR("utf8_to_utf16 1 returned %08lx\n", Status);
4330                 free_fcb(fcb);
4331                 return Status;
4332             }
4333 
4334             name.Length = name.MaximumLength = (uint16_t)stringlen;
4335 
4336             if (stringlen == 0)
4337                 name.Buffer = NULL;
4338             else {
4339                 if (hl_alloc)
4340                     ExFreePool(name.Buffer);
4341 
4342                 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
4343 
4344                 if (!name.Buffer) {
4345                     ERR("out of memory\n");
4346                     free_fcb(fcb);
4347                     return STATUS_INSUFFICIENT_RESOURCES;
4348                 }
4349 
4350                 Status = utf8_to_utf16(name.Buffer, stringlen, &stringlen, rr->name, rr->n);
4351                 if (!NT_SUCCESS(Status)) {
4352                     ERR("utf8_to_utf16 2 returned %08lx\n", Status);
4353                     ExFreePool(name.Buffer);
4354                     free_fcb(fcb);
4355                     return Status;
4356                 }
4357 
4358                 hl_alloc = true;
4359             }
4360         } else {
4361             if (!Vcb->options.no_root_dir && subvol->id == BTRFS_ROOT_FSTREE && Vcb->root_fileref->fcb->subvol != subvol) {
4362                 Status = open_fileref_by_inode(Vcb, Vcb->root_fileref->fcb->subvol, SUBVOL_ROOT_INODE, &parfr, Irp);
4363                 if (!NT_SUCCESS(Status)) {
4364                     ERR("open_fileref_by_inode returned %08lx\n", Status);
4365                     free_fcb(fcb);
4366                     return Status;
4367                 }
4368 
4369                 name.Length = name.MaximumLength = sizeof(root_dir_utf16) - sizeof(WCHAR);
4370                 name.Buffer = (WCHAR*)root_dir_utf16;
4371             } else {
4372                 ERR("couldn't find parent for subvol %I64x\n", subvol->id);
4373                 free_fcb(fcb);
4374                 return STATUS_INTERNAL_ERROR;
4375             }
4376         }
4377     } else {
4378         Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp);
4379         if (!NT_SUCCESS(Status)) {
4380             ERR("open_fileref_by_inode returned %08lx\n", Status);
4381             free_fcb(fcb);
4382             return Status;
4383         }
4384     }
4385 
4386     Status = open_fileref_child(Vcb, parfr, &name, true, true, false, PagedPool, &fr, Irp);
4387 
4388     if (hl_alloc)
4389         ExFreePool(name.Buffer);
4390 
4391     if (!NT_SUCCESS(Status)) {
4392         ERR("open_fileref_child returned %08lx\n", Status);
4393 
4394         free_fcb(fcb);
4395         free_fileref(parfr);
4396 
4397         return Status;
4398     }
4399 
4400     *pfr = fr;
4401 
4402     free_fcb(fcb);
4403     free_fileref(parfr);
4404 
4405     return STATUS_SUCCESS;
4406 }
4407 
4408 static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
4409     PFILE_OBJECT FileObject = NULL;
4410     ULONG RequestedDisposition;
4411     ULONG options;
4412     NTSTATUS Status;
4413     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4414     USHORT parsed;
4415     ULONG fn_offset = 0;
4416     file_ref *related, *fileref = NULL;
4417     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool : PagedPool;
4418     ACCESS_MASK granted_access;
4419     bool loaded_related = false;
4420     UNICODE_STRING fn;
4421 
4422     Irp->IoStatus.Information = 0;
4423 
4424     RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
4425     options = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
4426 
4427     if (options & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) {
4428         WARN("error - supersede requested with FILE_DIRECTORY_FILE\n");
4429         return STATUS_INVALID_PARAMETER;
4430     }
4431 
4432     FileObject = IrpSp->FileObject;
4433 
4434     if (!FileObject) {
4435         ERR("FileObject was NULL\n");
4436         return STATUS_INVALID_PARAMETER;
4437     }
4438 
4439     if (FileObject->RelatedFileObject && FileObject->RelatedFileObject->FsContext2) {
4440         struct _ccb* relatedccb = FileObject->RelatedFileObject->FsContext2;
4441 
4442         related = relatedccb->fileref;
4443     } else
4444         related = NULL;
4445 
4446     debug_create_options(options);
4447 
4448     switch (RequestedDisposition) {
4449         case FILE_SUPERSEDE:
4450             TRACE("requested disposition: FILE_SUPERSEDE\n");
4451             break;
4452 
4453         case FILE_CREATE:
4454             TRACE("requested disposition: FILE_CREATE\n");
4455             break;
4456 
4457         case FILE_OPEN:
4458             TRACE("requested disposition: FILE_OPEN\n");
4459             break;
4460 
4461         case FILE_OPEN_IF:
4462             TRACE("requested disposition: FILE_OPEN_IF\n");
4463             break;
4464 
4465         case FILE_OVERWRITE:
4466             TRACE("requested disposition: FILE_OVERWRITE\n");
4467             break;
4468 
4469         case FILE_OVERWRITE_IF:
4470             TRACE("requested disposition: FILE_OVERWRITE_IF\n");
4471             break;
4472 
4473         default:
4474             ERR("unknown disposition: %lx\n", RequestedDisposition);
4475             Status = STATUS_NOT_IMPLEMENTED;
4476             goto exit;
4477     }
4478 
4479     fn = FileObject->FileName;
4480 
4481     TRACE("(%.*S)\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer);
4482     TRACE("FileObject = %p\n", FileObject);
4483 
4484     if (Vcb->readonly && (RequestedDisposition == FILE_SUPERSEDE || RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OVERWRITE)) {
4485         Status = STATUS_MEDIA_WRITE_PROTECTED;
4486         goto exit;
4487     }
4488 
4489     if (options & FILE_OPEN_BY_FILE_ID) {
4490         if (RequestedDisposition != FILE_OPEN) {
4491             WARN("FILE_OPEN_BY_FILE_ID not supported for anything other than FILE_OPEN\n");
4492             Status = STATUS_INVALID_PARAMETER;
4493             goto exit;
4494         }
4495 
4496         if (fn.Length == sizeof(uint64_t)) {
4497             uint64_t inode;
4498 
4499             if (!related) {
4500                 WARN("cannot open by short file ID unless related fileref also provided");
4501                 Status = STATUS_INVALID_PARAMETER;
4502                 goto exit;
4503             }
4504 
4505             inode = (*(uint64_t*)fn.Buffer) & 0xffffffffff;
4506 
4507             if (related->fcb == Vcb->root_fileref->fcb && inode == 0)
4508                 inode = Vcb->root_fileref->fcb->inode;
4509 
4510             if (inode == 0) { // we use 0 to mean the parent of a subvolume
4511                 fileref = related->parent;
4512                 increase_fileref_refcount(fileref);
4513                 Status = STATUS_SUCCESS;
4514             } else
4515                 Status = open_fileref_by_inode(Vcb, related->fcb->subvol, inode, &fileref, Irp);
4516 
4517             goto loaded;
4518         } else if (fn.Length == sizeof(FILE_ID_128)) {
4519             uint64_t inode, subvol_id;
4520             root* subvol = NULL;
4521 
4522             RtlCopyMemory(&inode, fn.Buffer, sizeof(uint64_t));
4523             RtlCopyMemory(&subvol_id, (uint8_t*)fn.Buffer + sizeof(uint64_t), sizeof(uint64_t));
4524 
4525             if (subvol_id == BTRFS_ROOT_FSTREE || (subvol_id >= 0x100 && subvol_id < 0x8000000000000000)) {
4526                 LIST_ENTRY* le = Vcb->roots.Flink;
4527                 while (le != &Vcb->roots) {
4528                     root* r = CONTAINING_RECORD(le, root, list_entry);
4529 
4530                     if (r->id == subvol_id) {
4531                         subvol = r;
4532                         break;
4533                     }
4534 
4535                     le = le->Flink;
4536                 }
4537             }
4538 
4539             if (!subvol) {
4540                 WARN("subvol %I64x not found\n", subvol_id);
4541                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
4542             } else
4543                 Status = open_fileref_by_inode(Vcb, subvol, inode, &fileref, Irp);
4544 
4545             goto loaded;
4546         } else {
4547             WARN("invalid ID size for FILE_OPEN_BY_FILE_ID\n");
4548             Status = STATUS_INVALID_PARAMETER;
4549             goto exit;
4550         }
4551     }
4552 
4553     if (related && fn.Length != 0 && fn.Buffer[0] == '\\') {
4554         Status = STATUS_INVALID_PARAMETER;
4555         goto exit;
4556     }
4557 
4558     if (!related && RequestedDisposition != FILE_OPEN && !(IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY)) {
4559         ULONG fnoff;
4560 
4561         Status = open_fileref(Vcb, &related, &fn, NULL, true, &parsed, &fnoff,
4562                               pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
4563 
4564         if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
4565             Status = STATUS_OBJECT_PATH_NOT_FOUND;
4566         else if (Status == STATUS_REPARSE)
4567             fileref = related;
4568         else if (NT_SUCCESS(Status)) {
4569             fnoff *= sizeof(WCHAR);
4570             fnoff += (related->dc ? related->dc->name.Length : 0) + sizeof(WCHAR);
4571 
4572             if (related->fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) {
4573                 Status = STATUS_REPARSE;
4574                 fileref = related;
4575                 parsed = (USHORT)fnoff - sizeof(WCHAR);
4576             } else {
4577                 fn.Buffer = &fn.Buffer[fnoff / sizeof(WCHAR)];
4578                 fn.Length -= (USHORT)fnoff;
4579 
4580                 Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
4581                                       pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
4582 
4583                 loaded_related = true;
4584             }
4585         }
4586     } else {
4587         Status = open_fileref(Vcb, &fileref, &fn, related, IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY, &parsed, &fn_offset,
4588                               pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
4589     }
4590 
4591 loaded:
4592     if (Status == STATUS_REPARSE) {
4593         REPARSE_DATA_BUFFER* data;
4594 
4595         ExAcquireResourceSharedLite(fileref->fcb->Header.Resource, true);
4596         Status = get_reparse_block(fileref->fcb, (uint8_t**)&data);
4597         ExReleaseResourceLite(fileref->fcb->Header.Resource);
4598 
4599         if (!NT_SUCCESS(Status)) {
4600             ERR("get_reparse_block returned %08lx\n", Status);
4601 
4602             Status = STATUS_SUCCESS;
4603         } else {
4604             Status = STATUS_REPARSE;
4605             RtlCopyMemory(&Irp->IoStatus.Information, data, sizeof(ULONG));
4606 
4607             data->Reserved = FileObject->FileName.Length - parsed;
4608 
4609             Irp->Tail.Overlay.AuxiliaryBuffer = (void*)data;
4610 
4611             free_fileref(fileref);
4612 
4613             goto exit;
4614         }
4615     }
4616 
4617     if (NT_SUCCESS(Status) && fileref->deleted)
4618         Status = STATUS_OBJECT_NAME_NOT_FOUND;
4619 
4620     if (NT_SUCCESS(Status)) {
4621         if (RequestedDisposition == FILE_CREATE) {
4622             TRACE("file already exists, returning STATUS_OBJECT_NAME_COLLISION\n");
4623             Status = STATUS_OBJECT_NAME_COLLISION;
4624 
4625             free_fileref(fileref);
4626 
4627             goto exit;
4628         }
4629     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
4630         if (RequestedDisposition == FILE_OPEN || RequestedDisposition == FILE_OVERWRITE) {
4631             TRACE("file doesn't exist, returning STATUS_OBJECT_NAME_NOT_FOUND\n");
4632             goto exit;
4633         }
4634     } else if (Status == STATUS_OBJECT_PATH_NOT_FOUND) {
4635         TRACE("open_fileref returned %08lx\n", Status);
4636         goto exit;
4637     } else {
4638         ERR("open_fileref returned %08lx\n", Status);
4639         goto exit;
4640     }
4641 
4642     if (NT_SUCCESS(Status)) { // file already exists
4643         Status = open_file2(Vcb, RequestedDisposition, pool_type, fileref, &granted_access, FileObject, &fn, options, Irp, rollback);
4644         if (!NT_SUCCESS(Status))
4645             goto exit;
4646     } else {
4647         file_ref* existing_file;
4648 
4649         Status = file_create(Irp, Vcb, FileObject, related, loaded_related, &fn, RequestedDisposition, options, &existing_file, rollback);
4650 
4651         if (Status == STATUS_OBJECT_NAME_COLLISION) { // already exists
4652             fileref = existing_file;
4653 
4654             Status = open_file2(Vcb, RequestedDisposition, pool_type, fileref, &granted_access, FileObject, &fn, options, Irp, rollback);
4655             if (!NT_SUCCESS(Status))
4656                 goto exit;
4657         } else {
4658             Irp->IoStatus.Information = NT_SUCCESS(Status) ? FILE_CREATED : 0;
4659             granted_access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
4660         }
4661     }
4662 
4663     if (NT_SUCCESS(Status) && !(options & FILE_NO_INTERMEDIATE_BUFFERING))
4664         FileObject->Flags |= FO_CACHE_SUPPORTED;
4665 
4666 exit:
4667     if (loaded_related)
4668         free_fileref(related);
4669 
4670     if (Status == STATUS_SUCCESS) {
4671         fcb* fcb2;
4672 
4673         IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess |= granted_access;
4674         IrpSp->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess &= ~(granted_access | MAXIMUM_ALLOWED);
4675 
4676         if (!FileObject->Vpb)
4677             FileObject->Vpb = DeviceObject->Vpb;
4678 
4679         fcb2 = FileObject->FsContext;
4680 
4681         if (fcb2->ads) {
4682             struct _ccb* ccb2 = FileObject->FsContext2;
4683 
4684             fcb2 = ccb2->fileref->parent->fcb;
4685         }
4686 
4687         ExAcquireResourceExclusiveLite(fcb2->Header.Resource, true);
4688         fcb_load_csums(Vcb, fcb2, Irp);
4689         ExReleaseResourceLite(fcb2->Header.Resource);
4690     } else if (Status != STATUS_REPARSE && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_NOT_FOUND)
4691         TRACE("returning %08lx\n", Status);
4692 
4693     return Status;
4694 }
4695 
4696 static NTSTATUS verify_vcb(device_extension* Vcb, PIRP Irp) {
4697     NTSTATUS Status;
4698     LIST_ENTRY* le;
4699     bool need_verify = false;
4700 
4701     ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
4702 
4703     le = Vcb->devices.Flink;
4704     while (le != &Vcb->devices) {
4705         device* dev = CONTAINING_RECORD(le, device, list_entry);
4706 
4707         if (dev->devobj && dev->removable) {
4708             ULONG cc;
4709             IO_STATUS_BLOCK iosb;
4710 
4711             Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
4712 
4713             if (IoIsErrorUserInduced(Status)) {
4714                 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx (user-induced)\n", Status);
4715                 need_verify = true;
4716             } else if (!NT_SUCCESS(Status)) {
4717                 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx\n", Status);
4718                 goto end;
4719             } else if (iosb.Information < sizeof(ULONG)) {
4720                 ERR("iosb.Information was too short\n");
4721                 Status = STATUS_INTERNAL_ERROR;
4722             } else if (cc != dev->change_count) {
4723                 dev->devobj->Flags |= DO_VERIFY_VOLUME;
4724                 need_verify = true;
4725             }
4726         }
4727 
4728         le = le->Flink;
4729     }
4730 
4731     Status = STATUS_SUCCESS;
4732 
4733 end:
4734     ExReleaseResourceLite(&Vcb->tree_lock);
4735 
4736     if (need_verify) {
4737         PDEVICE_OBJECT devobj;
4738 
4739         devobj = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
4740         IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
4741 
4742         if (!devobj) {
4743             devobj = IoGetDeviceToVerify(PsGetCurrentThread());
4744             IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
4745         }
4746 
4747         devobj = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
4748 
4749         if (devobj)
4750             Status = IoVerifyVolume(devobj, false);
4751         else
4752             Status = STATUS_VERIFY_REQUIRED;
4753     }
4754 
4755     return Status;
4756 }
4757 
4758 static bool has_manage_volume_privilege(ACCESS_STATE* access_state, KPROCESSOR_MODE processor_mode) {
4759     PRIVILEGE_SET privset;
4760 
4761     privset.PrivilegeCount = 1;
4762     privset.Control = PRIVILEGE_SET_ALL_NECESSARY;
4763     privset.Privilege[0].Luid = RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE);
4764     privset.Privilege[0].Attributes = 0;
4765 
4766     return SePrivilegeCheck(&privset, &access_state->SubjectSecurityContext, processor_mode) ? true : false;
4767 }
4768 
4769 _Dispatch_type_(IRP_MJ_CREATE)
4770 _Function_class_(DRIVER_DISPATCH)
4771 NTSTATUS __stdcall drv_create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4772     NTSTATUS Status;
4773     PIO_STACK_LOCATION IrpSp;
4774     device_extension* Vcb = DeviceObject->DeviceExtension;
4775     bool top_level, locked = false;
4776 
4777     FsRtlEnterFileSystem();
4778 
4779     TRACE("create (flags = %lx)\n", Irp->Flags);
4780 
4781     top_level = is_top_level(Irp);
4782 
4783     /* return success if just called for FS device object */
4784     if (DeviceObject == master_devobj)  {
4785         TRACE("create called for FS device object\n");
4786 
4787         Irp->IoStatus.Information = FILE_OPENED;
4788         Status = STATUS_SUCCESS;
4789 
4790         goto exit;
4791     } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4792         Status = vol_create(DeviceObject, Irp);
4793         goto exit;
4794     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4795         Status = STATUS_INVALID_PARAMETER;
4796         goto exit;
4797     }
4798 
4799     if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) {
4800         Status = STATUS_DEVICE_NOT_READY;
4801         goto exit;
4802     }
4803 
4804     if (Vcb->removing) {
4805         Status = STATUS_ACCESS_DENIED;
4806         goto exit;
4807     }
4808 
4809     Status = verify_vcb(Vcb, Irp);
4810     if (!NT_SUCCESS(Status)) {
4811         ERR("verify_vcb returned %08lx\n", Status);
4812         goto exit;
4813     }
4814 
4815     ExAcquireResourceSharedLite(&Vcb->load_lock, true);
4816     locked = true;
4817 
4818     IrpSp = IoGetCurrentIrpStackLocation(Irp);
4819 
4820     if (IrpSp->Flags != 0) {
4821         uint32_t flags = IrpSp->Flags;
4822 
4823         TRACE("flags:\n");
4824 
4825         if (flags & SL_CASE_SENSITIVE) {
4826             TRACE("SL_CASE_SENSITIVE\n");
4827             flags &= ~SL_CASE_SENSITIVE;
4828         }
4829 
4830         if (flags & SL_FORCE_ACCESS_CHECK) {
4831             TRACE("SL_FORCE_ACCESS_CHECK\n");
4832             flags &= ~SL_FORCE_ACCESS_CHECK;
4833         }
4834 
4835         if (flags & SL_OPEN_PAGING_FILE) {
4836             TRACE("SL_OPEN_PAGING_FILE\n");
4837             flags &= ~SL_OPEN_PAGING_FILE;
4838         }
4839 
4840         if (flags & SL_OPEN_TARGET_DIRECTORY) {
4841             TRACE("SL_OPEN_TARGET_DIRECTORY\n");
4842             flags &= ~SL_OPEN_TARGET_DIRECTORY;
4843         }
4844 
4845         if (flags & SL_STOP_ON_SYMLINK) {
4846             TRACE("SL_STOP_ON_SYMLINK\n");
4847             flags &= ~SL_STOP_ON_SYMLINK;
4848         }
4849 
4850         if (flags)
4851             WARN("unknown flags: %x\n", flags);
4852     } else {
4853         TRACE("flags: (none)\n");
4854     }
4855 
4856     if (!IrpSp->FileObject) {
4857         ERR("FileObject was NULL\n");
4858         Status = STATUS_INVALID_PARAMETER;
4859         goto exit;
4860     }
4861 
4862     if (IrpSp->FileObject->RelatedFileObject) {
4863         fcb* relatedfcb = IrpSp->FileObject->RelatedFileObject->FsContext;
4864 
4865         if (relatedfcb && relatedfcb->Vcb != Vcb) {
4866             WARN("RelatedFileObject was for different device\n");
4867             Status = STATUS_INVALID_PARAMETER;
4868             goto exit;
4869         }
4870     }
4871 
4872     // opening volume
4873     if (IrpSp->FileObject->FileName.Length == 0 && !IrpSp->FileObject->RelatedFileObject) {
4874         ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
4875         ULONG RequestedOptions = IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
4876 #ifdef DEBUG_FCB_REFCOUNTS
4877         LONG rc;
4878 #endif
4879         ccb* ccb;
4880 
4881         TRACE("open operation for volume\n");
4882 
4883         if (RequestedDisposition != FILE_OPEN && RequestedDisposition != FILE_OPEN_IF) {
4884             Status = STATUS_ACCESS_DENIED;
4885             goto exit;
4886         }
4887 
4888         if (RequestedOptions & FILE_DIRECTORY_FILE) {
4889             Status = STATUS_NOT_A_DIRECTORY;
4890             goto exit;
4891         }
4892 
4893         ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ccb), ALLOC_TAG);
4894         if (!ccb) {
4895             ERR("out of memory\n");
4896             Status = STATUS_INSUFFICIENT_RESOURCES;
4897             goto exit;
4898         }
4899 
4900         RtlZeroMemory(ccb, sizeof(*ccb));
4901 
4902         ccb->NodeType = BTRFS_NODE_TYPE_CCB;
4903         ccb->NodeSize = sizeof(*ccb);
4904         ccb->disposition = RequestedDisposition;
4905         ccb->options = RequestedOptions;
4906         ccb->access = IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess;
4907         ccb->manage_volume_privilege = has_manage_volume_privilege(IrpSp->Parameters.Create.SecurityContext->AccessState,
4908                                                                    IrpSp->Flags & SL_FORCE_ACCESS_CHECK ? UserMode : Irp->RequestorMode);
4909         ccb->reserving = false;
4910         ccb->lxss = called_from_lxss();
4911 
4912 #ifdef DEBUG_FCB_REFCOUNTS
4913         rc = InterlockedIncrement(&Vcb->volume_fcb->refcount);
4914         WARN("fcb %p: refcount now %i (volume)\n", Vcb->volume_fcb, rc);
4915 #else
4916         InterlockedIncrement(&Vcb->volume_fcb->refcount);
4917 #endif
4918         IrpSp->FileObject->FsContext = Vcb->volume_fcb;
4919         IrpSp->FileObject->FsContext2 = ccb;
4920 
4921         IrpSp->FileObject->SectionObjectPointer = &Vcb->volume_fcb->nonpaged->segment_object;
4922 
4923         if (!IrpSp->FileObject->Vpb)
4924             IrpSp->FileObject->Vpb = DeviceObject->Vpb;
4925 
4926         InterlockedIncrement(&Vcb->open_files);
4927 
4928         Irp->IoStatus.Information = FILE_OPENED;
4929         Status = STATUS_SUCCESS;
4930     } else {
4931         LIST_ENTRY rollback;
4932         bool skip_lock;
4933 
4934         InitializeListHead(&rollback);
4935 
4936         TRACE("file name: %.*S\n", (int)(IrpSp->FileObject->FileName.Length / sizeof(WCHAR)), IrpSp->FileObject->FileName.Buffer);
4937 
4938         if (IrpSp->FileObject->RelatedFileObject)
4939             TRACE("related file = %p\n", IrpSp->FileObject->RelatedFileObject);
4940 
4941         // Don't lock again if we're being called from within CcCopyRead etc.
4942         skip_lock = ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock);
4943 
4944         if (!skip_lock)
4945             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
4946 
4947         ExAcquireResourceSharedLite(&Vcb->fileref_lock, true);
4948 
4949         Status = open_file(DeviceObject, Vcb, Irp, &rollback);
4950 
4951         if (!NT_SUCCESS(Status))
4952             do_rollback(Vcb, &rollback);
4953         else
4954             clear_rollback(&rollback);
4955 
4956         ExReleaseResourceLite(&Vcb->fileref_lock);
4957 
4958         if (!skip_lock)
4959             ExReleaseResourceLite(&Vcb->tree_lock);
4960     }
4961 
4962 exit:
4963     Irp->IoStatus.Status = Status;
4964     IoCompleteRequest( Irp, NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT );
4965 
4966     TRACE("create returning %08lx\n", Status);
4967 
4968     if (locked)
4969         ExReleaseResourceLite(&Vcb->load_lock);
4970 
4971     if (top_level)
4972         IoSetTopLevelIrp(NULL);
4973 
4974     FsRtlExitFileSystem();
4975 
4976     return Status;
4977 }
4978