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