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