ReactOS  0.4.15-dev-4872-g8a3db97
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) {
2324 
2325  if (type != BTRFS_TYPE_DIRECTORY)
2327  }
2328 
2329  if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
2331  }
2332 
2333  fcb->prop_compression = parfileref->fcb->prop_compression;
2335 
2336  fcb->inode_item_changed = true;
2337 
2338  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2339  fcb->Header.AllocationSize.QuadPart = 0;
2340  fcb->Header.FileSize.QuadPart = 0;
2341  fcb->Header.ValidDataLength.QuadPart = 0;
2342 
2343  fcb->atts = IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
2344  fcb->atts_changed = fcb->atts != defda;
2345 
2346 #ifdef DEBUG_FCB_REFCOUNTS
2347  rc = InterlockedIncrement(&parfileref->fcb->refcount);
2348  WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
2349 #else
2350  InterlockedIncrement(&parfileref->fcb->refcount);
2351 #endif
2352  fcb->subvol = parfileref->fcb->subvol;
2353  fcb->inode = inode;
2354  fcb->type = type;
2355  fcb->created = true;
2356  fcb->deleted = true;
2357 
2358  fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&inode, sizeof(uint64_t));
2359 
2360  acquire_fcb_lock_exclusive(Vcb);
2361 
2362  if (fcb->subvol->fcbs_ptrs[fcb->hash >> 24]) {
2363  LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[fcb->hash >> 24];
2364 
2365  while (le != &fcb->subvol->fcbs) {
2366  struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry);
2367 
2368  if (fcb2->hash > fcb->hash) {
2369  lastle = le->Blink;
2370  break;
2371  }
2372 
2373  le = le->Flink;
2374  }
2375  }
2376 
2377  if (!lastle) {
2378  uint8_t c = fcb->hash >> 24;
2379 
2380  if (c != 0xff) {
2381  uint8_t d = c + 1;
2382 
2383  do {
2384  if (fcb->subvol->fcbs_ptrs[d]) {
2385  lastle = fcb->subvol->fcbs_ptrs[d]->Blink;
2386  break;
2387  }
2388 
2389  d++;
2390  } while (d != 0);
2391  }
2392  }
2393 
2394  if (lastle) {
2395  InsertHeadList(lastle, &fcb->list_entry);
2396 
2397  if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
2398  fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
2399  } else {
2400  InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry);
2401 
2402  if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (fcb->hash >> 24))
2403  fcb->subvol->fcbs_ptrs[fcb->hash >> 24] = &fcb->list_entry;
2404  }
2405 
2406  InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2407 
2408  fcb->subvol->fcbs_version++;
2409 
2410  release_fcb_lock(Vcb);
2411 
2413 
2414  Status = fcb_get_new_sd(fcb, parfileref, IrpSp->Parameters.Create.SecurityContext->AccessState);
2415 
2416  if (!NT_SUCCESS(Status)) {
2417  ERR("fcb_get_new_sd returned %08lx\n", Status);
2418  free_fcb(fcb);
2419 
2420  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2421  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2422  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2423 
2424  ExFreePool(utf8);
2425 
2426  return Status;
2427  }
2428 
2429  fcb->sd_dirty = true;
2430 
2431  if (ea && ealen > 0) {
2433  if (!NT_SUCCESS(Status)) {
2434  ERR("file_create_parse_ea returned %08lx\n", Status);
2435  free_fcb(fcb);
2436 
2437  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2438  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2439  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2440 
2441  ExFreePool(utf8);
2442 
2443  return Status;
2444  }
2445  }
2446 
2448  if (!fileref) {
2449  ERR("out of memory\n");
2450  free_fcb(fcb);
2451 
2452  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2453  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2454  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2455 
2456  ExFreePool(utf8);
2457 
2459  }
2460 
2461  fileref->fcb = fcb;
2462 
2463  if (Irp->Overlay.AllocationSize.QuadPart > 0 && !write_fcb_compressed(fcb) && fcb->type != BTRFS_TYPE_DIRECTORY) {
2464  Status = extend_file(fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
2465 
2466  if (!NT_SUCCESS(Status)) {
2467  ERR("extend_file returned %08lx\n", Status);
2469 
2470  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2471  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2472  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2473 
2474  ExFreePool(utf8);
2475 
2476  return Status;
2477  }
2478  }
2479 
2480  if (fcb->type == BTRFS_TYPE_DIRECTORY) {
2482  if (!fcb->hash_ptrs) {
2483  ERR("out of memory\n");
2485 
2486  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2487  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2488  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2489 
2490  ExFreePool(utf8);
2491 
2493  }
2494 
2495  RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
2496 
2498  if (!fcb->hash_ptrs_uc) {
2499  ERR("out of memory\n");
2501 
2502  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2503  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2504  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2505 
2506  ExFreePool(utf8);
2507 
2509  }
2510 
2511  RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
2512  }
2513 
2514  fcb->deleted = false;
2515 
2516  fileref->created = true;
2517 
2518  fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2519  fcb->subvol->root_item.ctime = now;
2520 
2521  utf8as.Buffer = utf8;
2522  utf8as.Length = utf8as.MaximumLength = (uint16_t)utf8len;
2523 
2524  ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
2525 
2526  // check again doesn't already exist
2527  if (case_sensitive) {
2528  uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpus->Buffer, fpus->Length);
2529 
2530  if (parfileref->fcb->hash_ptrs[dc_hash >> 24]) {
2531  LIST_ENTRY* le = parfileref->fcb->hash_ptrs[dc_hash >> 24];
2532  while (le != &parfileref->fcb->dir_children_hash) {
2533  dc = CONTAINING_RECORD(le, dir_child, list_entry_hash);
2534 
2535  if (dc->hash == dc_hash && dc->name.Length == fpus->Length && RtlCompareMemory(dc->name.Buffer, fpus->Buffer, fpus->Length) == fpus->Length) {
2536  existing_fileref = dc->fileref;
2537  break;
2538  } else if (dc->hash > dc_hash)
2539  break;
2540 
2541  le = le->Flink;
2542  }
2543  }
2544  } else {
2545  UNICODE_STRING fpusuc;
2546 
2547  Status = RtlUpcaseUnicodeString(&fpusuc, fpus, true);
2548  if (!NT_SUCCESS(Status)) {
2549  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2550  ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2552 
2553  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2554  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2555  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2556 
2557  ExFreePool(utf8);
2558 
2559  return Status;
2560  }
2561 
2562  uint32_t dc_hash = calc_crc32c(0xffffffff, (uint8_t*)fpusuc.Buffer, fpusuc.Length);
2563 
2564  if (parfileref->fcb->hash_ptrs_uc[dc_hash >> 24]) {
2565  LIST_ENTRY* le = parfileref->fcb->hash_ptrs_uc[dc_hash >> 24];
2566  while (le != &parfileref->fcb->dir_children_hash_uc) {
2567  dc = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc);
2568 
2569  if (dc->hash_uc == dc_hash && dc->name.Length == fpusuc.Length && RtlCompareMemory(dc->name.Buffer, fpusuc.Buffer, fpusuc.Length) == fpusuc.Length) {
2570  existing_fileref = dc->fileref;
2571  break;
2572  } else if (dc->hash_uc > dc_hash)
2573  break;
2574 
2575  le = le->Flink;
2576  }
2577  }
2578 
2579  ExFreePool(fpusuc.Buffer);
2580  }
2581 
2582  if (existing_fileref) {
2583  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2585 
2586  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2587  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2588  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2589 
2590  ExFreePool(utf8);
2591 
2592  increase_fileref_refcount(existing_fileref);
2593  *pfr = existing_fileref;
2594 
2596  }
2597 
2598  Status = add_dir_child(parfileref->fcb, fcb->inode, false, &utf8as, fpus, fcb->type, &dc);
2599  if (!NT_SUCCESS(Status)) {
2600  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2601  ERR("add_dir_child returned %08lx\n", Status);
2603 
2604  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
2605  parfileref->fcb->inode_item.st_size -= utf8len * 2;
2606  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
2607 
2608  ExFreePool(utf8);
2609 
2610  return Status;
2611  }
2612 
2613  fileref->parent = parfileref;
2614  fileref->dc = dc;
2615  dc->fileref = fileref;
2616 
2617  if (type == BTRFS_TYPE_DIRECTORY)
2618  fileref->fcb->fileref = fileref;
2619 
2620  InsertTailList(&parfileref->children, &fileref->list_entry);
2621  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2622 
2623  ExFreePool(utf8);
2624 
2626  increase_fileref_refcount(parfileref);
2627 
2628  *pfr = fileref;
2629 
2630  TRACE("created new file in subvol %I64x, inode %I64x\n", fcb->subvol->id, fcb->inode);
2631 
2632  return STATUS_SUCCESS;
2633 }
2634 
2636  file_ref** pfileref, file_ref** pparfileref, PUNICODE_STRING fpus, PUNICODE_STRING stream, PIRP Irp,
2639  file_ref *fileref, *newpar, *parfileref;
2640  fcb* fcb;
2641  static const char xapref[] = "user.";
2642  static const WCHAR DOSATTRIB[] = L"DOSATTRIB";
2643  static const WCHAR EA[] = L"EA";
2644  static const WCHAR reparse[] = L"reparse";
2645  static const WCHAR casesensitive_str[] = L"casesensitive";
2647  BTRFS_TIME now;
2648  ULONG utf8len, overhead;
2649  NTSTATUS Status;
2650  KEY searchkey;
2651  traverse_ptr tp;
2652  dir_child* dc;
2653  dir_child* existing_dc = NULL;
2654  ACCESS_MASK granted_access;
2655 #ifdef DEBUG_FCB_REFCOUNTS
2656  LONG rc;
2657 #endif
2658 
2659  TRACE("fpus = %.*S\n", (int)(fpus->Length / sizeof(WCHAR)), fpus->Buffer);
2660  TRACE("stream = %.*S\n", (int)(stream->Length / sizeof(WCHAR)), stream->Buffer);
2661 
2662  parfileref = *pparfileref;
2663 
2664  if (parfileref->fcb == Vcb->dummy_fcb)
2665  return STATUS_ACCESS_DENIED;
2666 
2667  Status = check_file_name_valid(stream, false, true);
2668  if (!NT_SUCCESS(Status))
2669  return Status;
2670 
2671  Status = open_fileref(Vcb, &newpar, fpus, parfileref, false, NULL, NULL, PagedPool, case_sensitive, Irp);
2672 
2674  UNICODE_STRING fpus2;
2675 
2676  Status = check_file_name_valid(fpus, false, false);
2677  if (!NT_SUCCESS(Status))
2678  return Status;
2679 
2680  fpus2.Length = fpus2.MaximumLength = fpus->Length;
2682 
2683  if (!fpus2.Buffer) {
2684  ERR("out of memory\n");
2686  }
2687 
2688  RtlCopyMemory(fpus2.Buffer, fpus->Buffer, fpus2.Length);
2689 
2690  SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2691 
2692  if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
2695  &granted_access, &Status)) {
2696  SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2697  return Status;
2698  }
2699 
2700  SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2701 
2702  Status = file_create2(Irp, Vcb, &fpus2, parfileref, options, NULL, 0, &newpar, case_sensitive, rollback);
2703 
2704  if (!NT_SUCCESS(Status)) {
2705  ERR("file_create2 returned %08lx\n", Status);
2706  ExFreePool(fpus2.Buffer);
2707  return Status;
2708  } else if (Status != STATUS_OBJECT_NAME_COLLISION) {
2711  }
2712 
2713  ExFreePool(fpus2.Buffer);
2714  } else if (!NT_SUCCESS(Status)) {
2715  ERR("open_fileref returned %08lx\n", Status);
2716  return Status;
2717  }
2718 
2719  parfileref = newpar;
2720  *pparfileref = parfileref;
2721 
2722  if (parfileref->fcb->type != BTRFS_TYPE_FILE && parfileref->fcb->type != BTRFS_TYPE_SYMLINK && parfileref->fcb->type != BTRFS_TYPE_DIRECTORY) {
2723  WARN("parent not file, directory, or symlink\n");
2724  free_fileref(parfileref);
2725  return STATUS_INVALID_PARAMETER;
2726  }
2727 
2728  if (options & FILE_DIRECTORY_FILE) {
2729  WARN("tried to create directory as stream\n");
2730  free_fileref(parfileref);
2731  return STATUS_INVALID_PARAMETER;
2732  }
2733 
2735  free_fileref(parfileref);
2736  return STATUS_ACCESS_DENIED;
2737  }
2738 
2739  SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2740 
2741  if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
2743  &granted_access, &Status)) {
2744  SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2745  free_fileref(parfileref);
2746  return Status;
2747  }
2748 
2749  SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
2750 
2751  if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
2752  (stream->Length == sizeof(EA) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
2753  (stream->Length == sizeof(reparse) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length) ||
2754  (stream->Length == sizeof(casesensitive_str) - sizeof(WCHAR) && RtlCompareMemory(stream->Buffer, casesensitive_str, stream->Length) == stream->Length)) {
2755  free_fileref(parfileref);
2757  }
2758 
2760  if (!fcb) {
2761  ERR("out of memory\n");
2762  free_fileref(parfileref);
2764  }
2765 
2766  fcb->Vcb = Vcb;
2767 
2768  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
2769  fcb->Header.AllocationSize.QuadPart = 0;
2770  fcb->Header.FileSize.QuadPart = 0;
2771  fcb->Header.ValidDataLength.QuadPart = 0;
2772 
2773 #ifdef DEBUG_FCB_REFCOUNTS
2774  rc = InterlockedIncrement(&parfileref->fcb->refcount);
2775  WARN("fcb %p: refcount now %i\n", parfileref->fcb, rc);
2776 #else
2777  InterlockedIncrement(&parfileref->fcb->refcount);
2778 #endif
2779  fcb->subvol = parfileref->fcb->subvol;
2780  fcb->inode = parfileref->fcb->inode;
2781  fcb->hash = parfileref->fcb->hash;
2782  fcb->type = parfileref->fcb->type;
2783 
2784  fcb->ads = true;
2785 
2786  Status = utf16_to_utf8(NULL, 0, &utf8len, stream->Buffer, stream->Length);
2787  if (!NT_SUCCESS(Status)) {
2788  ERR("utf16_to_utf8 1 returned %08lx\n", Status);
2789  reap_fcb(fcb);
2790  free_fileref(parfileref);
2791  return Status;
2792  }
2793 
2794  fcb->adsxattr.Length = (uint16_t)utf8len + sizeof(xapref) - 1;
2797  if (!fcb->adsxattr.Buffer) {
2798  ERR("out of memory\n");
2799  reap_fcb(fcb);
2800  free_fileref(parfileref);
2802  }
2803 
2804  RtlCopyMemory(fcb->adsxattr.Buffer, xapref, sizeof(xapref) - 1);
2805 
2806  Status = utf16_to_utf8(&fcb->adsxattr.Buffer[sizeof(xapref) - 1], utf8len, &utf8len, stream->Buffer, stream->Length);
2807  if (!NT_SUCCESS(Status)) {
2808  ERR("utf16_to_utf8 2 returned %08lx\n", Status);
2809  reap_fcb(fcb);
2810  free_fileref(parfileref);
2811  return Status;
2812  }
2813 
2815 
2816  TRACE("adsxattr = %s\n", fcb->adsxattr.Buffer);
2817 
2819  TRACE("adshash = %08x\n", fcb->adshash);
2820 
2821  searchkey.obj_id = parfileref->fcb->inode;
2822  searchkey.obj_type = TYPE_XATTR_ITEM;
2823  searchkey.offset = fcb->adshash;
2824 
2825  Status = find_item(Vcb, parfileref->fcb->subvol, &tp, &searchkey, false, Irp);
2826  if (!NT_SUCCESS(Status)) {
2827  ERR("find_item returned %08lx\n", Status);
2828  reap_fcb(fcb);
2829  free_fileref(parfileref);
2830  return Status;
2831  }
2832 
2833  if (!keycmp(tp.item->key, searchkey))
2834  overhead = tp.item->size;
2835  else
2836  overhead = 0;
2837 
2838  fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
2839 
2840  if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) {
2841  WARN("not enough room for new DIR_ITEM (%Iu + %lu > %lu)", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
2842  reap_fcb(fcb);
2843  free_fileref(parfileref);
2844  return STATUS_DISK_FULL;
2845  } else
2846  fcb->adsmaxlen -= overhead + utf8len + sizeof(xapref) - 1;
2847 
2848  fcb->created = true;
2849  fcb->deleted = true;
2850 
2851  acquire_fcb_lock_exclusive(Vcb);
2852  InsertHeadList(&parfileref->fcb->list_entry, &fcb->list_entry); // insert in list after parent fcb
2853  InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all);
2854  parfileref->fcb->subvol->fcbs_version++;
2855  release_fcb_lock(Vcb);
2856 
2858 
2860  if (!fileref) {
2861  ERR("out of memory\n");
2862  free_fcb(fcb);
2863  free_fileref(parfileref);
2865  }
2866 
2867  fileref->fcb = fcb;
2868 
2870  if (!dc) {
2871  ERR("out of memory\n");
2873  free_fileref(parfileref);
2875  }
2876 
2877  RtlZeroMemory(dc, sizeof(dir_child));
2878 
2879  dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length + 1 - sizeof(xapref);
2880  dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength, ALLOC_TAG);
2881  if (!dc->utf8.Buffer) {
2882  ERR("out of memory\n");
2883  ExFreePool(dc);
2885  free_fileref(parfileref);
2887  }
2888 
2889  RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[sizeof(xapref) - 1], fcb->adsxattr.Length + 1 - sizeof(xapref));
2890 
2891  dc->name.MaximumLength = dc->name.Length = stream->Length;
2892  dc->name.Buffer = ExAllocatePoolWithTag(pool_type, dc->name.MaximumLength, ALLOC_TAG);
2893  if (!dc->name.Buffer) {
2894  ERR("out of memory\n");
2895  ExFreePool(dc->utf8.Buffer);
2896  ExFreePool(dc);
2898  free_fileref(parfileref);
2900  }
2901 
2902  RtlCopyMemory(dc->name.Buffer, stream->Buffer, stream->Length);
2903 
2904  Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
2905  if (!NT_SUCCESS(Status)) {
2906  ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
2907  ExFreePool(dc->utf8.Buffer);
2908  ExFreePool(dc->name.Buffer);
2909  ExFreePool(dc);
2911  free_fileref(parfileref);
2912  return Status;
2913  }
2914 
2917 
2918  ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true);
2919 
2920  LIST_ENTRY* le = parfileref->fcb->dir_children_index.Flink;
2921  while (le != &parfileref->fcb->dir_children_index) {
2922  dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_index);
2923 
2924  if (dc2->index == 0) {
2925  if ((case_sensitive && dc2->name.Length == dc->name.Length && RtlCompareMemory(dc2->name.Buffer, dc->name.Buffer, dc2->name.Length) == dc2->name.Length) ||
2926  (!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)
2927  ) {
2928  existing_dc = dc2;
2929  break;
2930  }
2931  } else
2932  break;
2933 
2934  le = le->Flink;
2935  }
2936 
2937  if (existing_dc) {
2938  ExFreePool(dc->utf8.Buffer);
2939  ExFreePool(dc->name.Buffer);
2940  ExFreePool(dc);
2942  free_fileref(parfileref);
2943 
2944  increase_fileref_refcount(existing_dc->fileref);
2945  *pfileref = existing_dc->fileref;
2946 
2948  }
2949 
2950  dc->fileref = fileref;
2951  fileref->dc = dc;
2952  fileref->parent = (struct _file_ref*)parfileref;
2953  fcb->deleted = false;
2954 
2955  InsertHeadList(&parfileref->fcb->dir_children_index, &dc->list_entry_index);
2956 
2957  InsertTailList(&parfileref->children, &fileref->list_entry);
2958 
2959  ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock);
2960 
2961  mark_fileref_dirty(fileref);
2962 
2963  parfileref->fcb->inode_item.transid = Vcb->superblock.generation;
2964  parfileref->fcb->inode_item.sequence++;
2965  parfileref->fcb->inode_item.st_ctime = now;
2966  parfileref->fcb->inode_item_changed = true;
2967 
2968  mark_fcb_dirty(parfileref->fcb);
2969 
2970  parfileref->fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
2971  parfileref->fcb->subvol->root_item.ctime = now;
2972 
2973  increase_fileref_refcount(parfileref);
2974 
2975  *pfileref = fileref;
2976 
2978 
2979  return STATUS_SUCCESS;
2980 }
2981 
2982 // LXSS programs can be distinguished by the fact they have a NULL PEB.
2983 #ifdef _AMD64_
2984 static __inline bool called_from_lxss() {
2985  NTSTATUS Status;
2987  ULONG retlen;
2988 
2990 
2991  if (!NT_SUCCESS(Status)) {
2992  ERR("ZwQueryInformationProcess returned %08lx\n", Status);
2993  return false;
2994  }
2995 
2996  return !pbi.PebBaseAddress;
2997 }
2998 #else
2999 #define called_from_lxss() false
3000 #endif
3001 
3003  PFILE_OBJECT FileObject, file_ref* related, bool loaded_related, PUNICODE_STRING fnus, ULONG disposition, ULONG options,
3004  file_ref** existing_fileref, LIST_ENTRY* rollback) {
3005  NTSTATUS Status;
3006  file_ref *fileref, *parfileref = NULL;
3007  ULONG i, j;
3008  ccb* ccb;
3009  static const WCHAR datasuf[] = {':','$','D','A','T','A',0};
3010  UNICODE_STRING dsus, fpus, stream;
3013  ECP_LIST* ecp_list;
3015 #ifdef DEBUG_FCB_REFCOUNTS
3016  LONG oc;
3017 #endif
3018 
3019  TRACE("(%p, %p, %p, %.*S, %lx, %lx)\n", Irp, Vcb, FileObject, (int)(fnus->Length / sizeof(WCHAR)), fnus->Buffer, disposition, options);
3020 
3021  if (Vcb->readonly)
3023 
3026  return STATUS_CANNOT_DELETE;
3027  }
3028 
3030  if (NT_SUCCESS(fFsRtlGetEcpListFromIrp(Irp, &ecp_list)) && ecp_list) {
3031  void* ctx = NULL;
3032  GUID type;
3033  ULONG ctxsize;
3034 
3035  do {
3036  Status = fFsRtlGetNextExtraCreateParameter(ecp_list, ctx, &type, &ctx, &ctxsize);
3037 
3038  if (NT_SUCCESS(Status)) {
3039  if (RtlCompareMemory(&type, &GUID_ECP_ATOMIC_CREATE, sizeof(GUID)) == sizeof(GUID)) {
3040  if (ctxsize >= sizeof(ATOMIC_CREATE_ECP_CONTEXT))
3041  acec = ctx;
3042  else {
3043  ERR("GUID_ECP_ATOMIC_CREATE context was too short: %lu bytes, expected %Iu\n", ctxsize,
3044  sizeof(ATOMIC_CREATE_ECP_CONTEXT));
3045  }
3046  } else if (RtlCompareMemory(&type, &GUID_ECP_QUERY_ON_CREATE, sizeof(GUID)) == sizeof(GUID))
3047  WARN("unhandled ECP GUID_ECP_QUERY_ON_CREATE\n");
3048  else if (RtlCompareMemory(&type, &GUID_ECP_CREATE_REDIRECTION, sizeof(GUID)) == sizeof(GUID))
3049  WARN("unhandled ECP GUID_ECP_CREATE_REDIRECTION\n");
3050  else {
3051  WARN("unhandled ECP {%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", type.Data1, type.Data2,
3052  type.Data3, type.Data4[0], type.Data4[1], type.Data4[2], type.Data4[3], type.Data4[4], type.Data4[5],
3053  type.Data4[6], type.Data4[7]);
3054  }
3055  }
3056  } while (NT_SUCCESS(Status));
3057  }
3058  }
3059 
3060  dsus.Buffer = (WCHAR*)datasuf;
3061  dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
3062  fpus.Buffer = NULL;
3063 
3064  if (!loaded_related) {
3065  Status = open_fileref(Vcb, &parfileref, fnus, related, true, NULL, NULL, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, Irp);
3066 
3067  if (!NT_SUCCESS(Status))
3068  goto end;
3069  } else
3070  parfileref = related;
3071 
3072  if (parfileref->fcb->type != BTRFS_TYPE_DIRECTORY && (fnus->Length < sizeof(WCHAR) || fnus->Buffer[0] != ':')) {
3074  goto end;
3075  }
3076 
3077  if (is_subvol_readonly(parfileref->fcb->subvol, Irp)) {
3079  goto end;
3080  }
3081 
3082  i = (fnus->Length / sizeof(WCHAR))-1;
3083  while ((fnus->Buffer[i] == '\\' || fnus->Buffer[i] == '/') && i > 0) { i--; }
3084 
3085  j = i;
3086 
3087  while (i > 0 && fnus->Buffer[i-1] != '\\' && fnus->Buffer[i-1] != '/') { i--; }
3088 
3089  fpus.MaximumLength = (USHORT)((j - i + 2) * sizeof(WCHAR));
3090  fpus.Buffer = ExAllocatePoolWithTag(pool_type, fpus.MaximumLength, ALLOC_TAG);
3091  if (!fpus.Buffer) {
3092  ERR("out of memory\n");
3094  goto end;
3095  }
3096 
3097  fpus.Length = (USHORT)((j - i + 1) * sizeof(WCHAR));
3098 
3099  RtlCopyMemory(fpus.Buffer, &fnus->Buffer[i], (j - i + 1) * sizeof(WCHAR));
3100  fpus.Buffer[j - i + 1] = 0;
3101 
3102  if (fpus.Length > dsus.Length) { // check for :$DATA suffix
3103  UNICODE_STRING lb;
3104 
3105  lb.Buffer = &fpus.Buffer[(fpus.Length - dsus.Length)/sizeof(WCHAR)];
3106  lb.Length = lb.MaximumLength = dsus.Length;
3107 
3108  TRACE("lb = %.*S\n", (int)(lb.Length/sizeof(WCHAR)), lb.Buffer);
3109 
3110  if (FsRtlAreNamesEqual(&dsus, &lb, true, NULL)) {
3111  TRACE("ignoring :$DATA suffix\n");
3112 
3113  fpus.Length -= lb.Length;
3114 
3115  if (fpus.Length > sizeof(WCHAR) && fpus.Buffer[(fpus.Length-1)/sizeof(WCHAR)] == ':')
3116  fpus.Length -= sizeof(WCHAR);
3117 
3118  TRACE("fpus = %.*S\n", (int)(fpus.Length / sizeof(WCHAR)), fpus.Buffer);
3119  }
3120  }
3121 
3122  stream.Length = 0;
3123 
3124  for (i = 0; i < fpus.Length / sizeof(WCHAR); i++) {
3125  if (fpus.Buffer[i] == ':') {
3126  stream.Length = (USHORT)(fpus.Length - (i * sizeof(WCHAR)) - sizeof(WCHAR));
3127  stream.Buffer = &fpus.Buffer[i+1];
3128  fpus.Buffer[i] = 0;
3129  fpus.Length = (USHORT)(i * sizeof(WCHAR));
3130  break;
3131  }
3132  }
3133 
3134  if (stream.Length > 0) {
3135  Status = create_stream(Vcb, &fileref, &parfileref, &fpus, &stream, Irp, options, pool_type, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
3136  if (!NT_SUCCESS(Status)) {
3137  ERR("create_stream returned %08lx\n", Status);
3138  goto end;
3139  }
3140 
3141  IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess,
3142  FileObject, &fileref->fcb->share_access);
3143  } else {
3144  ACCESS_MASK granted_access;
3145 
3146  Status = check_file_name_valid(&fpus, false, false);
3147  if (!NT_SUCCESS(Status))
3148  goto end;
3149 
3150  SeLockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3151 
3152  if (!SeAccessCheck(parfileref->fcb->sd, &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext,
3155  &granted_access, &Status)) {
3156  SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3157  goto end;
3158  }
3159 
3160  SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
3161 
3162  if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
3163  ULONG offset;
3164 
3165  Status = IoCheckEaBufferValidity(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, &offset);
3166  if (!NT_SUCCESS(Status)) {
3167  ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
3168  goto end;
3169  }
3170  }
3171 
3172  Status = file_create2(Irp, Vcb, &fpus, parfileref, options, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength,
3173  &fileref, IrpSp->Flags & SL_CASE_SENSITIVE, rollback);
3174 
3176  *existing_fileref = fileref;
3177  goto end;
3178  } else if (!NT_SUCCESS(Status)) {
3179  ERR("file_create2 returned %08lx\n", Status);
3180  goto end;
3181  }
3182 
3183  IoSetShareAccess(IrpSp->Parameters.Create.SecurityContext->DesiredAccess, IrpSp->Parameters.Create.ShareAccess, FileObject, &fileref->fcb->share_access);
3184 
3187  }
3188 
3189  FileObject->FsContext = fileref->fcb;
3190 
3192  if (!ccb) {
3193  ERR("out of memory\n");
3195  fileref->deleted = true;
3196  fileref->fcb->deleted = true;
3197 
3198  if (stream.Length == 0) {
3199  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
3200  parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2;
3201  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
3202  }
3203 
3204  free_fileref(fileref);
3205  goto end;
3206  }
3207 
3208  RtlZeroMemory(ccb, sizeof(*ccb));
3209 
3210  ccb->fileref = fileref;
3211 
3213  ccb->NodeSize = sizeof(*ccb);
3214  ccb->disposition = disposition;
3215  ccb->options = options;
3216  ccb->query_dir_offset = 0;
3218  ccb->has_wildcard = false;
3219  ccb->specific_file = false;
3220  ccb->access = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
3222  ccb->reserving = false;
3223  ccb->lxss = called_from_lxss();
3224 
3225 #ifdef DEBUG_FCB_REFCOUNTS
3226  oc = InterlockedIncrement(&fileref->open_count);
3227  ERR("fileref %p: open_count now %i\n", fileref, oc);
3228 #else
3229  InterlockedIncrement(&fileref->open_count);
3230 #endif
3231  InterlockedIncrement(&Vcb->open_files);
3232 
3233  FileObject->FsContext2 = ccb;
3234 
3235  FileObject->SectionObjectPointer = &fileref->fcb->nonpaged->segment_object;
3236 
3237  // FIXME - ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT
3239  if (acec->ReparseBufferLength > sizeof(uint32_t) && *(uint32_t*)acec->ReparseBuffer == IO_REPARSE_TAG_SYMLINK) {
3241  fileref->fcb->type = BTRFS_TYPE_FILE;
3242  fileref->fcb->atts &= ~FILE_ATTRIBUTE_DIRECTORY;
3243  }
3244 
3245  if (fileref->fcb->type == BTRFS_TYPE_SOCKET || fileref->fcb->type == BTRFS_TYPE_FIFO ||
3246  fileref->fcb->type == BTRFS_TYPE_CHARDEV || fileref->fcb->type == BTRFS_TYPE_BLOCKDEV) {
3247  // NOP. If called from LXSS, humour it - we hardcode the values elsewhere.
3248  } else {
3250  if (!NT_SUCCESS(Status)) {
3251  ERR("set_reparse_point2 returned %08lx\n", Status);
3252  fileref->deleted = true;
3253  fileref->fcb->deleted = true;
3254 
3255  if (stream.Length == 0) {
3256  ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, true);
3257  parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length * 2;
3258  ExReleaseResourceLite(parfileref->fcb->Header.Resource);
3259  }
3260 
3261  free_fileref(fileref);
3262  return Status;
3263  }
3264  }
3265 
3267  }
3268 
3273  fileref->fcb->case_sensitive = true;
3274  ccb->case_sensitive = true;
3275  }
3276 
3278  }
3279 
3281  }
3282 
3283  fileref->dc->type = fileref->fcb->type;
3284 
3285 end:
3286  if (fpus.Buffer)
3287  ExFreePool(fpus.Buffer);
3288 
3289  if (parfileref && !loaded_related)
3290  free_fileref(parfileref);
3291 
3292  return Status;
3293 }
3294 
3295 static __inline void debug_create_options(ULONG RequestedOptions) {
3296  if (RequestedOptions != 0) {
3297  ULONG options = RequestedOptions;
3298 
3299  TRACE("requested options:\n");
3300 
3301  if (options & FILE_DIRECTORY_FILE) {
3302  TRACE(" FILE_DIRECTORY_FILE\n");
3304  }
3305 
3306  if (options & FILE_WRITE_THROUGH) {
3307  TRACE(" FILE_WRITE_THROUGH\n");
3309  }
3310 
3311  if (options & FILE_SEQUENTIAL_ONLY) {
3312  TRACE(" FILE_SEQUENTIAL_ONLY\n");
3314  }
3315 
3317  TRACE(" FILE_NO_INTERMEDIATE_BUFFERING\n");
3319  }
3320 
3322  TRACE(" FILE_SYNCHRONOUS_IO_ALERT\n");
3324  }
3325 
3327  TRACE(" FILE_SYNCHRONOUS_IO_NONALERT\n");
3329  }
3330 
3332  TRACE(" FILE_NON_DIRECTORY_FILE\n");
3334  }
3335 
3337  TRACE(" FILE_CREATE_TREE_CONNECTION\n");
3339  }
3340 
3342  TRACE(" FILE_COMPLETE_IF_OPLOCKED\n");
3344  }
3345 
3346  if (options & FILE_NO_EA_KNOWLEDGE) {
3347  TRACE(" FILE_NO_EA_KNOWLEDGE\n");
3349  }
3350 
3352  TRACE(" FILE_OPEN_REMOTE_INSTANCE\n");
3354  }
3355 
3356  if (options & FILE_RANDOM_ACCESS) {
3357  TRACE(" FILE_RANDOM_ACCESS\n");
3359  }
3360 
3361  if (options & FILE_DELETE_ON_CLOSE) {
3362  TRACE(" FILE_DELETE_ON_CLOSE\n");
3364  }
3365 
3366  if (options & FILE_OPEN_BY_FILE_ID) {
3367  TRACE(" FILE_OPEN_BY_FILE_ID\n");
3369  }
3370 
3372  TRACE(" FILE_OPEN_FOR_BACKUP_INTENT\n");
3374  }
3375 
3376  if (options & FILE_NO_COMPRESSION) {
3377  TRACE(" FILE_NO_COMPRESSION\n");
3379  }
3380 
3381 #if NTDDI_VERSION >= NTDDI_WIN7
3383  TRACE(" FILE_OPEN_REQUIRING_OPLOCK\n");
3385  }
3386 
3388  TRACE(" FILE_DISALLOW_EXCLUSIVE\n");
3390  }
3391 #endif
3392 
3394  TRACE(" FILE_RESERVE_OPFILTER\n");
3396  }
3397 
3399  TRACE(" FILE_OPEN_REPARSE_POINT\n");
3401  }
3402 
3403  if (options & FILE_OPEN_NO_RECALL) {
3404  TRACE(" FILE_OPEN_NO_RECALL\n");
3406  }
3407 
3409  TRACE(" FILE_OPEN_FOR_FREE_SPACE_QUERY\n");
3411  }
3412 
3413  if (options)
3414  TRACE(" unknown options: %lx\n", options);
3415  } else {
3416  TRACE("requested options: (none)\n");
3417  }
3418 }
3419 
3421  NTSTATUS Status;
3422 
3424  ULONG size, bytes_read, i;
3425 
3426  if (fcb->type == BTRFS_TYPE_FILE && fcb->inode_item.st_size < sizeof(ULONG)) {
3427  WARN("file was too short to be a reparse point\n");
3428  return STATUS_INVALID_PARAMETER;
3429  }
3430 
3431  // 0x10007 = 0xffff (maximum length of data buffer) + 8 bytes header
3432  size = (ULONG)min(0x10007, fcb->inode_item.st_size);
3433 
3434  if (size == 0)
3435  return STATUS_INVALID_PARAMETER;
3436 
3438  if (!*data) {
3439  ERR("out of memory\n");
3441  }
3442 
3443  Status = read_file(fcb, *data, 0, size, &bytes_read, NULL);
3444  if (!NT_SUCCESS(Status)) {
3445  ERR("read_file_fcb returned %08lx\n", Status);
3446  ExFreePool(*data);
3447  return Status;
3448  }
3449 
3450  if (fcb->type == BTRFS_TYPE_SYMLINK) {
3451  ULONG stringlen, reqlen;
3452  uint16_t subnamelen, printnamelen;
3453  REPARSE_DATA_BUFFER* rdb;
3454 
3455  Status = utf8_to_utf16(NULL, 0, &stringlen, (char*)*data, bytes_read);
3456  if (!NT_SUCCESS(Status)) {
3457  ERR("utf8_to_utf16 1 returned %08lx\n", Status);
3458  ExFreePool(*data);
3459  return Status;
3460  }
3461 
3462  subnamelen = printnamelen = (USHORT)stringlen;
3463 
3464  reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
3465 
3466  rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
3467 
3468  if (!rdb) {
3469  ERR("out of memory\n");
3470  ExFreePool(*data);
3472  }
3473 
3475  rdb->ReparseDataLength = (USHORT)(reqlen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
3476  rdb->Reserved = 0;
3477 
3478  rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
3479  rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = subnamelen;
3480  rdb->SymbolicLinkReparseBuffer.PrintNameOffset = subnamelen;
3481  rdb->SymbolicLinkReparseBuffer.PrintNameLength = printnamelen;
3483 
3484  Status = utf8_to_utf16(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
3485  stringlen, &stringlen, (char*)*data, size);
3486 
3487  if (!NT_SUCCESS(Status)) {
3488  ERR("utf8_to_utf16 2 returned %08lx\n", Status);
3489  ExFreePool(rdb);
3490  ExFreePool(*data);
3491  return Status;
3492  }
3493 
3494  for (i = 0; i < stringlen / sizeof(WCHAR); i++) {
3495  if (rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] == '/')
3496  rdb->SymbolicLinkReparseBuffer.PathBuffer[(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)) + i] = '\\';
3497  }
3498 
3499  RtlCopyMemory(&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)],
3500  &rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
3501  rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
3502 
3503  ExFreePool(*data);
3504 
3505  *data = (uint8_t*)rdb;
3506  } else {
3508  if (!NT_SUCCESS(Status)) {
3509  ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status);
3510  ExFreePool(*data);
3511  return Status;
3512  }
3513  }
3514  } else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
3516  return STATUS_INTERNAL_ERROR;
3517 
3518  if (fcb->reparse_xattr.Length < sizeof(ULONG)) {
3519  WARN("xattr was too short to be a reparse point\n");
3520  return STATUS_INTERNAL_ERROR;
3521  }
3522 
3524  if (!NT_SUCCESS(Status)) {
3525  ERR("FsRtlValidateReparsePointBuffer returned %08lx\n", Status);
3526  return Status;
3527  }
3528 
3530  if (!*data) {
3531  ERR("out of memory\n");
3533  }
3534 
3536  } else
3537  return STATUS_INVALID_PARAMETER;
3538 
3539  return STATUS_SUCCESS;
3540 }
3541 
3543  LIST_ENTRY* le;
3544  NTSTATUS Status;
3545 
3546  if (fcb->csum_loaded)
3547  return;
3548 
3550  goto end;
3551 
3552  le = fcb->extents.Flink;
3553  while (le != &fcb->extents) {
3555 
3556  if (!ext->ignore && ext->extent_data.type == EXTENT_TYPE_REGULAR) {
3557  EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0];
3558  uint64_t len;
3559 
3560  len = (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->num_bytes : ed2->size) >> Vcb->sector_shift;
3561 
3562  ext->csum = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(len * Vcb->csum_size), ALLOC_TAG);
3563  if (!ext->csum) {
3564  ERR("out of memory\n");
3565  goto end;
3566  }
3567 
3568  Status = load_csum(Vcb, ext->csum, ed2->address + (ext->extent_data.compression == BTRFS_COMPRESSION_NONE ? ed2->offset : 0), len, Irp);
3569 
3570  if (!NT_SUCCESS(Status)) {
3571  ERR("load_csum returned %08lx\n", Status);
3572  goto end;
3573  }
3574  }
3575 
3576  le = le->Flink;
3577  }
3578 
3579 end:
3580  fcb->csum_loaded = true;
3581 }
3582 
3584  NTSTATUS Status;
3587  ULONG RequestedDisposition = ((IrpSp->Parameters.Create.Options >> 24) & 0xff);
3590  ccb* ccb;
3591 
3592  if (granted_access & FILE_WRITE_DATA || options & FILE_DELETE_ON_CLOSE) {
3593  if (!MmFlushImageSection(&fileref->fcb->nonpaged->segment_object, MmFlushForWrite))
3595  }
3596 
3597  if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) {
3598  ULONG defda, oldatts, filter;
3600  BTRFS_TIME now;
3601 
3602  if (!fileref->fcb->ads && (IrpSp->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != ((fileref->fcb->atts & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN))))
3603  return STATUS_ACCESS_DENIED;
3604 
3605  if (fileref->fcb->ads) {
3606  Status = stream_set_end_of_file_information(Vcb, 0, fileref->fcb, fileref, false);
3607  if (!NT_SUCCESS(Status)) {
3608  ERR("stream_set_end_of_file_information returned %08lx\n", Status);
3609  return Status;
3610  }
3611  } else {
3612  Status = truncate_file(fileref->fcb, 0, Irp, rollback);
3613  if (!NT_SUCCESS(Status)) {
3614  ERR("truncate_file returned %08lx\n", Status);
3615  return Status;
3616  }
3617  }
3618 
3619  if (Irp->Overlay.AllocationSize.QuadPart > 0) {
3620  Status = extend_file(fileref->fcb, fileref, Irp->Overlay.AllocationSize.QuadPart, true, NULL, rollback);
3621 
3622  if (!NT_SUCCESS(Status)) {
3623  ERR("extend_file returned %08lx\n", Status);
3624  return Status;
3625  }
3626  }
3627 
3628  if (!fileref->fcb->ads) {
3629  LIST_ENTRY* le;
3630 
3631  if (Irp->AssociatedIrp.SystemBuffer && IrpSp->Parameters.Create.EaLength > 0) {
3632  ULONG offset;
3633