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