ReactOS 0.4.15-dev-7924-g5949c20
create.c
Go to the documentation of this file.
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
29
30static const WCHAR datastring[] = L"::$DATA";
31
32static const char root_dir[] = "$Root";
33static 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
50typedef struct _FILE_TIMESTAMPS {
56
78
79static const GUID GUID_ECP_ATOMIC_CREATE = { 0x4720bd83, 0x52ac, 0x4104, { 0xa1, 0x30, 0xd1, 0xec, 0x6a, 0x8c, 0xc8, 0xe5 } };
80static const GUID GUID_ECP_QUERY_ON_CREATE = { 0x1aca62e9, 0xabb4, 0x4ff2, { 0xbb, 0x5c, 0x1c, 0x79, 0x02, 0x5e, 0x41, 0x7f } };
81static const GUID GUID_ECP_CREATE_REDIRECTION = { 0x188d6bd6, 0xa126, 0x4fa8, { 0xbd, 0xf2, 0x1c, 0xcd, 0xf8, 0x96, 0xf3, 0xe0 } };
82
83typedef struct {
90
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)
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
142 fcb->Header.Resource = &fcb->nonpaged->resource;
143
144 ExInitializeResourceLite(&fcb->nonpaged->dir_children_lock);
145
148
152
156
157 return fcb;
158}
159
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
178
179 return fr;
180}
181
184 UNICODE_STRING fnus;
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]) {
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) {
233
234 if (r2->id == dc->key.obj_id) {
235 *subvol = r2;
236 break;
237 }
238
239 le2 = le2->Flink;
240 }
241
243 } else {
244 *subvol = fcb->subvol;
245 *inode = dc->key.obj_id;
246 }
247
248 *pdc = dc;
249
251 goto end;
252 }
253 } else if (dc->hash > hash) {
255 goto end;
256 }
257
258 le = le->Flink;
259 }
260 } else {
261 if (!fcb->hash_ptrs_uc[c]) {
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) {
280
281 if (r2->id == dc->key.obj_id) {
282 *subvol = r2;
283 break;
284 }
285
286 le2 = le2->Flink;
287 }
288
290 } else {
291 *subvol = fcb->subvol;
292 *inode = dc->key.obj_id;
293 }
294
295 *pdc = dc;
296
298 goto end;
299 }
300 } else if (dc->hash_uc > hash) {
302 goto end;
303 }
304
305 le = le->Flink;
306 }
307 }
308
310
311end:
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
322 ULONG len, i;
323 bool has_stream;
324 WCHAR* buf;
325 name_bit* nb;
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");
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");
353 goto cleanup;
354 }
355
356 nb = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
357 if (!nb) {
358 ERR("out of memory\n");
360 goto cleanup;
361 }
362
363 nb->us.Buffer = buf;
364 nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
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");
375 goto cleanup;
376 }
377
378 nb->us.Buffer = buf;
379 nb->us.Length = nb->us.MaximumLength = (USHORT)(&path->Buffer[i] - buf) * sizeof(WCHAR);
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");
396 goto cleanup;
397 }
398
399 nb2 = ExAllocateFromPagedLookasideList(&Vcb->name_bit_lookaside);
400 if (!nb2) {
401 ERR("out of memory\n");
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));
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) {
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] == ':') {
435
436 ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb1);
437 }
438
439 *stream = has_stream;
440
441 return STATUS_SUCCESS;
442
443cleanup:
444 while (!IsListEmpty(parts)) {
446
447 ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
448 }
449
450 return Status;
451}
452
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));
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);
505 }
506
507 return STATUS_SUCCESS;
508}
509
511 KEY searchkey;
512 traverse_ptr tp, next_tp;
514 ULONG num_children = 0;
515 uint64_t max_index = 2;
516
518 if (!fcb->hash_ptrs) {
519 ERR("out of memory\n");
521 }
522
523 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
524
526 if (!fcb->hash_ptrs_uc) {
527 ERR("out of memory\n");
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
575 if (!dc) {
576 ERR("out of memory\n");
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);
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);
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
631
632 num_children++;
633
634cont:
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) {
651 if (!dc) {
652 ERR("out of memory\n");
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);
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);
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
700 }
701 }
702
703 return STATUS_SUCCESS;
704}
705
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;
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;
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
735
736 WARN("fcb %p: refcount now %i (subvol %I64x, inode %I64x)\n", fcb, rc, fcb->subvol->id, fcb->inode);
737#else
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");
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);
800 }
801
802 if (tp.item->size > 0)
804
805 if (fcb->type == 0) { // guess the type from the inode mode, if the caller doesn't know already
808 else if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR)
810 else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK)
812 else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO)
814 else if ((fcb->inode_item.st_mode & __S_IFLNK) == __S_IFLNK)
816 else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK)
818 else
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
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;
843
844 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
845 if (!hl) {
846 ERR("out of memory\n");
847 reap_fcb(fcb);
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) {
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
870
871 if (stringlen == 0)
872 hl->name.Buffer = NULL;
873 else {
875
876 if (!hl->name.Buffer) {
877 ERR("out of memory\n");
878 ExFreePool(hl);
879 reap_fcb(fcb);
881 }
882
884 if (!NT_SUCCESS(Status)) {
885 ERR("utf8_to_utf16 2 returned %08lx\n", Status);
887 ExFreePool(hl);
888 reap_fcb(fcb);
889 return Status;
890 }
891 }
892
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;
908
909 hl = ExAllocatePoolWithTag(pooltype, sizeof(hardlink), ALLOC_TAG);
910 if (!hl) {
911 ERR("out of memory\n");
912 reap_fcb(fcb);
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) {
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
935
936 if (stringlen == 0)
937 hl->name.Buffer = NULL;
938 else {
940
941 if (!hl->name.Buffer) {
942 ERR("out of memory\n");
943 ExFreePool(hl);
944 reap_fcb(fcb);
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);
952 ExFreePool(hl);
953 reap_fcb(fcb);
954 return Status;
955 }
956 }
957
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) {
984 if (!fcb->reparse_xattr.Buffer) {
985 ERR("out of memory\n");
986 reap_fcb(fcb);
988 }
989
990 RtlCopyMemory(fcb->reparse_xattr.Buffer, &di->name[di->n], di->m);
991 } else
993
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) {
998
1000
1001 if (!NT_SUCCESS(Status))
1002 WARN("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
1003 else {
1005
1007 if (!fcb->ea_xattr.Buffer) {
1008 ERR("out of memory\n");
1009 reap_fcb(fcb);
1011 }
1012
1013 RtlCopyMemory(fcb->ea_xattr.Buffer, &di->name[di->n], di->m);
1014
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
1038 else if (fcb->type == BTRFS_TYPE_SYMLINK)
1040
1042 fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY;
1043
1044 if (inode == SUBVOL_ROOT_INODE) {
1045 if (subvol->root_item.flags & BTRFS_SUBVOL_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) {
1055 if (!fcb->sd) {
1056 ERR("out of memory\n");
1057 reap_fcb(fcb);
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)
1078 else if (di->m == sizeof(zlib) - 1 && RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m)
1080 else if (di->m == sizeof(zstd) - 1 && RtlCompareMemory(&di->name[di->n], zstd, di->m) == di->m)
1082 else
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
1102 if (!dc) {
1103 ERR("out of memory\n");
1104 reap_fcb(fcb);
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);
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);
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);
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
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);
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
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
1312 fcb->atts &= ~FILE_ATTRIBUTE_REPARSE_POINT;
1313
1314 if (!Vcb->readonly && !is_subvol_readonly(subvol, Irp)) {
1315 fcb->atts_changed = true;
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 {
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)
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
1367 dir_child* dc, fcb* parent, fcb** pfcb, PIRP Irp) {
1368 fcb* fcb;
1369 uint8_t* xattrdata;
1370 uint16_t xattrlen, overhead;
1372 KEY searchkey;
1374 static const char xapref[] = "user.";
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");
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
1391 if (!fcb) {
1392 ERR("out of memory\n");
1393 ExFreePool(xattr.Buffer);
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
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) {
1463 file_ref* sf2;
1464
1465 if (sf->fcb == Vcb->dummy_fcb)
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
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);
1575 }
1576
1577 sf2 = create_fileref(Vcb);
1578 if (!sf2) {
1579 ERR("out of memory\n");
1580 free_fcb(fcb);
1582 }
1583
1584 ExAcquireResourceExclusiveLite(&sf->fcb->nonpaged->dir_children_lock, true);
1585
1586 if (dc->fileref) {
1587 duff_fr = sf2;
1588 sf2 = dc->fileref;
1590 } else {
1591 sf2->fcb = fcb;
1592 sf2->parent = (struct _file_ref*)sf;
1593 sf2->dc = dc;
1594 dc->fileref = sf2;
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;
1606 dir_child* dc;
1607
1608 Status = find_file_in_dir(name, sf->fcb, &subvol, &inode, &dc, case_sensitive);
1610 TRACE("could not find %.*S\n", (int)(name->Length / sizeof(WCHAR)), name->Buffer);
1611
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");
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;
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);
1650 }
1651
1652 sf2 = create_fileref(Vcb);
1653 if (!sf2) {
1654 ERR("out of memory\n");
1655 free_fcb(fcb);
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);
1669
1670 if (dc->type == BTRFS_TYPE_DIRECTORY)
1671 fcb->fileref = sf2;
1672 } else {
1673 duff_fr = sf2;
1674 sf2 = dc->fileref;
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
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;
1696 bool has_stream = false;
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) {
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);
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
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] == '\\')
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);
1759 }
1760
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;
1774
1775 if (parent && !IsListEmpty(&parts)) {
1776 name_bit* nb;
1777
1779 ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
1780
1781 if (has_stream && !IsListEmpty(&parts)) {
1783 ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
1784
1785 has_stream = false;
1786 }
1787 }
1788
1789 if (IsListEmpty(&parts)) {
1791 *pfr = dir;
1792
1793 if (fn_offset)
1794 *fn_offset = 0;
1795
1796 goto end2;
1797 }
1798
1799 le = parts.Flink;
1800 do {
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)) {
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)
1828
1829 *fn_offset = (ULONG)(nb->us.Buffer - fnus->Buffer);
1830 }
1831
1832 break;
1833 }
1834
1837
1838 if (parsed) {
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)
1855 *pfr = sf2;
1856
1857end:
1858 free_fileref(sf);
1859
1860 while (!IsListEmpty(&parts)) {
1862 ExFreeToPagedLookasideList(&Vcb->name_bit_lookaside, nb);
1863 }
1864
1865end2:
1866 TRACE("returning %08lx\n", Status);
1867
1868 return Status;
1869}
1870
1873 dir_child* dc;
1874 bool locked;
1875
1877 if (!dc) {
1878 ERR("out of memory\n");
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);
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);
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
1929 dc->index = 2;
1930 else {
1932
1933 dc->index = max(2, dc2->index + 1);
1934 }
1935
1936 InsertTailList(&fcb->dir_children_index, &dc->list_entry_index);
1937
1939
1940 if (!locked)
1941 ExReleaseResourceLite(&fcb->nonpaged->dir_children_lock);
1942
1943 *pdc = dc;
1944
1945 return STATUS_SUCCESS;
1946}
1947
1948uint32_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
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) {
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) {
1998 if (!item) {
1999 ERR("out of memory\n");
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;
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");
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);
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");
2044 goto end;
2045 }
2046
2047 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t));
2048
2049 RemoveEntryList(&item->list_entry);
2051 } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) {
2053 uint32_t val;
2054
2055 if (item->value.Length < sizeof(uint32_t)) {
2056 ERR("mode value was shorter than expected\n");
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
2067 if (__S_ISTYPE(val, __S_IFCHR)) {
2069 fcb->inode_item.st_mode &= ~__S_IFMT;
2071 } else if (__S_ISTYPE(val, __S_IFBLK)) {
2073 fcb->inode_item.st_mode &= ~__S_IFMT;
2075 } else if (__S_ISTYPE(val, __S_IFIFO)) {
2077 fcb->inode_item.st_mode &= ~__S_IFMT;
2079 } else if (__S_ISTYPE(val, __S_IFSOCK)) {
2081 fcb->inode_item.st_mode &= ~__S_IFMT;
2083 }
2084 }
2085
2086 RemoveEntryList(&item->list_entry);
2088 } else if (item->name.Length == sizeof(lxdev) - 1 && RtlCompareMemory(item->name.Buffer, lxdev, item->name.Length) == item->name.Length) {
2090
2091 if (item->value.Length < sizeof(uint64_t)) {
2092 ERR("dev value was shorter than expected\n");
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);
2104 }
2105
2106 le = le2;
2107 }
2108
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) {
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
2128 if (!buf) {
2129 ERR("out of memory\n");
2131 goto end;
2132 }
2133
2136
2137 fcb->ealen = 4;
2138 ea = NULL;
2139
2140 le = ealist.Flink;
2141 while (le != &ealist) {
2143
2144 if (ea) {
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
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
2171
2172end:
2173 while (!IsListEmpty(&ealist)) {
2175
2177 }
2178
2179 return Status;
2180}
2181
2184 _Out_ file_ref** pfr, bool case_sensitive, _In_ LIST_ENTRY* rollback) {
2186 fcb* fcb;
2187 ULONG utf8len;
2188 char* utf8 = NULL;
2190 uint8_t type;
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
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");
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
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
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
2263 defda |= 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
2288 }
2289
2290 fcb->Vcb = Vcb;
2291
2294
2295 fcb->inode_item.generation = Vcb->superblock.generation;
2296 fcb->inode_item.transid = Vcb->superblock.generation;
2297 fcb->inode_item.st_size = 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;
2310
2313 else {
2315 fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory
2316 }
2317
2320 } else {
2321 // inherit nodatacow flag from parent directory
2322 if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW || Vcb->options.nodatacow) {
2324
2327 }
2328
2329 if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS &&
2332 }
2333 }
2334
2336 fcb->prop_compression = parfileref->fcb->prop_compression;
2338 } else
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 {
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
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) {
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
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
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);
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) {
2487 if (!fcb->hash_ptrs) {
2488 ERR("out of memory\n");
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
2498 }
2499
2500 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
2501
2503 if (!fcb->hash_ptrs_uc) {
2504 ERR("out of memory\n");
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
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);
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);
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
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);
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
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
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
2641 file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream, PIRP 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";
2653 ULONG utf8len, overhead;
2655 KEY searchkey;
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
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;
2687
2688 if (!fpus2.Buffer) {
2689 ERR("out of memory\n");
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,
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) {
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);
2731 }
2732
2734 WARN("tried to create directory as stream\n");
2735 free_fileref(parfileref);
2737 }
2738
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,
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);
2762 }
2763
2765 if (!fcb) {
2766 ERR("out of memory\n");
2767 free_fileref(parfileref);
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;
2802 if (!fcb->adsxattr.Buffer) {
2803 ERR("out of memory\n");
2804 reap_fcb(fcb);
2805 free_fileref(parfileref);
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
2820
2821 TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
2822
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
2863
2865 if (!fileref) {
2866 ERR("out of memory\n");
2867 free_fcb(fcb);
2868 free_fileref(parfileref);
2870 }
2871
2872 fileref->fcb = fcb;
2873
2875 if (!dc) {
2876 ERR("out of memory\n");
2878 free_fileref(parfileref);
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);
2890 free_fileref(parfileref);
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);
2903 free_fileref(parfileref);
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);
2916 free_fileref(parfileref);
2917 return Status;
2918 }
2919
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);
2947 free_fileref(parfileref);
2948
2949 increase_fileref_refcount(existing_dc->fileref);
2950 *pfileref = existing_dc->fileref;
2951
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
2983
2984 return STATUS_SUCCESS;
2985}
2986
2987// LXSS programs can be distinguished by the fact they have a NULL PEB.
2988#ifdef _AMD64_
2989static __inline bool called_from_lxss() {
2992 ULONG retlen;
2993
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
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) {
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;
3018 ECP_LIST* ecp_list;
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)
3028
3031 return STATUS_CANNOT_DELETE;
3032 }
3033
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,
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] != ':')) {
3079 goto end;
3080 }
3081
3082 if (is_subvol_readonly(parfileref->fcb->subvol, Irp)) {
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");
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,
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
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
3192 }
3193
3194 FileObject->FsContext = fileref->fcb;
3195
3197 if (!ccb) {
3198 ERR("out of memory\n");
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
3218 ccb->NodeSize = sizeof(*ccb);
3219 ccb->disposition = disposition;
3220 ccb->options = options;
3221 ccb->query_dir_offset = 0;
3223 ccb->has_wildcard = false;
3224 ccb->specific_file = false;
3225 ccb->access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
3227 ccb->reserving = false;
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
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
3244 if (acec->ReparseBufferLength > sizeof(uint32_t) && *(uint32_t*)acec->ReparseBuffer == IO_REPARSE_TAG_SYMLINK) {
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 {
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
3272 }
3273
3278 fileref->fcb->case_sensitive = true;
3279 ccb->case_sensitive = true;
3280 }
3281
3283 }
3284
3286 }
3287
3288 fileref->dc->type = fileref->fcb->type;
3289
3290end:
3291 if (fpus.Buffer)
3292 ExFreePool(fpus.Buffer);
3293
3294 if (parfileref && !loaded_related)
3295 free_fileref(parfileref);
3296
3297 return Status;
3298}
3299
3300static __inline void debug_create_options(ULONG RequestedOptions) {
3301 if (RequestedOptions != 0) {
3302 ULONG options = RequestedOptions;
3303
3304 TRACE("requested options:\n");
3305
3307 TRACE(" FILE_DIRECTORY_FILE\n");
3308 options &= ~FILE_DIRECTORY_FILE;
3309 }
3310
3312 TRACE(" FILE_WRITE_THROUGH\n");
3313 options &= ~FILE_WRITE_THROUGH;
3314 }
3315
3317 TRACE(" FILE_SEQUENTIAL_ONLY\n");
3318 options &= ~FILE_SEQUENTIAL_ONLY;
3319 }
3320
3322 TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
3323 options &= ~FILE_NO_INTERMEDIATE_BUFFERING;
3324 }
3325
3327 TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
3328 options &= ~FILE_SYNCHRONOUS_IO_ALERT;
3329 }
3330
3332 TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
3333 options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
3334 }
3335
3337 TRACE(" FILE_NON_DIRECTORY_FILE\n");
3338 options &= ~FILE_NON_DIRECTORY_FILE;
3339 }
3340
3342 TRACE(" FILE_CREATE_TREE_CONNECTION\n");
3343 options &= ~FILE_CREATE_TREE_CONNECTION;
3344 }
3345
3347 TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
3348 options &= ~FILE_COMPLETE_IF_OPLOCKED;
3349 }
3350
3352 TRACE(" FILE_NO_EA_KNOWLEDGE\n");
3353 options &= ~FILE_NO_EA_KNOWLEDGE;
3354 }
3355
3357 TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
3358 options &= ~FILE_OPEN_REMOTE_INSTANCE;
3359 }
3360
3362 TRACE(" FILE_RANDOM_ACCESS\n");
3363 options &= ~FILE_RANDOM_ACCESS;
3364 }
3365
3367 TRACE(" FILE_DELETE_ON_CLOSE\n");
3368 options &= ~FILE_DELETE_ON_CLOSE;
3369 }
3370
3372 TRACE(" FILE_OPEN_BY_FILE_ID\n");
3373 options &= ~FILE_OPEN_BY_FILE_ID;
3374 }
3375
3377 TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
3378 options &= ~FILE_OPEN_FOR_BACKUP_INTENT;
3379 }
3380
3382 TRACE(" FILE_NO_COMPRESSION\n");