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