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