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