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