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