ReactOS  0.4.15-dev-5142-g967f5b9
btrfs.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 #ifdef _DEBUG
19 #define DEBUG
20 #endif
21 
22 #include "btrfs_drv.h"
23 #include "xxhash.h"
24 #include "crc32c.h"
25 #ifndef __REACTOS__
26 #ifndef _MSC_VER
27 #include <cpuid.h>
28 #else
29 #include <intrin.h>
30 #endif
31 #endif // __REACTOS__
32 #include <ntddscsi.h>
33 #include "btrfs.h"
34 #include <ata.h>
35 
36 #ifndef _MSC_VER
37 #include <initguid.h>
38 #include <ntddstor.h>
39 #undef INITGUID
40 #endif
41 
42 #include <ntdddisk.h>
43 #include <ntddvol.h>
44 
45 #ifdef _MSC_VER
46 #include <initguid.h>
47 #include <ntddstor.h>
48 #undef INITGUID
49 #endif
50 
51 #include <ntstrsafe.h>
52 
53 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
54  BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
55  BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
56  BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD | BTRFS_INCOMPAT_FLAGS_METADATA_UUID | BTRFS_INCOMPAT_FLAGS_RAID1C34)
57 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID | \
58  BTRFS_COMPAT_RO_FLAGS_VERITY)
59 
60 static const WCHAR device_name[] = {'\\','B','t','r','f','s',0};
61 static const WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
62 
63 DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
64 
88 bool log_started = false;
101 bool diskacc = false;
105 bool finished_probing = false;
107 bool degraded_wait = true;
109 bool shutting_down = false;
112 extern uint64_t boot_subvol;
113 
114 #ifdef _DEBUG
115 PFILE_OBJECT comfo = NULL;
116 PDEVICE_OBJECT comdo = NULL;
117 HANDLE log_handle = NULL;
118 ERESOURCE log_lock;
119 HANDLE serial_thread_handle = NULL;
120 
121 static void init_serial(bool first_time);
122 #endif
123 
125 static void __stdcall do_xor_basic(uint8_t* buf1, uint8_t* buf2, uint32_t len);
126 
128 
129 typedef struct {
132 } read_context;
133 
134 // no longer in Windows headers??
136 
137 #ifdef _DEBUG
138 _Function_class_(IO_COMPLETION_ROUTINE)
139 static NTSTATUS __stdcall dbg_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
140  read_context* context = conptr;
141 
143 
144  context->iosb = Irp->IoStatus;
145  KeSetEvent(&context->Event, 0, false);
146 
148 }
149 
150 #define DEBUG_MESSAGE_LEN 1024
151 
152 #ifdef DEBUG_LONG_MESSAGES
153 void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) {
154 #else
155 void _debug_message(_In_ const char* func, _In_ char* s, ...) {
156 #endif
160  PIRP Irp;
161  va_list ap;
162  char *buf2, *buf;
165 
166  buf2 = ExAllocatePoolWithTag(NonPagedPool, DEBUG_MESSAGE_LEN, ALLOC_TAG);
167 
168  if (!buf2) {
169  DbgPrint("Couldn't allocate buffer in debug_message\n");
170  return;
171  }
172 
173 #ifdef DEBUG_LONG_MESSAGES
174  sprintf(buf2, "%p:%s:%s:%u:", (void*)PsGetCurrentThread(), func, file, line);
175 #else
176  sprintf(buf2, "%p:%s:", (void*)PsGetCurrentThread(), func);
177 #endif
178  buf = &buf2[strlen(buf2)];
179 
180  va_start(ap, s);
181 
182  RtlStringCbVPrintfA(buf, DEBUG_MESSAGE_LEN - strlen(buf2), s, ap);
183 
184  ExAcquireResourceSharedLite(&log_lock, true);
185 
186  if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
187  DbgPrint(buf2);
188  } else if (log_device.Length > 0) {
189  if (!comdo) {
190  DbgPrint(buf2);
191  goto exit2;
192  }
193 
194  length = (uint32_t)strlen(buf2);
195 
196  offset.u.LowPart = 0;
197  offset.u.HighPart = 0;
198 
200 
202 
203  Irp = IoAllocateIrp(comdo->StackSize, false);
204 
205  if (!Irp) {
206  DbgPrint("IoAllocateIrp failed\n");
207  goto exit2;
208  }
209 
212  IrpSp->FileObject = comfo;
213 
214  if (comdo->Flags & DO_BUFFERED_IO) {
215  Irp->AssociatedIrp.SystemBuffer = buf2;
216 
217  Irp->Flags = IRP_BUFFERED_IO;
218  } else if (comdo->Flags & DO_DIRECT_IO) {
219  Irp->MdlAddress = IoAllocateMdl(buf2, length, false, false, NULL);
220  if (!Irp->MdlAddress) {
221  DbgPrint("IoAllocateMdl failed\n");
222  goto exit;
223  }
224 
225  MmBuildMdlForNonPagedPool(Irp->MdlAddress);
226  } else {
227  Irp->UserBuffer = buf2;
228  }
229 
230  IrpSp->Parameters.Write.Length = length;
231  IrpSp->Parameters.Write.ByteOffset = offset;
232 
233  Irp->UserIosb = &context.iosb;
234 
235  Irp->UserEvent = &context.Event;
236 
237  IoSetCompletionRoutine(Irp, dbg_completion, &context, true, true, true);
238 
239  Status = IoCallDriver(comdo, Irp);
240 
241  if (Status == STATUS_PENDING) {
243  Status = context.iosb.Status;
244  }
245 
246  if (comdo->Flags & DO_DIRECT_IO)
247  IoFreeMdl(Irp->MdlAddress);
248 
249  if (!NT_SUCCESS(Status)) {
250  DbgPrint("failed to write to COM1 - error %08lx\n", Status);
251  goto exit;
252  }
253 
254 exit:
255  IoFreeIrp(Irp);
256  } else if (log_handle != NULL) {
258 
259  length = (uint32_t)strlen(buf2);
260 
261  Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
262 
263  if (!NT_SUCCESS(Status)) {
264  DbgPrint("failed to write to file - error %08lx\n", Status);
265  }
266  }
267 
268 exit2:
269  ExReleaseResourceLite(&log_lock);
270 
271  va_end(ap);
272 
273  if (buf2)
274  ExFreePool(buf2);
275 }
276 #endif
277 
279  if (!IoGetTopLevelIrp()) {
281  return true;
282  }
283 
284  return false;
285 }
286 
287 static void __stdcall do_xor_basic(uint8_t* buf1, uint8_t* buf2, uint32_t len) {
288  uint32_t j;
289 
290 #if defined(_ARM_) || defined(_ARM64_)
291  uint64x2_t x1, x2;
292 
293  if (((uintptr_t)buf1 & 0xf) == 0 && ((uintptr_t)buf2 & 0xf) == 0) {
294  while (len >= 16) {
295  x1 = vld1q_u64((const uint64_t*)buf1);
296  x2 = vld1q_u64((const uint64_t*)buf2);
297  x1 = veorq_u64(x1, x2);
298  vst1q_u64((uint64_t*)buf1, x1);
299 
300  buf1 += 16;
301  buf2 += 16;
302  len -= 16;
303  }
304  }
305 #endif
306 
307 #if defined(_AMD64_) || defined(_ARM64_)
308  while (len > 8) {
309  *(uint64_t*)buf1 ^= *(uint64_t*)buf2;
310  buf1 += 8;
311  buf2 += 8;
312  len -= 8;
313  }
314 #endif
315 
316  while (len > 4) {
317  *(uint32_t*)buf1 ^= *(uint32_t*)buf2;
318  buf1 += 4;
319  buf2 += 4;
320  len -= 4;
321  }
322 
323  for (j = 0; j < len; j++) {
324  *buf1 ^= *buf2;
325  buf1++;
326  buf2++;
327  }
328 }
329 
330 _Function_class_(DRIVER_UNLOAD)
332  UNICODE_STRING dosdevice_nameW;
333 
334  TRACE("(%p)\n", DriverObject);
335 
336  dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
337  dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) - sizeof(WCHAR);
338 
339  IoDeleteSymbolicLink(&dosdevice_nameW);
340  IoDeleteDevice(DriverObject->DeviceObject);
341 
342  while (!IsListEmpty(&uid_map_list)) {
344  uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
345 
346  ExFreePool(um->sid);
347 
348  ExFreePool(um);
349  }
350 
351  while (!IsListEmpty(&gid_map_list)) {
353 
354  ExFreePool(gm->sid);
355  ExFreePool(gm);
356  }
357 
358  // FIXME - free volumes and their devpaths
359 
360 #ifdef _DEBUG
361  if (comfo)
362  ObDereferenceObject(comfo);
363 
364  if (log_handle)
365  ZwClose(log_handle);
366 #endif
367 
370 
371  if (log_device.Buffer)
373 
374  if (log_file.Buffer)
376 
377  if (registry_path.Buffer)
379 
380 #ifdef _DEBUG
381  ExDeleteResourceLite(&log_lock);
382 #endif
384 }
385 
387  KEY searchkey;
388  traverse_ptr tp, prev_tp;
390 
391  // get last entry
392  searchkey.obj_id = 0xffffffffffffffff;
393  searchkey.obj_type = 0xff;
394  searchkey.offset = 0xffffffffffffffff;
395 
396  Status = find_item(Vcb, r, &tp, &searchkey, false, Irp);
397  if (!NT_SUCCESS(Status)) {
398  ERR("error - find_item returned %08lx\n", Status);
399  return false;
400  }
401 
403  r->lastinode = tp.item->key.obj_id;
404  TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
405  return true;
406  }
407 
408  while (find_prev_item(Vcb, &tp, &prev_tp, Irp)) {
409  tp = prev_tp;
410 
411  TRACE("moving on to %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
412 
414  r->lastinode = tp.item->key.obj_id;
415  TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
416  return true;
417  }
418  }
419 
420  r->lastinode = SUBVOL_ROOT_INODE;
421 
422  WARN("no INODE_ITEMs in tree %I64x\n", r->id);
423 
424  return true;
425 }
426 
427 _Success_(return)
428 static bool extract_xattr(_In_reads_bytes_(size) void* item, _In_ USHORT size, _In_z_ char* name, _Out_ uint8_t** data, _Out_ uint16_t* datalen) {
429  DIR_ITEM* xa = (DIR_ITEM*)item;
430  USHORT xasize;
431 
432  while (true) {
433  if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
434  WARN("DIR_ITEM is truncated\n");
435  return false;
436  }
437 
438  if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
439  TRACE("found xattr %s\n", name);
440 
441  *datalen = xa->m;
442 
443  if (xa->m > 0) {
445  if (!*data) {
446  ERR("out of memory\n");
447  return false;
448  }
449 
450  RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
451  } else
452  *data = NULL;
453 
454  return true;
455  }
456 
457  xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
458 
459  if (size > xasize) {
460  size -= xasize;
461  xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
462  } else
463  break;
464  }
465 
466  TRACE("xattr %s not found\n", name);
467 
468  return false;
469 }
470 
471 _Success_(return)
472 bool get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* subvol, _In_ uint64_t inode, _In_z_ char* name, _In_ uint32_t crc32,
474  KEY searchkey;
477 
478  TRACE("(%p, %I64x, %I64x, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
479 
480  searchkey.obj_id = inode;
481  searchkey.obj_type = TYPE_XATTR_ITEM;
482  searchkey.offset = crc32;
483 
484  Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
485  if (!NT_SUCCESS(Status)) {
486  ERR("error - find_item returned %08lx\n", Status);
487  return false;
488  }
489 
490  if (keycmp(tp.item->key, searchkey)) {
491  TRACE("could not find item (%I64x,%x,%I64x)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
492  return false;
493  }
494 
495  if (tp.item->size < sizeof(DIR_ITEM)) {
496  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, sizeof(DIR_ITEM));
497  return false;
498  }
499 
500  return extract_xattr(tp.item->data, tp.item->size, name, data, datalen);
501 }
502 
508  device_extension* Vcb = DeviceObject->DeviceExtension;
509  bool top_level;
510 
512 
513  TRACE("close\n");
514 
515  top_level = is_top_level(Irp);
516 
517  if (DeviceObject == master_devobj) {
518  TRACE("Closing file system\n");
520  goto end;
521  } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
523  goto end;
524  } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
526  goto end;
527  }
528 
530 
531  // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
532 
534 
535 end:
536  Irp->IoStatus.Status = Status;
537  Irp->IoStatus.Information = 0;
538 
540 
541  if (top_level)
543 
544  TRACE("returning %08lx\n", Status);
545 
547 
548  return Status;
549 }
550 
553 static NTSTATUS __stdcall drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
557  fcb* fcb = FileObject->FsContext;
558  device_extension* Vcb = DeviceObject->DeviceExtension;
559  bool top_level;
560 
562 
563  TRACE("flush buffers\n");
564 
565  top_level = is_top_level(Irp);
566 
567  if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
569  goto end;
570  } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
572  goto end;
573  }
574 
575  if (!fcb) {
576  ERR("fcb was NULL\n");
578  goto end;
579  }
580 
581  if (fcb == Vcb->volume_fcb) {
583  goto end;
584  }
585 
587 
588  Irp->IoStatus.Information = 0;
589 
590  fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
591 
593  Irp->IoStatus.Status = Status;
594 
595  if (fcb->type != BTRFS_TYPE_DIRECTORY) {
596  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &Irp->IoStatus);
597 
598  if (fcb->Header.PagingIoResource) {
599  ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
600  ExReleaseResourceLite(fcb->Header.PagingIoResource);
601  }
602 
603  Status = Irp->IoStatus.Status;
604  }
605 
606 end:
608 
609  TRACE("returning %08lx\n", Status);
610 
611  if (top_level)
613 
615 
616  return Status;
617 }
618 
620  uint64_t nfactor, dfactor, sectors_used;
621 
622  if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) {
623  nfactor = 1;
624  dfactor = 2;
625  } else if (Vcb->data_flags & BLOCK_FLAG_RAID5) {
626  nfactor = Vcb->superblock.num_devices - 1;
627  dfactor = Vcb->superblock.num_devices;
628  } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
629  nfactor = Vcb->superblock.num_devices - 2;
630  dfactor = Vcb->superblock.num_devices;
631  } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C3) {
632  nfactor = 1;
633  dfactor = 3;
634  } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C4) {
635  nfactor = 1;
636  dfactor = 4;
637  } else {
638  nfactor = 1;
639  dfactor = 1;
640  }
641 
642  sectors_used = (Vcb->superblock.bytes_used >> Vcb->sector_shift) * nfactor / dfactor;
643 
644  *totalsize = (Vcb->superblock.total_bytes >> Vcb->sector_shift) * nfactor / dfactor;
645  *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
646 }
647 
648 #ifndef __REACTOS__
649 // simplified version of FsRtlAreNamesEqual, which can be a bottleneck!
650 static bool compare_strings(const UNICODE_STRING* us1, const UNICODE_STRING* us2) {
651  if (us1->Length != us2->Length)
652  return false;
653 
654  WCHAR* s1 = us1->Buffer;
655  WCHAR* s2 = us2->Buffer;
656 
657  for (unsigned int i = 0; i < us1->Length; i++) {
658  WCHAR c1 = *s1;
659  WCHAR c2 = *s2;
660 
661  if (c1 != c2) {
662  if (c1 >= 'a' && c1 <= 'z')
663  c1 = c1 - 'a' + 'A';
664 
665  if (c2 >= 'a' && c2 <= 'z')
666  c2 = c2 - 'a' + 'A';
667 
668  if (c1 != c2)
669  return false;
670  }
671 
672  s1++;
673  s2++;
674  }
675 
676  return true;
677 }
678 
679 #define INIT_UNICODE_STRING(var, val) UNICODE_STRING us##var; us##var.Buffer = (WCHAR*)val; us##var.Length = us##var.MaximumLength = sizeof(val) - sizeof(WCHAR);
680 
681 // This function exists because we have to lie about our FS type in certain situations.
682 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
683 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
684 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
685 // blacklist cmd.exe too.
686 
687 static bool lie_about_fs_type() {
690  PPEB peb;
691  LIST_ENTRY* le;
692  ULONG retlen;
693 #ifdef _AMD64_
694  ULONG_PTR wow64info;
695 #endif
696 
697  INIT_UNICODE_STRING(mpr, L"MPR.DLL");
698  INIT_UNICODE_STRING(cmd, L"CMD.EXE");
699  INIT_UNICODE_STRING(fsutil, L"FSUTIL.EXE");
700  INIT_UNICODE_STRING(storsvc, L"STORSVC.DLL");
701 
702  /* Not doing a Volkswagen, honest! Some IFS tests won't run if not recognized FS. */
703  INIT_UNICODE_STRING(ifstest, L"IFSTEST.EXE");
704 
705  if (!PsGetCurrentProcess())
706  return false;
707 
708 #ifdef _AMD64_
709  Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &wow64info, sizeof(wow64info), NULL);
710 
711  if (NT_SUCCESS(Status) && wow64info != 0)
712  return true;
713 #endif
714 
716 
717  if (!NT_SUCCESS(Status)) {
718  ERR("ZwQueryInformationProcess returned %08lx\n", Status);
719  return false;
720  }
721 
722  if (!pbi.PebBaseAddress)
723  return false;
724 
725  peb = pbi.PebBaseAddress;
726 
727  if (!peb->Ldr)
728  return false;
729 
730  le = peb->Ldr->InMemoryOrderModuleList.Flink;
731  while (le != &peb->Ldr->InMemoryOrderModuleList) {
733  bool blacklist = false;
734 
735  if (entry->FullDllName.Length >= usmpr.Length) {
737 
738  name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usmpr.Length) / sizeof(WCHAR)];
739  name.Length = name.MaximumLength = usmpr.Length;
740 
741  blacklist = compare_strings(&name, &usmpr);
742  }
743 
744  if (!blacklist && entry->FullDllName.Length >= uscmd.Length) {
746 
747  name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - uscmd.Length) / sizeof(WCHAR)];
748  name.Length = name.MaximumLength = uscmd.Length;
749 
750  blacklist = compare_strings(&name, &uscmd);
751  }
752 
753  if (!blacklist && entry->FullDllName.Length >= usfsutil.Length) {
755 
756  name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usfsutil.Length) / sizeof(WCHAR)];
757  name.Length = name.MaximumLength = usfsutil.Length;
758 
759  blacklist = compare_strings(&name, &usfsutil);
760  }
761 
762  if (!blacklist && entry->FullDllName.Length >= usstorsvc.Length) {
764 
765  name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usstorsvc.Length) / sizeof(WCHAR)];
766  name.Length = name.MaximumLength = usstorsvc.Length;
767 
768  blacklist = compare_strings(&name, &usstorsvc);
769  }
770 
771  if (!blacklist && entry->FullDllName.Length >= usifstest.Length) {
773 
774  name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usifstest.Length) / sizeof(WCHAR)];
775  name.Length = name.MaximumLength = usifstest.Length;
776 
777  blacklist = compare_strings(&name, &usifstest);
778  }
779 
780  if (blacklist) {
781  void** frames;
782  ULONG i, num_frames;
783 
784  frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
785  if (!frames) {
786  ERR("out of memory\n");
787  return false;
788  }
789 
790  num_frames = RtlWalkFrameChain(frames, 256, 1);
791 
792  for (i = 0; i < num_frames; i++) {
793  // entry->Reserved3[1] appears to be the image size
794  if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
795  ExFreePool(frames);
796  return true;
797  }
798  }
799 
800  ExFreePool(frames);
801  }
802 
803  le = le->Flink;
804  }
805 
806  return false;
807 }
808 #endif // __REACTOS__
809 
810 // version of RtlUTF8ToUnicodeN for Vista and below
811 NTSTATUS utf8_to_utf16(WCHAR* dest, ULONG dest_max, ULONG* dest_len, char* src, ULONG src_len) {
813  uint8_t* in = (uint8_t*)src;
814  uint16_t* out = (uint16_t*)dest;
815  ULONG needed = 0, left = dest_max / sizeof(uint16_t);
816 
817  for (ULONG i = 0; i < src_len; i++) {
818  uint32_t cp;
819 
820  if (!(in[i] & 0x80))
821  cp = in[i];
822  else if ((in[i] & 0xe0) == 0xc0) {
823  if (i == src_len - 1 || (in[i+1] & 0xc0) != 0x80) {
824  cp = 0xfffd;
826  } else {
827  cp = ((in[i] & 0x1f) << 6) | (in[i+1] & 0x3f);
828  i++;
829  }
830  } else if ((in[i] & 0xf0) == 0xe0) {
831  if (i >= src_len - 2 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80) {
832  cp = 0xfffd;
834  } else {
835  cp = ((in[i] & 0xf) << 12) | ((in[i+1] & 0x3f) << 6) | (in[i+2] & 0x3f);
836  i += 2;
837  }
838  } else if ((in[i] & 0xf8) == 0xf0) {
839  if (i >= src_len - 3 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80 || (in[i+3] & 0xc0) != 0x80) {
840  cp = 0xfffd;
842  } else {
843  cp = ((in[i] & 0x7) << 18) | ((in[i+1] & 0x3f) << 12) | ((in[i+2] & 0x3f) << 6) | (in[i+3] & 0x3f);
844  i += 3;
845  }
846  } else {
847  cp = 0xfffd;
849  }
850 
851  if (cp > 0x10ffff) {
852  cp = 0xfffd;
854  }
855 
856  if (dest) {
857  if (cp <= 0xffff) {
858  if (left < 1)
859  return STATUS_BUFFER_OVERFLOW;
860 
861  *out = (uint16_t)cp;
862  out++;
863 
864  left--;
865  } else {
866  if (left < 2)
867  return STATUS_BUFFER_OVERFLOW;
868 
869  cp -= 0x10000;
870 
871  *out = 0xd800 | ((cp & 0xffc00) >> 10);
872  out++;
873 
874  *out = 0xdc00 | (cp & 0x3ff);
875  out++;
876 
877  left -= 2;
878  }
879  }
880 
881  if (cp <= 0xffff)
882  needed += sizeof(uint16_t);
883  else
884  needed += 2 * sizeof(uint16_t);
885  }
886 
887  if (dest_len)
888  *dest_len = needed;
889 
890  return Status;
891 }
892 
893 // version of RtlUnicodeToUTF8N for Vista and below
894 NTSTATUS utf16_to_utf8(char* dest, ULONG dest_max, ULONG* dest_len, WCHAR* src, ULONG src_len) {
896  uint16_t* in = (uint16_t*)src;
897  uint8_t* out = (uint8_t*)dest;
898  ULONG in_len = src_len / sizeof(uint16_t);
899  ULONG needed = 0, left = dest_max;
900 
901  for (ULONG i = 0; i < in_len; i++) {
902  uint32_t cp = *in;
903  in++;
904 
905  if ((cp & 0xfc00) == 0xd800) {
906  if (i == in_len - 1 || (*in & 0xfc00) != 0xdc00) {
907  cp = 0xfffd;
909  } else {
910  cp = (cp & 0x3ff) << 10;
911  cp |= *in & 0x3ff;
912  cp += 0x10000;
913 
914  in++;
915  i++;
916  }
917  } else if ((cp & 0xfc00) == 0xdc00) {
918  cp = 0xfffd;
920  }
921 
922  if (cp > 0x10ffff) {
923  cp = 0xfffd;
925  }
926 
927  if (dest) {
928  if (cp < 0x80) {
929  if (left < 1)
930  return STATUS_BUFFER_OVERFLOW;
931 
932  *out = (uint8_t)cp;
933  out++;
934 
935  left--;
936  } else if (cp < 0x800) {
937  if (left < 2)
938  return STATUS_BUFFER_OVERFLOW;
939 
940  *out = 0xc0 | ((cp & 0x7c0) >> 6);
941  out++;
942 
943  *out = 0x80 | (cp & 0x3f);
944  out++;
945 
946  left -= 2;
947  } else if (cp < 0x10000) {
948  if (left < 3)
949  return STATUS_BUFFER_OVERFLOW;
950 
951  *out = 0xe0 | ((cp & 0xf000) >> 12);
952  out++;
953 
954  *out = 0x80 | ((cp & 0xfc0) >> 6);
955  out++;
956 
957  *out = 0x80 | (cp & 0x3f);
958  out++;
959 
960  left -= 3;
961  } else {
962  if (left < 4)
963  return STATUS_BUFFER_OVERFLOW;
964 
965  *out = 0xf0 | ((cp & 0x1c0000) >> 18);
966  out++;
967 
968  *out = 0x80 | ((cp & 0x3f000) >> 12);
969  out++;
970 
971  *out = 0x80 | ((cp & 0xfc0) >> 6);
972  out++;
973 
974  *out = 0x80 | (cp & 0x3f);
975  out++;
976 
977  left -= 4;
978  }
979  }
980 
981  if (cp < 0x80)
982  needed++;
983  else if (cp < 0x800)
984  needed += 2;
985  else if (cp < 0x10000)
986  needed += 3;
987  else
988  needed += 4;
989  }
990 
991  if (dest_len)
992  *dest_len = needed;
993 
994  return Status;
995 }
996 
999 static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
1001  NTSTATUS Status;
1002  ULONG BytesCopied = 0;
1003  device_extension* Vcb = DeviceObject->DeviceExtension;
1004  bool top_level;
1005 
1007 
1008  TRACE("query volume information\n");
1009  top_level = is_top_level(Irp);
1010 
1011  if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1013  goto end;
1014  } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1016  goto end;
1017  }
1018 
1020 
1022 
1023  switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
1025  {
1026  FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1027  bool overflow = false;
1028 #ifndef __REACTOS__
1029  static const WCHAR ntfs[] = L"NTFS";
1030 #endif
1031  static const WCHAR btrfs[] = L"Btrfs";
1032  const WCHAR* fs_name;
1033  ULONG fs_name_len, orig_fs_name_len;
1034 
1035 #ifndef __REACTOS__
1036  if (Irp->RequestorMode == UserMode && lie_about_fs_type()) {
1037  fs_name = ntfs;
1038  orig_fs_name_len = fs_name_len = sizeof(ntfs) - sizeof(WCHAR);
1039  } else {
1040  fs_name = btrfs;
1041  orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
1042  }
1043 #else
1044  fs_name = btrfs;
1045  orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
1046 #endif
1047 
1048  TRACE("FileFsAttributeInformation\n");
1049 
1050  if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
1051  if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
1052  fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
1053  else
1054  fs_name_len = 0;
1055 
1056  overflow = true;
1057  }
1058 
1059  data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
1064  if (Vcb->readonly)
1065  data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
1066 
1067  // should also be FILE_FILE_COMPRESSION when supported
1068  data->MaximumComponentNameLength = 255; // FIXME - check
1069  data->FileSystemNameLength = orig_fs_name_len;
1070  RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
1071 
1072  BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
1074  break;
1075  }
1076 
1078  {
1079  FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
1080 
1081  TRACE("FileFsDeviceInformation\n");
1082 
1083  ffdi->DeviceType = FILE_DEVICE_DISK;
1084 
1085  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1086  ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
1087  ExReleaseResourceLite(&Vcb->tree_lock);
1088 
1089  if (Vcb->readonly)
1091  else
1093 
1096 
1097  break;
1098  }
1099 
1101  {
1102  FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1103 
1104  TRACE("FileFsFullSizeInformation\n");
1105 
1108  ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1109  ffsi->BytesPerSector = 512;
1110 
1113 
1114  break;
1115  }
1116 
1118  {
1119  FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
1120 
1121  TRACE("FileFsObjectIdInformation\n");
1122 
1123  RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
1124  RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
1125 
1128 
1129  break;
1130  }
1131 
1132  case FileFsSizeInformation:
1133  {
1134  FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1135 
1136  TRACE("FileFsSizeInformation\n");
1137 
1139  ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1140  ffsi->BytesPerSector = 512;
1141 
1144 
1145  break;
1146  }
1147 
1149  {
1150  FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1152  bool overflow = false;
1153  ULONG label_len, orig_label_len;
1154 
1155  TRACE("FileFsVolumeInformation\n");
1156  TRACE("max length = %lu\n", IrpSp->Parameters.QueryVolume.Length);
1157 
1158  ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1159 
1160  Status = utf8_to_utf16(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1161  if (!NT_SUCCESS(Status)) {
1162  ERR("utf8_to_utf16 returned %08lx\n", Status);
1163  ExReleaseResourceLite(&Vcb->tree_lock);
1164  break;
1165  }
1166 
1167  orig_label_len = label_len;
1168 
1169  if (IrpSp->Parameters.QueryVolume.Length < offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + label_len) {
1170  if (IrpSp->Parameters.QueryVolume.Length > offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel))
1171  label_len = IrpSp->Parameters.QueryVolume.Length - offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel);
1172  else
1173  label_len = 0;
1174 
1175  overflow = true;
1176  }
1177 
1178  TRACE("label_len = %lu\n", label_len);
1179 
1180  RtlZeroMemory(&ffvi, offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel));
1181 
1182  ffvi.VolumeSerialNumber = Vcb->superblock.uuid.uuid[12] << 24 | Vcb->superblock.uuid.uuid[13] << 16 | Vcb->superblock.uuid.uuid[14] << 8 | Vcb->superblock.uuid.uuid[15];
1183  ffvi.VolumeLabelLength = orig_label_len;
1184 
1185  RtlCopyMemory(data, &ffvi, min(offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel), IrpSp->Parameters.QueryVolume.Length));
1186 
1187  if (label_len > 0) {
1188  ULONG bytecount;
1189 
1190  Status = utf8_to_utf16(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1192  ERR("utf8_to_utf16 returned %08lx\n", Status);
1193  ExReleaseResourceLite(&Vcb->tree_lock);
1194  break;
1195  }
1196 
1197  TRACE("label = %.*S\n", (int)(label_len / sizeof(WCHAR)), data->VolumeLabel);
1198  }
1199 
1200  ExReleaseResourceLite(&Vcb->tree_lock);
1201 
1202  BytesCopied = offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + label_len;
1204  break;
1205  }
1206 
1207 #ifndef __REACTOS__
1208 #ifdef _MSC_VER // not in mingw yet
1209  case FileFsSectorSizeInformation:
1210  {
1211  FILE_FS_SECTOR_SIZE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1212 
1213  data->LogicalBytesPerSector = Vcb->superblock.sector_size;
1214  data->PhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1215  data->PhysicalBytesPerSectorForPerformance = Vcb->superblock.sector_size;
1216  data->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1217  data->ByteOffsetForSectorAlignment = 0;
1218  data->ByteOffsetForPartitionAlignment = 0;
1219 
1220  data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
1221 
1222  if (Vcb->trim && !Vcb->options.no_trim)
1223  data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
1224 
1225  BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
1227 
1228  break;
1229  }
1230 #endif
1231 #endif /* __REACTOS__ */
1232 
1233  default:
1235  WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
1236  break;
1237  }
1238 
1240  Irp->IoStatus.Information = 0;
1241  else
1242  Irp->IoStatus.Information = BytesCopied;
1243 
1244 end:
1245  Irp->IoStatus.Status = Status;
1246 
1248 
1249  if (top_level)
1251 
1252  TRACE("query volume information returning %08lx\n", Status);
1253 
1255 
1256  return Status;
1257 }
1258 
1259 _Function_class_(IO_COMPLETION_ROUTINE)
1260 static NTSTATUS __stdcall read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
1261  read_context* context = conptr;
1262 
1264 
1265  context->iosb = Irp->IoStatus;
1266  KeSetEvent(&context->Event, 0, false);
1267 
1269 }
1270 
1272  _Out_ root** rootptr, _In_ bool no_tree, _In_ uint64_t offset, _In_opt_ PIRP Irp) {
1273  NTSTATUS Status;
1274  root* r;
1275  ROOT_ITEM* ri;
1276  traverse_ptr tp;
1277 
1279  if (!r) {
1280  ERR("out of memory\n");
1282  }
1283 
1285  if (!r->nonpaged) {
1286  ERR("out of memory\n");
1287  ExFreePool(r);
1289  }
1290 
1292  if (!ri) {
1293  ERR("out of memory\n");
1294 
1295  ExFreePool(r->nonpaged);
1296  ExFreePool(r);
1298  }
1299 
1300  r->id = id;
1301  r->treeholder.address = 0;
1302  r->treeholder.generation = Vcb->superblock.generation;
1303  r->treeholder.tree = NULL;
1304  r->lastinode = 0;
1305  r->dirty = false;
1306  r->received = false;
1307  r->reserved = NULL;
1308  r->parent = 0;
1309  r->send_ops = 0;
1310  RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
1311  r->root_item.num_references = 1;
1312  r->fcbs_version = 0;
1313  r->checked_for_orphans = true;
1314  r->dropped = false;
1315  InitializeListHead(&r->fcbs);
1316  RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
1317 
1318  RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
1319 
1320  // We ask here for a traverse_ptr to the item we're inserting, so we can
1321  // copy some of the tree's variables
1322 
1323  Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
1324  if (!NT_SUCCESS(Status)) {
1325  ERR("insert_tree_item returned %08lx\n", Status);
1326  ExFreePool(ri);
1327  ExFreePool(r->nonpaged);
1328  ExFreePool(r);
1329  return Status;
1330  }
1331 
1332  ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
1333 
1334  InsertTailList(&Vcb->roots, &r->list_entry);
1335 
1336  if (!no_tree) {
1338  if (!t) {
1339  ERR("out of memory\n");
1340 
1341  delete_tree_item(Vcb, &tp);
1342 
1343  ExFreePool(r->nonpaged);
1344  ExFreePool(r);
1345  ExFreePool(ri);
1347  }
1348 
1349  t->nonpaged = NULL;
1350 
1351  t->is_unique = true;
1352  t->uniqueness_determined = true;
1353  t->buf = NULL;
1354 
1355  r->treeholder.tree = t;
1356 
1357  RtlZeroMemory(&t->header, sizeof(tree_header));
1358  t->header.fs_uuid = tp.tree->header.fs_uuid;
1359  t->header.address = 0;
1360  t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
1361  t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
1362  t->header.generation = Vcb->superblock.generation;
1363  t->header.tree_id = id;
1364  t->header.num_items = 0;
1365  t->header.level = 0;
1366 
1367  t->has_address = false;
1368  t->size = 0;
1369  t->Vcb = Vcb;
1370  t->parent = NULL;
1371  t->paritem = NULL;
1372  t->root = r;
1373 
1374  InitializeListHead(&t->itemlist);
1375 
1376  t->new_address = 0;
1377  t->has_new_address = false;
1378  t->updated_extents = false;
1379 
1380  InsertTailList(&Vcb->trees, &t->list_entry);
1381  t->list_entry_hash.Flink = NULL;
1382 
1383  t->write = true;
1384  Vcb->need_write = true;
1385  }
1386 
1387  *rootptr = r;
1388 
1389  return STATUS_SUCCESS;
1390 }
1391 
1393  ULONG utf8len;
1394  NTSTATUS Status;
1395  ULONG vollen, i;
1396 
1397  TRACE("label = %.*S\n", (int)(ffli->VolumeLabelLength / sizeof(WCHAR)), ffli->VolumeLabel);
1398 
1399  vollen = ffli->VolumeLabelLength;
1400 
1401  for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
1402  if (ffli->VolumeLabel[i] == 0) {
1403  vollen = i * sizeof(WCHAR);
1404  break;
1405  } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
1407  goto end;
1408  }
1409  }
1410 
1411  if (vollen == 0) {
1412  utf8len = 0;
1413  } else {
1414  Status = utf16_to_utf8(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
1415  if (!NT_SUCCESS(Status))
1416  goto end;
1417 
1418  if (utf8len > MAX_LABEL_SIZE) {
1420  goto end;
1421  }
1422  }
1423 
1424  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1425 
1426  if (utf8len > 0) {
1427  Status = utf16_to_utf8((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
1428  if (!NT_SUCCESS(Status))
1429  goto release;
1430  } else
1432 
1433  if (utf8len < MAX_LABEL_SIZE)
1434  RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
1435 
1436  Vcb->need_write = true;
1437 
1438 release:
1439  ExReleaseResourceLite(&Vcb->tree_lock);
1440 
1441 end:
1442  TRACE("returning %08lx\n", Status);
1443 
1444  return Status;
1445 }
1446 
1449 static NTSTATUS __stdcall drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
1451  device_extension* Vcb = DeviceObject->DeviceExtension;
1452  NTSTATUS Status;
1453  bool top_level;
1454 
1456 
1457  TRACE("set volume information\n");
1458 
1459  top_level = is_top_level(Irp);
1460 
1461  if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1463  goto end;
1464  } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1466  goto end;
1467  }
1468 
1470 
1471  if (Vcb->readonly) {
1473  goto end;
1474  }
1475 
1476  if (Vcb->removing || Vcb->locked) {
1478  goto end;
1479  }
1480 
1481  switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
1483  FIXME("STUB: FileFsControlInformation\n");
1484  break;
1485 
1487  TRACE("FileFsLabelInformation\n");
1488 
1489  Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
1490  break;
1491 
1493  FIXME("STUB: FileFsObjectIdInformation\n");
1494  break;
1495 
1496  default:
1497  WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
1498  break;
1499  }
1500 
1501 end:
1502  Irp->IoStatus.Status = Status;
1503  Irp->IoStatus.Information = 0;
1504 
1505  TRACE("returning %08lx\n", Status);
1506 
1508 
1509  if (top_level)
1511 
1513 
1514  return Status;
1515 }
1516 
1519  NTSTATUS Status;
1520  ULONG reqlen;
1521  USHORT name_offset;
1522  fcb* fcb = fileref->fcb;
1523 
1524  fn.Length = fn.MaximumLength = 0;
1525  Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1526  if (Status != STATUS_BUFFER_OVERFLOW) {
1527  ERR("fileref_get_filename returned %08lx\n", Status);
1528  return;
1529  }
1530 
1531  if (reqlen > 0xffff) {
1532  WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1533  return;
1534  }
1535 
1536  fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1537  if (!fn.Buffer) {
1538  ERR("out of memory\n");
1539  return;
1540  }
1541 
1542  fn.MaximumLength = (USHORT)reqlen;
1543  fn.Length = 0;
1544 
1545  Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
1546  if (!NT_SUCCESS(Status)) {
1547  ERR("fileref_get_filename returned %08lx\n", Status);
1548  ExFreePool(fn.Buffer);
1549  return;
1550  }
1551 
1552  FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
1553  (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1554  ExFreePool(fn.Buffer);
1555 }
1556 
1558  fcb* fcb = fileref->fcb;
1559  LIST_ENTRY* le;
1560  NTSTATUS Status;
1561 
1562  // no point looking for hardlinks if st_nlink == 1
1563  if (fileref->fcb->inode_item.st_nlink == 1) {
1564  ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
1565  send_notification_fileref(fileref, filter_match, action, stream);
1566  ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
1567  return;
1568  }
1569 
1570  ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
1571 
1572  le = fcb->hardlinks.Flink;
1573  while (le != &fcb->hardlinks) {
1575  file_ref* parfr;
1576 
1577  Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
1578 
1579  if (!NT_SUCCESS(Status))
1580  ERR("open_fileref_by_inode returned %08lx\n", Status);
1581  else if (!parfr->deleted) {
1583  ULONG pathlen;
1584 
1585  fn.Length = fn.MaximumLength = 0;
1586  Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
1587  if (Status != STATUS_BUFFER_OVERFLOW) {
1588  ERR("fileref_get_filename returned %08lx\n", Status);
1589  free_fileref(parfr);
1590  break;
1591  }
1592 
1593  if (parfr != fcb->Vcb->root_fileref)
1594  pathlen += sizeof(WCHAR);
1595 
1596  if (pathlen + hl->name.Length > 0xffff) {
1597  WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1598  free_fileref(parfr);
1599  break;
1600  }
1601 
1602  fn.MaximumLength = (USHORT)(pathlen + hl->name.Length);
1603  fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
1604  if (!fn.Buffer) {
1605  ERR("out of memory\n");
1606  free_fileref(parfr);
1607  break;
1608  }
1609 
1610  Status = fileref_get_filename(parfr, &fn, NULL, NULL);
1611  if (!NT_SUCCESS(Status)) {
1612  ERR("fileref_get_filename returned %08lx\n", Status);
1613  free_fileref(parfr);
1614  ExFreePool(fn.Buffer);
1615  break;
1616  }
1617 
1618  if (parfr != fcb->Vcb->root_fileref) {
1619  fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
1620  fn.Length += sizeof(WCHAR);
1621  }
1622 
1623  RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
1624  fn.Length += hl->name.Length;
1625 
1626  FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, (USHORT)pathlen,
1627  (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1628 
1629  ExFreePool(fn.Buffer);
1630 
1631  free_fileref(parfr);
1632  }
1633 
1634  le = le->Flink;
1635  }
1636 
1637  ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
1638 }
1639 
1640 typedef struct {
1647 
1648 _Function_class_(IO_WORKITEM_ROUTINE)
1649 static void __stdcall notification_work_item(PDEVICE_OBJECT DeviceObject, PVOID con) {
1650  notification_fcb* nf = con;
1651 
1653 
1654  ExAcquireResourceSharedLite(&nf->fileref->fcb->Vcb->tree_lock, TRUE); // protect us from fileref being reaped
1655 
1657 
1658  free_fileref(nf->fileref);
1659 
1660  ExReleaseResourceLite(&nf->fileref->fcb->Vcb->tree_lock);
1661 
1663 
1664  ExFreePool(nf);
1665 }
1666 
1668  notification_fcb* nf;
1669  PIO_WORKITEM work_item;
1670 
1672  if (!nf) {
1673  ERR("out of memory\n");
1674  return;
1675  }
1676 
1677  work_item = IoAllocateWorkItem(master_devobj);
1678  if (!work_item) {
1679  ERR("out of memory\n");
1680  ExFreePool(nf);
1681  return;
1682  }
1683 
1684  InterlockedIncrement(&fileref->refcount);
1685 
1686  nf->fileref = fileref;
1687  nf->filter_match = filter_match;
1688  nf->action = action;
1689  nf->stream = stream;
1690  nf->work_item = work_item;
1691 
1692  IoQueueWorkItem(work_item, notification_work_item, DelayedWorkQueue, nf);
1693 }
1694 
1696  if (!fcb->dirty) {
1697 #ifdef DEBUG_FCB_REFCOUNTS
1698  LONG rc;
1699 #endif
1700  fcb->dirty = true;
1701 
1702 #ifdef DEBUG_FCB_REFCOUNTS
1704  WARN("fcb %p: refcount now %i\n", fcb, rc);
1705 #else
1707 #endif
1708 
1709  ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, true);
1710  InsertTailList(&fcb->Vcb->dirty_fcbs, &fcb->list_entry_dirty);
1711  ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
1712  }
1713 
1714  fcb->Vcb->need_write = true;
1715 }
1716 
1718  if (!fileref->dirty) {
1719  fileref->dirty = true;
1720  increase_fileref_refcount(fileref);
1721 
1722  ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, true);
1723  InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &fileref->list_entry_dirty);
1724  ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
1725  }
1726 
1727  fileref->fcb->Vcb->need_write = true;
1728 }
1729 
1730 #ifdef DEBUG_FCB_REFCOUNTS
1731 void _free_fcb(_Inout_ fcb* fcb, _In_ const char* func) {
1733 #else
1736 #endif
1737 
1738 #ifdef DEBUG_FCB_REFCOUNTS
1739  ERR("fcb %p (%s): refcount now %i (subvol %I64x, inode %I64x)\n", fcb, func, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1740 #endif
1741 }
1742 
1743 void reap_fcb(fcb* fcb) {
1744  uint8_t c = fcb->hash >> 24;
1745 
1746  if (fcb->subvol && fcb->subvol->fcbs_ptrs[c] == &fcb->list_entry) {
1747  if (fcb->list_entry.Flink != &fcb->subvol->fcbs && (CONTAINING_RECORD(fcb->list_entry.Flink, struct _fcb, list_entry)->hash >> 24) == c)
1748  fcb->subvol->fcbs_ptrs[c] = fcb->list_entry.Flink;
1749  else
1750  fcb->subvol->fcbs_ptrs[c] = NULL;
1751  }
1752 
1753  if (fcb->list_entry.Flink) {
1755 
1756  if (fcb->subvol && fcb->subvol->dropped && IsListEmpty(&fcb->subvol->fcbs)) {
1757  ExDeleteResourceLite(&fcb->subvol->nonpaged->load_tree_lock);
1758  ExFreePool(fcb->subvol->nonpaged);
1759  ExFreePool(fcb->subvol);
1760  }
1761  }
1762 
1763  if (fcb->list_entry_all.Flink)
1765 
1766  ExDeleteResourceLite(&fcb->nonpaged->resource);
1767  ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
1768  ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
1769 
1770  ExFreeToNPagedLookasideList(&fcb->Vcb->fcb_np_lookaside, fcb->nonpaged);
1771 
1772  if (fcb->sd)
1773  ExFreePool(fcb->sd);
1774 
1775  if (fcb->adsxattr.Buffer)
1777 
1778  if (fcb->reparse_xattr.Buffer)
1780 
1781  if (fcb->ea_xattr.Buffer)
1783 
1784  if (fcb->adsdata.Buffer)
1786 
1787  while (!IsListEmpty(&fcb->extents)) {
1790 
1791  if (ext->csum)
1792  ExFreePool(ext->csum);
1793 
1794  ExFreePool(ext);
1795  }
1796 
1797  while (!IsListEmpty(&fcb->hardlinks)) {
1800 
1801  if (hl->name.Buffer)
1802  ExFreePool(hl->name.Buffer);
1803 
1804  if (hl->utf8.Buffer)
1805  ExFreePool(hl->utf8.Buffer);
1806 
1807  ExFreePool(hl);
1808  }
1809 
1810  while (!IsListEmpty(&fcb->xattrs)) {
1812 
1813  ExFreePool(xa);
1814  }
1815 
1816  while (!IsListEmpty(&fcb->dir_children_index)) {
1818  dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
1819 
1820  ExFreePool(dc->utf8.Buffer);
1821  ExFreePool(dc->name.Buffer);
1822  ExFreePool(dc->name_uc.Buffer);
1823  ExFreePool(dc);
1824  }
1825 
1826  if (fcb->hash_ptrs)
1828 
1829  if (fcb->hash_ptrs_uc)
1831 
1834 
1835  if (fcb->pool_type == NonPagedPool)
1836  ExFreePool(fcb);
1837  else
1838  ExFreeToPagedLookasideList(&fcb->Vcb->fcb_lookaside, fcb);
1839 }
1840 
1842  LIST_ENTRY* le;
1843 
1844  le = Vcb->all_fcbs.Flink;
1845  while (le != &Vcb->all_fcbs) {
1846  fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
1847  LIST_ENTRY* le2 = le->Flink;
1848 
1849  if (fcb->refcount == 0)
1850  reap_fcb(fcb);
1851 
1852  le = le2;
1853  }
1854 }
1855 
1857 #if defined(_DEBUG) || defined(DEBUG_FCB_REFCOUNTS)
1858  LONG rc = InterlockedDecrement(&fr->refcount);
1859 
1860 #ifdef DEBUG_FCB_REFCOUNTS
1861  ERR("fileref %p: refcount now %i\n", fr, rc);
1862 #endif
1863 
1864 #ifdef _DEBUG
1865  if (rc < 0) {
1866  ERR("fileref %p: refcount now %li\n", fr, rc);
1867  int3;
1868  }
1869 #endif
1870 #else
1871  InterlockedDecrement(&fr->refcount);
1872 #endif
1873 }
1874 
1876  // FIXME - do we need a file_ref lock?
1877 
1878  // FIXME - do delete if needed
1879 
1880  // FIXME - throw error if children not empty
1881 
1882  if (fr->fcb->fileref == fr)
1883  fr->fcb->fileref = NULL;
1884 
1885  if (fr->dc) {
1886  if (fr->fcb->ads)
1887  fr->dc->size = fr->fcb->adsdata.Length;
1888 
1889  fr->dc->fileref = NULL;
1890  }
1891 
1892  if (fr->list_entry.Flink)
1894 
1895  if (fr->parent)
1896  free_fileref(fr->parent);
1897 
1898  free_fcb(fr->fcb);
1899 
1900  if (fr->oldutf8.Buffer)
1901  ExFreePool(fr->oldutf8.Buffer);
1902 
1903  ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
1904 }
1905 
1907  LIST_ENTRY* le;
1908 
1909  // FIXME - recursion is a bad idea in kernel mode
1910 
1911  le = fr->children.Flink;
1912  while (le != &fr->children) {
1914  LIST_ENTRY* le2 = le->Flink;
1915 
1916  reap_filerefs(Vcb, c);
1917 
1918  le = le2;
1919  }
1920 
1921  if (fr->refcount == 0)
1922  reap_fileref(Vcb, fr);
1923 }
1924 
1926  fcb* fcb;
1927  ccb* ccb;
1928  file_ref* fileref = NULL;
1929  LONG open_files;
1930 
1931  UNUSED(Irp);
1932 
1933  TRACE("FileObject = %p\n", FileObject);
1934 
1935  fcb = FileObject->FsContext;
1936  if (!fcb) {
1937  TRACE("FCB was NULL, returning success\n");
1938  return STATUS_SUCCESS;
1939  }
1940 
1941  open_files = InterlockedDecrement(&fcb->Vcb->open_files);
1942 
1943  ccb = FileObject->FsContext2;
1944 
1945  TRACE("close called for fcb %p)\n", fcb);
1946 
1947  // FIXME - make sure notification gets sent if file is being deleted
1948 
1949  if (ccb) {
1950  if (ccb->query_string.Buffer)
1952 
1953  if (ccb->filename.Buffer)
1955 
1956  // FIXME - use refcounts for fileref
1957  fileref = ccb->fileref;
1958 
1959  if (fcb->Vcb->running_sends > 0) {
1960  bool send_cancelled = false;
1961 
1962  ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1963 
1964  if (ccb->send) {
1965  ccb->send->cancelling = true;
1966  send_cancelled = true;
1967  KeSetEvent(&ccb->send->cleared_event, 0, false);
1968  }
1969 
1970  ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1971 
1972  if (send_cancelled) {
1973  while (ccb->send) {
1974  ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1975  ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1976  }
1977  }
1978  }
1979 
1980  ExFreePool(ccb);
1981  }
1982 
1984 
1985  if (open_files == 0 && fcb->Vcb->removing) {
1986  uninit(fcb->Vcb);
1987  return STATUS_SUCCESS;
1988  }
1989 
1990  if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
1991  return STATUS_SUCCESS;
1992 
1993  if (fileref)
1994  free_fileref(fileref);
1995  else
1996  free_fcb(fcb);
1997 
1998  return STATUS_SUCCESS;
1999 }
2000 
2002  uint64_t i;
2003  KIRQL irql;
2004  NTSTATUS Status;
2005  LIST_ENTRY* le;
2007 
2008  if (!Vcb->removing) {
2009  ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2010  Vcb->removing = true;
2011  ExReleaseResourceLite(&Vcb->tree_lock);
2012  }
2013 
2014  if (Vcb->vde && Vcb->vde->mounted_device == Vcb->devobj)
2015  Vcb->vde->mounted_device = NULL;
2016 
2018  Vcb->Vpb->Flags &= ~VPB_MOUNTED;
2019  Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
2020  Vcb->Vpb->DeviceObject = NULL;
2022 
2023  // FIXME - needs global_loading_lock to be held
2024  if (Vcb->list_entry.Flink)
2025  RemoveEntryList(&Vcb->list_entry);
2026 
2027  if (Vcb->balance.thread) {
2028  Vcb->balance.paused = false;
2029  Vcb->balance.stopping = true;
2030  KeSetEvent(&Vcb->balance.event, 0, false);
2031  KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, false, NULL);
2032  }
2033 
2034  if (Vcb->scrub.thread) {
2035  Vcb->scrub.paused = false;
2036  Vcb->scrub.stopping = true;
2037  KeSetEvent(&Vcb->scrub.event, 0, false);
2038  KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, false, NULL);
2039  }
2040 
2041  if (Vcb->running_sends != 0) {
2042  bool send_cancelled = false;
2043 
2044  ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
2045 
2046  le = Vcb->send_ops.Flink;
2047  while (le != &Vcb->send_ops) {
2049 
2050  if (!send->cancelling) {
2051  send->cancelling = true;
2052  send_cancelled = true;
2053  send->ccb = NULL;
2054  KeSetEvent(&send->cleared_event, 0, false);
2055  }
2056 
2057  le = le->Flink;
2058  }
2059 
2060  ExReleaseResourceLite(&Vcb->send_load_lock);
2061 
2062  if (send_cancelled) {
2063  while (Vcb->running_sends != 0) {
2064  ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
2065  ExReleaseResourceLite(&Vcb->send_load_lock);
2066  }
2067  }
2068  }
2069 
2070  Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
2072  WARN("registry_mark_volume_unmounted returned %08lx\n", Status);
2073 
2074  for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2075  Vcb->calcthreads.threads[i].quit = true;
2076  }
2077 
2078  KeSetEvent(&Vcb->calcthreads.event, 0, false);
2079 
2080  for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2081  KeWaitForSingleObject(&Vcb->calcthreads.threads[i].finished, Executive, KernelMode, false, NULL);
2082 
2083  ZwClose(Vcb->calcthreads.threads[i].handle);
2084  }
2085 
2086  ExFreePool(Vcb->calcthreads.threads);
2087 
2088  time.QuadPart = 0;
2089  KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
2090  KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, false, NULL);
2091 
2092  reap_fcb(Vcb->volume_fcb);
2093  reap_fcb(Vcb->dummy_fcb);
2094 
2095  if (Vcb->root_file)
2096  ObDereferenceObject(Vcb->root_file);
2097 
2098  le = Vcb->chunks.Flink;
2099  while (le != &Vcb->chunks) {
2101 
2102  if (c->cache) {
2103  reap_fcb(c->cache);
2104  c->cache = NULL;
2105  }
2106 
2107  le = le->Flink;
2108  }
2109 
2110  while (!IsListEmpty(&Vcb->all_fcbs)) {
2111  fcb* fcb = CONTAINING_RECORD(Vcb->all_fcbs.Flink, struct _fcb, list_entry_all);
2112 
2113  reap_fcb(fcb);
2114  }
2115 
2116  while (!IsListEmpty(&Vcb->sys_chunks)) {
2118 
2119  if (sc->data)
2120  ExFreePool(sc->data);
2121 
2122  ExFreePool(sc);
2123  }
2124 
2125  while (!IsListEmpty(&Vcb->roots)) {
2127 
2128  ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
2129  ExFreePool(r->nonpaged);
2130  ExFreePool(r);
2131  }
2132 
2133  while (!IsListEmpty(&Vcb->chunks)) {
2135 
2136  while (!IsListEmpty(&c->space)) {
2137  LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2139 
2140  ExFreePool(s);
2141  }
2142 
2143  while (!IsListEmpty(&c->deleting)) {
2144  LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
2146 
2147  ExFreePool(s);
2148  }
2149 
2150  if (c->devices)
2151  ExFreePool(c->devices);
2152 
2153  if (c->cache)
2154  reap_fcb(c->cache);
2155 
2156  ExDeleteResourceLite(&c->range_locks_lock);
2157  ExDeleteResourceLite(&c->partial_stripes_lock);
2158  ExDeleteResourceLite(&c->lock);
2159  ExDeleteResourceLite(&c->changed_extents_lock);
2160 
2161  ExFreePool(c->chunk_item);
2162  ExFreePool(c);
2163  }
2164 
2165  while (!IsListEmpty(&Vcb->devices)) {
2167 
2168  while (!IsListEmpty(&dev->space)) {
2169  LIST_ENTRY* le2 = RemoveHeadList(&dev->space);
2171 
2172  ExFreePool(s);
2173  }
2174 
2175  ExFreePool(dev);
2176  }
2177 
2178  ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, true);
2179  while (!IsListEmpty(&Vcb->scrub.errors)) {
2181 
2182  ExFreePool(err);
2183  }
2184  ExReleaseResourceLite(&Vcb->scrub.stats_lock);
2185 
2186  ExDeleteResourceLite(&Vcb->fcb_lock);
2187  ExDeleteResourceLite(&Vcb->fileref_lock);
2188  ExDeleteResourceLite(&Vcb->load_lock);
2189  ExDeleteResourceLite(&Vcb->tree_lock);
2190  ExDeleteResourceLite(&Vcb->chunk_lock);
2191  ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
2192  ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
2193  ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
2194  ExDeleteResourceLite(&Vcb->scrub.stats_lock);
2195  ExDeleteResourceLite(&Vcb->send_load_lock);
2196 
2197  ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
2198  ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
2199  ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
2200  ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
2201  ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
2202  ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
2203  ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
2204  ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
2205 
2206  ZwClose(Vcb->flush_thread_handle);
2207 
2208  if (Vcb->devobj->AttachedDevice)
2209  IoDetachDevice(Vcb->devobj);
2210 
2211  IoDeleteDevice(Vcb->devobj);
2212 }
2213 
2215  NTSTATUS Status;
2216  LIST_ENTRY* le;
2217 
2218  // excise extents
2219 
2220  if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
2221  Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
2222  if (!NT_SUCCESS(Status)) {
2223  ERR("excise_extents returned %08lx\n", Status);
2224  return Status;
2225  }
2226  }
2227 
2228  fileref->fcb->Header.AllocationSize.QuadPart = 0;
2229  fileref->fcb->Header.FileSize.QuadPart = 0;
2230  fileref->fcb->Header.ValidDataLength.QuadPart = 0;
2231 
2232  if (FileObject) {
2233  CC_FILE_SIZES ccfs;
2234 
2235  ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
2236  ccfs.FileSize = fileref->fcb->Header.FileSize;
2237  ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
2238 
2240 
2241  _SEH2_TRY {
2242  CcSetFileSizes(FileObject, &ccfs);
2245  } _SEH2_END;
2246 
2247  if (!NT_SUCCESS(Status)) {
2248  ERR("CcSetFileSizes threw exception %08lx\n", Status);
2249  return Status;
2250  }
2251  }
2252 
2253  fileref->fcb->deleted = true;
2254 
2255  le = fileref->children.Flink;
2256  while (le != &fileref->children) {
2258 
2259  if (fr2->fcb->ads) {
2260  fr2->fcb->deleted = true;
2261  mark_fcb_dirty(fr2->fcb);
2262  }
2263 
2264  le = le->Flink;
2265  }
2266 
2267  return STATUS_SUCCESS;
2268 }
2269 
2271  LARGE_INTEGER newlength, time;
2272  BTRFS_TIME now;
2273  NTSTATUS Status;
2274  ULONG utf8len = 0;
2275 
2278 
2279  ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, true);
2280 
2281  if (fileref->deleted) {
2282  ExReleaseResourceLite(fileref->fcb->Header.Resource);
2283  return STATUS_SUCCESS;
2284  }
2285 
2286  if (fileref->fcb->subvol->send_ops > 0) {
2287  ExReleaseResourceLite(fileref->fcb->Header.Resource);
2288  return STATUS_ACCESS_DENIED;
2289  }
2290 
2291  fileref->deleted = true;
2292  mark_fileref_dirty(fileref);
2293 
2294  // delete INODE_ITEM (0x1)
2295 
2296  TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
2297 
2298  if (!fileref->fcb->ads) {
2299  if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
2300  LIST_ENTRY* le;
2301 
2302  mark_fcb_dirty(fileref->fcb);
2303 
2304  fileref->fcb->inode_item_changed = true;
2305 
2306  if (fileref->fcb->inode_item.st_nlink > 1 || make_orphan) {
2307  fileref->fcb->inode_item.st_nlink--;
2308  fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2309  fileref->fcb->inode_item.sequence++;
2310  fileref->fcb->inode_item.st_ctime = now;
2311  } else {
2313  if (!NT_SUCCESS(Status)) {
2314  ERR("delete_fileref_fcb returned %08lx\n", Status);
2315  ExReleaseResourceLite(fileref->fcb->Header.Resource);
2316  return Status;
2317  }
2318  }
2319 
2320  if (fileref->dc) {
2321  le = fileref->fcb->hardlinks.Flink;
2322  while (le != &fileref->fcb->hardlinks) {
2324 
2325  if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
2327 
2328  if (hl->name.Buffer)
2329  ExFreePool(hl->name.Buffer);
2330 
2331  if (hl->utf8.Buffer)
2332  ExFreePool(hl->utf8.Buffer);
2333 
2334  ExFreePool(hl);
2335  break;
2336  }
2337 
2338  le = le->Flink;
2339  }
2340  }
2341  } else if (fileref->fcb->subvol->parent == fileref->parent->fcb->subvol->id) { // valid subvolume
2342  if (fileref->fcb->subvol->root_item.num_references > 1) {
2343  fileref->fcb->subvol->root_item.num_references--;
2344 
2345  mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2346  } else {
2347  LIST_ENTRY* le;
2348 
2349  // FIXME - we need a lock here
2350 
2351  RemoveEntryList(&fileref->fcb->subvol->list_entry);
2352 
2353  InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2354 
2355  le = fileref->children.Flink;
2356  while (le != &fileref->children) {
2358 
2359  if (fr2->fcb->ads) {
2360  fr2->fcb->deleted = true;
2361  mark_fcb_dirty(fr2->fcb);
2362  }
2363 
2364  le = le->Flink;
2365  }
2366  }
2367  }
2368  } else {
2369  fileref->fcb->deleted = true;
2370  mark_fcb_dirty(fileref->fcb);
2371  }
2372 
2373  // remove dir_child from parent
2374 
2375  if (fileref->dc) {
2376  TRACE("delete file %.*S\n", (int)(fileref->dc->name.Length / sizeof(WCHAR)), fileref->dc->name.Buffer);
2377 
2378  ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2379  RemoveEntryList(&fileref->dc->list_entry_index);
2380 
2381  if (!fileref->fcb->ads)
2382  remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
2383 
2384  ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2385 
2386  if (!fileref->oldutf8.Buffer)
2387  fileref->oldutf8 = fileref->dc->utf8;
2388  else
2389  ExFreePool(fileref->dc->utf8.Buffer);
2390 
2391  utf8len = fileref->dc->utf8.Length;
2392 
2393  fileref->oldindex = fileref->dc->index;
2394 
2395  ExFreePool(fileref->dc->name.Buffer);
2396  ExFreePool(fileref->dc->name_uc.Buffer);
2397  ExFreePool(fileref->dc);
2398 
2399  fileref->dc = NULL;
2400  }
2401 
2402  // update INODE_ITEM of parent
2403 
2404  ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, true);
2405 
2406  fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2407  fileref->parent->fcb->inode_item.sequence++;
2408  fileref->parent->fcb->inode_item.st_ctime = now;
2409 
2410  if (!fileref->fcb->ads) {
2411  TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2412  fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
2413  TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2414  fileref->parent->fcb->inode_item.st_mtime = now;
2415  }
2416 
2417  fileref->parent->fcb->inode_item_changed = true;
2418  ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2419 
2420  if (!fileref->fcb->ads && fileref->parent->dc)
2422 
2423  mark_fcb_dirty(fileref->parent->fcb);
2424 
2425  fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2426  fileref->fcb->subvol->root_item.ctime = now;
2427 
2428  newlength.QuadPart = 0;
2429 
2430  if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2431  TRACE("CcUninitializeCacheMap failed\n");
2432 
2433  ExReleaseResourceLite(fileref->fcb->Header.Resource);
2434 
2435  return STATUS_SUCCESS;
2436 }
2437 
2441  NTSTATUS Status;
2444  device_extension* Vcb = DeviceObject->DeviceExtension;
2445  fcb* fcb = FileObject->FsContext;
2446  bool top_level;
2447 
2449 
2450  TRACE("cleanup\n");
2451 
2452  top_level = is_top_level(Irp);
2453 
2454  if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2455  Irp->IoStatus.Information = 0;
2457  goto exit;
2458  } else if (DeviceObject == master_devobj) {
2459  TRACE("closing file system\n");
2461  goto exit;
2462  } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2464  goto exit;
2465  }
2466 
2467  if (FileObject->Flags & FO_CLEANUP_COMPLETE) {
2468  TRACE("FileObject %p already cleaned up\n", FileObject);
2470  goto exit;
2471  }
2472 
2473  if (!fcb) {
2474  ERR("fcb was NULL\n");
2476  goto exit;
2477  }
2478 
2480 
2481  // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2482  // messages belonging to other devices.
2483 
2484  if (FileObject && FileObject->FsContext) {
2485  ccb* ccb;
2486  file_ref* fileref;
2487  bool locked = true;
2488 
2489  ccb = FileObject->FsContext2;
2490  fileref = ccb ? ccb->fileref : NULL;
2491 
2492  TRACE("cleanup called for FileObject %p\n", FileObject);
2493  TRACE("fileref %p, refcount = %li, open_count = %li\n", fileref, fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
2494 
2495  ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
2496 
2497  ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
2498 
2500 
2502 
2503  if (ccb)
2504  FsRtlNotifyCleanup(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, ccb);
2505 
2506  if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2507  fileref->delete_on_close = true;
2508 
2509  if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && fcb != fcb->Vcb->dummy_fcb)
2510  fileref->delete_on_close = false;
2511 
2512  if (fcb->Vcb->locked && fcb->Vcb->locked_fileobj == FileObject) {
2513  TRACE("unlocking volume\n");
2516  }
2517 
2518  if (ccb && ccb->reserving) {
2519  fcb->subvol->reserved = NULL;
2520  ccb->reserving = false;
2521  // FIXME - flush all of subvol's fcbs
2522  }
2523 
2524  if (fileref) {
2525  LONG oc = InterlockedDecrement(&fileref->open_count);
2526 #ifdef DEBUG_FCB_REFCOUNTS
2527  ERR("fileref %p: open_count now %i\n", fileref, oc);
2528 #endif
2529 
2530  if (oc == 0 || (fileref->delete_on_close && fileref->posix_delete)) {
2531  if (!fcb->Vcb->removing) {
2532  if (oc == 0 && fileref->fcb->inode_item.st_nlink == 0 && fileref != fcb->Vcb->root_fileref &&
2533  fcb != fcb->Vcb->volume_fcb && !fcb->ads) { // last handle closed on POSIX-deleted file
2535 
2537 
2539  if (!NT_SUCCESS(Status)) {
2540  ERR("delete_fileref_fcb returned %08lx\n", Status);
2542  ExReleaseResourceLite(fileref->fcb->Header.Resource);
2543  ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2544  goto exit;
2545  }
2546 
2548 
2549  mark_fcb_dirty(fileref->fcb);
2550  } else if (fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2552 
2554 
2555  if (!fileref->fcb->ads || fileref->dc) {
2556  if (fileref->fcb->ads) {
2558  FILE_ACTION_REMOVED, &fileref->dc->name);
2559  } else
2561  }
2562 
2563  ExReleaseResourceLite(fcb->Header.Resource);
2564  locked = false;
2565 
2566  // fileref_lock needs to be acquired before fcb->Header.Resource
2567  ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
2568 
2569  Status = delete_fileref(fileref, FileObject, oc > 0 && fileref->posix_delete, Irp, &rollback);
2570  if (!NT_SUCCESS(Status)) {
2571  ERR("delete_fileref returned %08lx\n", Status);
2573  ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2574  ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2575  goto exit;
2576  }
2577 
2578  ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2579 
2581  } else if (FileObject->Flags & FO_CACHE_SUPPORTED && FileObject->SectionObjectPointer->DataSectionObject) {
2583 
2584  if (locked) {
2585  ExReleaseResourceLite(fcb->Header.Resource);
2586  locked = false;
2587  }
2588 
2589  CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2590 
2591  if (!NT_SUCCESS(iosb.Status))
2592  ERR("CcFlushCache returned %08lx\n", iosb.Status);
2593 
2594  if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2595  ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
2596  ExReleaseResourceLite(fcb->Header.PagingIoResource);
2597  }
2598 
2599  CcPurgeCacheSection(FileObject->SectionObjectPointer, NULL, 0, false);
2600 
2601  TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x)\n",
2602  FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2603  }
2604  }
2605 
2606  if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2608  }
2609  }
2610 
2611  if (locked)
2612  ExReleaseResourceLite(fcb->Header.Resource);
2613 
2614  ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2615 
2616  FileObject->Flags |= FO_CLEANUP_COMPLETE;
2617  }
2618 
2620 
2621 exit:
2622  TRACE("returning %08lx\n", Status);
2623 
2624  Irp->IoStatus.Status = Status;
2625  Irp->IoStatus.Information = 0;
2626 
2628 
2629  if (top_level)
2631 
2633 
2634  return Status;
2635 }
2636 
2637 _Success_(return)
2638 bool get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ uint16_t len, _Out_ ULONG* atts) {
2639  if (len > 2 && val[0] == '0' && val[1] == 'x') {
2640  int i;
2641  ULONG dosnum = 0;
2642 
2643  for (i = 2; i < len; i++) {
2644  dosnum *= 0x10;
2645 
2646  if (val[i] >= '0' && val[i] <= '9')
2647  dosnum |= val[i] - '0';
2648  else if (val[i] >= 'a' && val[i] <= 'f')
2649  dosnum |= val[i] + 10 - 'a';
2650  else if (val[i] >= 'A' && val[i] <= 'F')
2651  dosnum |= val[i] + 10 - 'a';
2652  }
2653 
2654  TRACE("DOSATTRIB: %08lx\n", dosnum);
2655 
2656  *atts = dosnum;
2657 
2658  return true;
2659  }
2660 
2661  return false;
2662 }
2663 
2665  _In_ uint8_t type, _In_ bool dotfile, _In_ bool ignore_xa, _In_opt_ PIRP Irp) {
2666  ULONG att;
2667  char* eaval;
2668  uint16_t ealen;
2669 
2670  if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (uint8_t**)&eaval, &ealen, Irp)) {
2671  ULONG dosnum = 0;
2672 
2673  if (get_file_attributes_from_xattr(eaval, ealen, &dosnum)) {
2674  ExFreePool(eaval);
2675 
2676  if (type == BTRFS_TYPE_DIRECTORY)
2677  dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2678  else if (type == BTRFS_TYPE_SYMLINK)
2679  dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2680 
2681  if (type != BTRFS_TYPE_DIRECTORY)
2682  dosnum &= ~FILE_ATTRIBUTE_DIRECTORY;
2683 
2684  if (inode == SUBVOL_ROOT_INODE) {
2685  if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2686  dosnum |= FILE_ATTRIBUTE_READONLY;
2687  else
2688  dosnum &= ~FILE_ATTRIBUTE_READONLY;
2689  }
2690 
2691  return dosnum;
2692  }
2693 
2694  ExFreePool(eaval);
2695  }
2696 
2697  switch (type) {
2698  case BTRFS_TYPE_DIRECTORY:
2700  break;
2701 
2702  case BTRFS_TYPE_SYMLINK:
2704  break;
2705 
2706  default:
2707  att = 0;
2708  break;
2709  }
2710 
2711  if (dotfile || (r->id == BTRFS_ROOT_FSTREE && inode == SUBVOL_ROOT_INODE))
2712  att |= FILE_ATTRIBUTE_HIDDEN;
2713 
2714  att |= FILE_ATTRIBUTE_ARCHIVE;
2715 
2716  if (inode == SUBVOL_ROOT_INODE) {
2717  if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2718  att |= FILE_ATTRIBUTE_READONLY;
2719  else
2720  att &= ~FILE_ATTRIBUTE_READONLY;
2721  }
2722 
2723  // FIXME - get READONLY from ii->st_mode
2724  // FIXME - return SYSTEM for block/char devices?
2725 
2726  if (att == 0)
2727  att = FILE_ATTRIBUTE_NORMAL;
2728 
2729  return att;
2730 }
2731 
2733  _Out_writes_bytes_(Length) PUCHAR Buffer, _In_ bool override) {
2736  PIRP Irp;
2738  NTSTATUS Status;
2740 
2741  num_reads++;
2742 
2743  RtlZeroMemory(&context, sizeof(read_context));
2745 
2746  Offset.QuadPart = (LONGLONG)StartingOffset;
2747 
2748  Irp = IoAllocateIrp(DeviceObject->StackSize, false);
2749 
2750  if (!Irp) {
2751  ERR("IoAllocateIrp failed\n");
2753  }
2754 
2755  Irp->Flags |= IRP_NOCACHE;
2759 
2760  if (override)
2762 
2763  if (DeviceObject->Flags & DO_BUFFERED_IO) {
2764  Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
2765  if (!Irp->AssociatedIrp.SystemBuffer) {
2766  ERR("out of memory\n");
2768  goto exit;
2769  }
2770 
2772 
2773  Irp->UserBuffer = Buffer;
2774  } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2775  Irp->MdlAddress = IoAllocateMdl(Buffer, Length, false, false, NULL);
2776  if (!Irp->MdlAddress) {
2777  ERR("IoAllocateMdl failed\n");
2779  goto exit;
2780  }
2781 
2783 
2784  _SEH2_TRY {
2788  } _SEH2_END;
2789 
2790  if (!NT_SUCCESS(Status)) {
2791  ERR("MmProbeAndLockPages threw exception %08lx\n", Status);
2792  IoFreeMdl(Irp->MdlAddress);
2793  goto exit;
2794  }
2795  } else
2796  Irp->UserBuffer = Buffer;
2797 
2798  IrpSp->Parameters.Read.Length = Length;
2799  IrpSp->Parameters.Read.ByteOffset = Offset;
2800 
2801  Irp->UserIosb = &IoStatus;
2802 
2803  Irp->UserEvent = &context.Event;
2804 
2805  IoSetCompletionRoutine(Irp, read_completion, &context, true, true, true);
2806 
2808 
2809  if (Status == STATUS_PENDING) {
2811  Status = context.iosb.Status;
2812  }
2813 
2814  if (DeviceObject->Flags & DO_DIRECT_IO) {
2815  MmUnlockPages(Irp->MdlAddress);
2816  IoFreeMdl(Irp->MdlAddress);
2817  }
2818 
2819 exit:
2820  IoFreeIrp(Irp);
2821 
2822  return Status;
2823 }
2824 
2826  switch (sb->csum_type) {
2827  case CSUM_TYPE_CRC32C: {
2828  uint32_t crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2829 
2830  if (crc32 == *((uint32_t*)sb->checksum))
2831  return true;
2832 
2833  WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
2834 
2835  break;
2836  }
2837 
2838  case CSUM_TYPE_XXHASH: {
2839  uint64_t hash = XXH64(&sb->uuid, sizeof(superblock) - sizeof(sb->checksum), 0);
2840 
2841  if (hash == *((uint64_t*)sb->checksum))
2842  return true;
2843 
2844  WARN("superblock hash was %I64x, expected %I64x\n", hash, *((uint64_t*)sb->checksum));
2845 
2846  break;
2847  }
2848 
2849  case CSUM_TYPE_SHA256: {
2851 
2852  calc_sha256(hash, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
2853 
2855  return true;
2856 
2857  WARN("superblock hash was invalid\n");
2858 
2859  break;
2860  }
2861 
2862  case CSUM_TYPE_BLAKE2: {
2864 
2865  blake2b(hash, sizeof(hash), &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
2866 
2868  return true;
2869 
2870  WARN("superblock hash was invalid\n");
2871 
2872  break;
2873  }
2874 
2875  default:
2876  WARN("unrecognized csum type %x\n", sb->csum_type);
2877  }
2878 
2879  return false;
2880 }
2881 
2883  NTSTATUS Status;
2884  superblock* sb;
2885  ULONG i, to_read;
2886  uint8_t valid_superblocks;
2887 
2888  to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
2889 
2891  if (!sb) {
2892  ERR("out of memory\n");
2894  }
2895 
2896  if (superblock_addrs[0] + to_read > length) {
2897  WARN("device was too short to have any superblock\n");
2898  ExFreePool(sb);
2900  }
2901 
2902  i = 0;
2903  valid_superblocks = 0;
2904 
2905  while (superblock_addrs[i] > 0) {
2906  if (i > 0 && superblock_addrs[i] + to_read > length)
2907  break;
2908 
2909  Status = sync_read_phys(device, fileobj, superblock_addrs[i], to_read, (PUCHAR)sb, false);
2910  if (!NT_SUCCESS(Status)) {
2911  ERR("Failed to read superblock %lu: %08lx\n", i, Status);
2912  ExFreePool(sb);
2913  return Status;
2914  }
2915 
2916  if (sb->magic != BTRFS_MAGIC) {
2917  if (i == 0) {
2918  TRACE("not a BTRFS volume\n");
2919  ExFreePool(sb);
2921  }
2922  } else {
2923  TRACE("got superblock %lu!\n", i);
2924 
2925  if (sb->sector_size == 0)
2926  WARN("superblock sector size was 0\n");
2927  else if (sb->sector_size & (sb->sector_size - 1))
2928  WARN("superblock sector size was not power of 2\n");
2929  else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
2930  WARN("invalid node size %x\n", sb->node_size);
2931  else if ((sb->node_size % sb->sector_size) != 0)
2932  WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
2933  else if (check_superblock_checksum(sb) && (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation)) {
2934  RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2935  valid_superblocks++;
2936  }
2937  }
2938 
2939  i++;
2940  }
2941 
2942  ExFreePool(sb);
2943 
2944  if (valid_superblocks == 0) {
2945  ERR("could not find any valid superblocks\n");
2946  return STATUS_INTERNAL_ERROR;
2947  }
2948 
2949  TRACE("label is %s\n", Vcb->superblock.label);
2950 
2951  return STATUS_SUCCESS;
2952 }
2953 
2955  _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ bool Override, _Out_opt_ IO_STATUS_BLOCK* iosb) {
2956  PIRP Irp;
2957  KEVENT Event;
2958  NTSTATUS Status;
2961 
2963 
2965  DeviceObject,
2966  InputBuffer,
2967  InputBufferSize,
2968  OutputBuffer,
2969  OutputBufferSize,
2970  false,
2971  &Event,
2972  &IoStatus);
2973 
2974  if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2975 
2976  if (Override) {
2979  }
2980 
2982 
2983  if (Status == STATUS_PENDING) {
2985  Status = IoStatus.Status;
2986  }
2987 
2988  if (iosb)
2989  *iosb = IoStatus;
2990 
2991  return Status;
2992 }
2993 
2998  if (!r) {
2999  ERR("out of memory\n");
3001  }
3002 
3003  r->id = id;
3004  r->dirty = false;
3005  r->received = false;
3006  r->reserved = NULL;
3007  r->treeholder.address = addr;
3008  r->treeholder.tree = NULL;
3009  r->treeholder.generation = generation;
3010  r->parent = 0;
3011  r->send_ops = 0;
3012  r->fcbs_version = 0;
3013  r->checked_for_orphans = false;
3014  r->dropped = false;
3016  RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
3017 
3019  if (!r->nonpaged) {
3020  ERR("out of memory\n");
3021  ExFreePool(r);
3023  }
3024 
3025  ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
3026 
3027  r->lastinode = 0;
3028 
3029  if (tp) {
3030  RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
3031  if (tp->item->size < sizeof(ROOT_ITEM))
3032  RtlZeroMemory(((uint8_t*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
3033  } else
3034  RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
3035 
3036  if (!Vcb->readonly && (r->id == BTRFS_ROOT_ROOT || r->id == BTRFS_ROOT_FSTREE || (r->id >= 0x100 && !(r->id & 0xf000000000000000)))) { // FS tree root
3037  // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
3038  get_last_inode(Vcb, r, NULL);
3039 
3040  if (r->id == BTRFS_ROOT_ROOT && r->lastinode < 0x100)
3041  r->lastinode = 0x100;
3042  }
3043 
3044  InsertTailList(&Vcb->roots, &r->list_entry);
3045 
3046  switch (r->id) {
3047  case BTRFS_ROOT_ROOT:
3048  Vcb->root_root = r;
3049  break;
3050 
3051  case BTRFS_ROOT_EXTENT:
3052  Vcb->extent_root = r;
3053  break;
3054 
3055  case BTRFS_ROOT_CHUNK:
3056  Vcb->chunk_root = r;
3057  break;
3058 
3059  case BTRFS_ROOT_DEVTREE:
3060  Vcb->dev_root = r;
3061  break;
3062 
3063  case BTRFS_ROOT_CHECKSUM:
3064  Vcb->checksum_root = r;
3065  break;
3066 
3067  case BTRFS_ROOT_UUID:
3068  Vcb->uuid_root = r;
3069  break;
3070 
3071  case BTRFS_ROOT_FREE_SPACE:
3072  Vcb->space_root = r;
3073  break;
3074 
3075  case BTRFS_ROOT_DATA_RELOC:
3076  Vcb->data_reloc_root = r;
3077  break;
3078  }
3079 
3081 }
3082 
3084  traverse_ptr tp, next_tp;
3085  KEY searchkey;
3086  bool b;
3087  NTSTATUS Status;
3088 
3089  searchkey.obj_id = 0;
3090  searchkey.obj_type = 0;
3091  searchkey.offset = 0;
3092 
3093  Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
3094  if (!NT_SUCCESS(Status)) {
3095  ERR("error - find_item returned %08lx\n", Status);
3096  return Status;
3097  }
3098 
3099  do {
3100  TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3101 
3102  if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
3103  ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
3104 
3105  if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
3106  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(ROOT_ITEM, byte_limit));
3107  } else {
3108  TRACE("root %I64x - address %I64x\n", tp.item->key.obj_id, ri->block_number);
3109 
3111  if (!NT_SUCCESS(Status)) {
3112  ERR("add_root returned %08lx\n", Status);
3113  return Status;
3114  }
3115  }
3116  } else if (tp.item->key.obj_type == TYPE_ROOT_BACKREF && !IsListEmpty(&Vcb->roots)) {
3117  root* lastroot = CONTAINING_RECORD(Vcb->roots.Blink, root, list_entry);
3118 
3119  if (lastroot->id == tp.item->key.obj_id)
3120  lastroot->parent = tp.item->key.offset;
3121  }
3122 
3123  b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3124 
3125  if (b)
3126  tp = next_tp;
3127  } while (b);
3128 
3129  if (!Vcb->readonly && !Vcb->data_reloc_root) {
3130  root* reloc_root;
3131  INODE_ITEM* ii;
3132  uint16_t irlen;
3133  INODE_REF* ir;
3135  BTRFS_TIME now;
3136 
3137  WARN("data reloc root doesn't exist, creating it\n");
3138 
3140 
3141  if (!NT_SUCCESS(Status)) {
3142  ERR("create_root returned %08lx\n", Status);
3143  return Status;
3144  }
3145 
3146  reloc_root->root_item.inode.generation = 1;
3147  reloc_root->root_item.inode.st_size = 3;
3148  reloc_root->root_item.inode.st_blocks = Vcb->superblock.node_size;
3149  reloc_root->root_item.inode.st_nlink = 1;
3150  reloc_root->root_item.inode.st_mode = 040755;
3151  reloc_root->root_item.inode.flags = 0x80000000;
3152  reloc_root->root_item.inode.flags_ro = 0xffffffff;
3153  reloc_root->root_item.objid = SUBVOL_ROOT_INODE;
3154  reloc_root->root_item.bytes_used = Vcb->superblock.node_size;
3155 
3157  if (!ii) {
3158  ERR("out of memory\n");
3160  }
3161 
3164 
3165  RtlZeroMemory(ii, sizeof(INODE_ITEM));
3166  ii->generation = Vcb->superblock.generation;
3167  ii->st_blocks = Vcb->superblock.node_size;
3168  ii->st_nlink = 1;
3169  ii->st_mode = 040755;
3170  ii->st_atime = now;
3171  ii->st_ctime = now;
3172  ii->st_mtime = now;
3173 
3175  if (!NT_SUCCESS(Status)) {
3176  ERR("insert_tree_item returned %08lx\n", Status);
3177  ExFreePool(ii);
3178  return Status;
3179  }
3180 
3181  irlen = (uint16_t)offsetof(INODE_REF, name[0]) + 2;
3183  if (!ir) {
3184  ERR("out of memory\n");
3186  }
3187 
3188  ir->index = 0;
3189  ir->n = 2;
3190  ir->name[0] = '.';
3191  ir->name[1] = '.';
3192 
3194  if (!NT_SUCCESS(Status)) {
3195  ERR("insert_tree_item returned %08lx\n", Status);
3196  ExFreePool(ir);
3197  return Status;
3198  }
3199 
3200  Vcb->data_reloc_root = reloc_root;
3201  Vcb->need_write = true;
3202  }
3203 
3204  return STATUS_SUCCESS;
3205 }
3206 
3208  KEY searchkey;
3209  traverse_ptr tp, next_tp;
3210  bool b;
3211  uint64_t lastaddr;
3212  NTSTATUS Status;
3213 
3214  InitializeListHead(&dev->space);
3215 
3216  searchkey.obj_id = 0;
3217  searchkey.obj_type = TYPE_DEV_STATS;
3218  searchkey.offset = dev->devitem.dev_id;
3219 
3220  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3221  if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
3222  RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(uint64_t) * 5, tp.item->size));
3223 
3224  searchkey.obj_id = dev->devitem.dev_id;
3225  searchkey.obj_type = TYPE_DEV_EXTENT;
3226  searchkey.offset = 0;
3227 
3228  Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3229  if (!NT_SUCCESS(Status)) {
3230  ERR("error - find_item returned %08lx\n", Status);
3231  return Status;
3232  }
3233 
3234  lastaddr = 0;
3235 
3236  do {
3237  if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
3238  if (tp.item->size >= sizeof(DEV_EXTENT)) {
3239  DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
3240 
3241  if (tp.item->key.offset > lastaddr) {
3242  Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
3243  if (!NT_SUCCESS(Status)) {
3244  ERR("add_space_entry returned %08lx\n", Status);
3245  return Status;
3246  }
3247  }
3248 
3249  lastaddr = tp.item->key.offset + de->length;
3250  } else {
3251  ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
3252  }
3253  }
3254 
3255  b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3256 
3257  if (b) {
3258  tp = next_tp;
3259  if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
3260  break;
3261  }
3262  } while (b);
3263 
3264  if (lastaddr < dev->devitem.num_bytes) {
3265  Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
3266  if (!NT_SUCCESS(Status)) {
3267  ERR("add_space_entry returned %08lx\n", Status);
3268  return Status;
3269  }
3270  }
3271 
3272  // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
3273 
3274  space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
3275 
3276  return STATUS_SUCCESS;
3277 }
3278 
3280  LIST_ENTRY* le;
3281 
3282  le = Vcb->devices.Flink;
3283 
3284  while (le != &Vcb->devices) {
3285  device* dev2 = CONTAINING_RECORD(le, device, list_entry);
3286 
3287  if (dev2->devitem.dev_id > dev->devitem.dev_id) {
3288  InsertHeadList(le->Blink, &dev->list_entry);
3289  return;
3290  }
3291 
3292  le = le->Flink;
3293  }
3294 
3295  InsertTailList(&Vcb->devices, &dev->list_entry);
3296 }
3297 
3301  pdo_device_extension* pdode;
3302  LIST_ENTRY* le;
3303 
3304  le = Vcb->devices.Flink;
3305  while (le != &Vcb->devices) {
3307 
3308  TRACE("device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev->devitem.dev_id,
3309  dev->devitem.device_uuid.uuid[0], dev->devitem.device_uuid.uuid[1], dev->devitem.device_uuid.uuid[2], dev->devitem.device_uuid.uuid[3], dev->devitem.device_uuid.uuid[4], dev->devitem.device_uuid.uuid[5], dev->devitem.device_uuid.uuid[6], dev->devitem.device_uuid.uuid[7],
3310  dev->devitem.device_uuid.uuid[8], dev->devitem.device_uuid.uuid[9], dev->devitem.device_uuid.uuid[10], dev->devitem.device_uuid.uuid[11], dev->devitem.device_uuid.uuid[12], dev->devitem.device_uuid.uuid[13], dev->devitem.device_uuid.uuid[14], dev->devitem.device_uuid.uuid[15]);
3311 
3312  if (RtlCompareMemory(&dev->devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3313  TRACE("returning device %I64x\n", dev->devitem.dev_id);
3314  return dev;
3315  }
3316 
3317  le = le->Flink;
3318  }
3319 
3320  vde = Vcb->vde;
3321 
3322  if (!vde)
3323  goto end;
3324 
3325  pdode = vde->pdode;
3326 
3327  ExAcquireResourceSharedLite(&pdode->child_lock, true);
3328 
3329  if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3330  le = pdode->children.Flink;
3331 
3332  while (le != &pdode->children) {
3334 
3335  if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3336  device* dev;
3337 
3339  if (!dev) {
3341  ERR("out of memory\n");
3342  return NULL;
3343  }
3344 
3345  RtlZeroMemory(dev, sizeof(device));
3346  dev->devobj = vc->devobj;
3347  dev->fileobj = vc->fileobj;
3348  dev->devitem.device_uuid = *uuid;
3349  dev->devitem.dev_id = vc->devid;
3350  dev->devitem.num_bytes = vc->size;
3351  dev->seeding = vc->seeding;
3352  dev->readonly = dev->seeding;
3353  dev->reloc = false;
3354  dev->removable = false;
3355  dev->disk_num = vc->disk_num;
3356  dev->part_num = vc->part_num;
3357  dev->num_trim_entries = 0;
3358  InitializeListHead(&dev->trim_list);
3359 
3361  Vcb->devices_loaded++;
3362 
3364 
3365  return dev;
3366  }
3367 
3368  le = le->Flink;
3369  }
3370  }
3371 
3373 
3374 end:
3375  WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3376  uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
3377  uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
3378 
3379  return NULL;
3380 }
3381 
3383  NTSTATUS Status;
3385 
3386  Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), true, NULL);
3387 
3388  if (!NT_SUCCESS(Status)) {
3389  ERR("dev_ioctl returned %08lx\n", Status);
3390  return false;
3391  }
3392 
3393  return shi.MediaRemovable != 0 ? true : false;
3394 }
3395 
3397  NTSTATUS Status;
3398  ULONG cc;
3400 
3401  Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
3402 
3403  if (!NT_SUCCESS(Status)) {
3404  ERR("dev_ioctl returned %08lx\n", Status);
3405  return 0;
3406  }
3407 
3408  if (iosb.Information < sizeof(ULONG)) {
3409  ERR("iosb.Information was too short\n");
3410  return 0;
3411  }
3412 
3413  return cc;
3414 }
3415 
3417  NTSTATUS Status;
3418  ULONG aptelen;
3419  ATA_PASS_THROUGH_EX* apte;
3422 
3423  dev->removable = is_device_removable(dev->devobj);
3424  dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
3425 
3426  if (get_nums) {
3428 
3430  &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
3431 
3432  if (!NT_SUCCESS(Status)) {
3433  WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status);
3434  dev->disk_num = 0xffffffff;
3435  dev->part_num = 0xffffffff;
3436  } else {
3437  dev->disk_num = sdn.DeviceNumber;
3438  dev->part_num = sdn.PartitionNumber;
3439  }
3440  }
3441 
3442  dev->trim = false;
3443  dev->readonly = dev->seeding;
3444  dev->reloc = false;
3445  dev->num_trim_entries = 0;
3446  dev->stats_changed = false;
3447  InitializeListHead(&dev->trim_list);
3448 
3449  if (!dev->readonly) {
3451  NULL, 0, true, NULL);
3453  dev->readonly = true;
3454  }
3455 
3456  aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
3457  apte = ExAllocatePoolWithTag(NonPagedPool, aptelen, ALLOC_TAG);
3458  if (!apte) {
3459  ERR("out of memory\n");
3460  return;
3461  }
3462 
3463  RtlZeroMemory(apte, aptelen);
3464 
3465  apte->Length = sizeof(ATA_PASS_THROUGH_EX);
3466  apte->AtaFlags = ATA_FLAGS_DATA_IN;
3467  apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
3468  apte->TimeOutValue = 3;
3469  apte->DataBufferOffset = apte->Length;
3471 
3472  Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
3473  apte, aptelen, true, NULL);
3474 
3475  if (!NT_SUCCESS(Status))
3476  TRACE("IOCTL_ATA_PASS_THROUGH returned %08lx for IDENTIFY DEVICE\n", Status);
3477  else {
3479 
3480  if (idd->CommandSetSupport.FlushCache) {
3481  dev->can_flush = true;
3482  TRACE("FLUSH CACHE supported\n");
3483  } else
3484  TRACE("FLUSH CACHE not supported\n");
3485  }
3486 
3487  ExFreePool(apte);
3488 
3489 #ifdef DEBUG_TRIM_EMULATION
3490  dev->trim = true;
3491  Vcb->trim = true;
3492 #else
3495  spq.AdditionalParameters[0] = 0;
3496 
3498  &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), true, NULL);
3499 
3500  if (NT_SUCCESS(Status)) {
3501  if (dtd.TrimEnabled) {
3502  dev->trim = true;
3503  Vcb->trim = true;
3504  TRACE("TRIM supported\n");
3505  } else
3506  TRACE("TRIM not supported\n");
3507  }
3508 #endif
3509 
3510  RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3511 }
3512 
3514  traverse_ptr tp, next_tp;
3515  KEY searchkey;
3516  bool b;
3517  chunk* c;
3518  NTSTATUS Status;
3519 
3520  searchkey.obj_id = 0;
3521  searchkey.obj_type = 0;
3522  searchkey.offset = 0;
3523 
3524  Vcb->data_flags = 0;
3525  Vcb->metadata_flags = 0;
3526  Vcb->system_flags = 0;
3527 
3528  Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp);
3529  if (!NT_SUCCESS(Status)) {
3530  ERR("error - find_item returned %08lx\n", Status);
3531  return Status;
3532  }
3533 
3534  do {
3535  TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3536 
3537  if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
3538  if (tp.item->size < sizeof(DEV_ITEM)) {
3539  ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
3540  } else {
3541  DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
3542  LIST_ENTRY* le;
3543  bool done = false;
3544 
3545  le = Vcb->devices.Flink;
3546  while (le != &Vcb->devices) {
3548 
3549  if (dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3550  RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
3551 
3552  if (le != Vcb->devices.Flink)
3553  init_device(Vcb, dev, true);
3554 
3555  done = true;
3556  break;
3557  }
3558 
3559  le = le->Flink;
3560  }
3561 
3562  if (!done && Vcb->vde) {
3563  volume_device_extension* vde = Vcb->vde;
3564  pdo_device_extension* pdode = vde->pdode;
3565 
3566  ExAcquireResourceSharedLite(&pdode->child_lock, true);
3567 
3568  if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3569  le = pdode->children.Flink;
3570 
3571  while (le != &pdode->children) {
3573 
3574  if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3575  device* dev;
3576 
3578  if (!dev) {
3580  ERR("out of memory\n");
3582  }
3583 
3584  RtlZeroMemory(dev, sizeof(device));
3585 
3586  dev->devobj = vc->devobj;
3587  dev->fileobj = vc->fileobj;
3588  RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3589  dev->seeding = vc->seeding;
3590  init_device(Vcb, dev, false);
3591 
3592  if (dev->devitem.num_bytes > vc->size) {
3593  WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", tp.item->key.offset,
3594  dev->devitem.num_bytes, vc->size);
3595 
3596  dev->devitem.num_bytes = vc->size;
3597  }
3598 
3599  dev->disk_num = vc->disk_num;
3600  dev->part_num = vc->part_num;
3602  Vcb->devices_loaded++;
3603 
3604  done = true;
3605  break;
3606  }
3607 
3608  le = le->Flink;
3609  }
3610 
3611  if (!done) {
3612  if (!Vcb->options.allow_degraded) {
3613  ERR("volume not found: device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset,
3614  di->device_uuid.uuid[0], di->device_uuid.uuid[1], di->device_uuid.uuid[2], di->device_uuid.uuid[3], di->device_uuid.uuid[4], di->device_uuid.uuid[5], di->device_uuid.uuid[6], di->device_uuid.uuid[7],
3615  di->device_uuid.uuid[8], di->device_uuid.uuid[9], di->device_uuid.uuid[10], di->device_uuid.uuid[11], di->device_uuid.uuid[12], di->device_uuid.uuid[13], di->device_uuid.uuid[14], di->device_uuid.uuid[15]);
3616  } else {
3617  device* dev;
3618 
3620  if (!dev) {
3622  ERR("out of memory\n");
3624  }
3625 
3626  RtlZeroMemory(dev, sizeof(device));
3627 
3628  // Missing device, so we keep dev->devobj as NULL
3629  RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3630  InitializeListHead(&dev->trim_list);
3631 
3633  Vcb->devices_loaded++;
3634  }
3635  }
3636  } else
3637  ERR("unexpected device %I64x found\n", tp.item->key.offset);
3638 
3640  }
3641  }
3642  } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
3643  if (tp.item->size < sizeof(CHUNK_ITEM)) {
3644  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, sizeof(CHUNK_ITEM));
3645  } else {
3647 
3648  if (!c) {
3649  ERR("out of memory\n");
3651  }
3652 
3653  c->size = tp.item->size;
3654  c->offset = tp.item->key.offset;
3655  c->used = c->oldused = 0;
3656  c->cache = c->old_cache = NULL;
3657  c->created = false;
3658  c->readonly = false;
3659  c->reloc = false;
3660  c->cache_loaded = false;
3661  c->changed = false;
3662  c->space_changed = false;
3663  c->balance_num = 0;
3664 
3666 
3667  if (!c->chunk_item) {
3668  ERR("out of memory\n");
3669  ExFreePool(c);
3671  }
3672 
3673  RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
3674 
3675  if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
3676  Vcb->data_flags = c->chunk_item->type;
3677 
3678  if (c->chunk_item->type & BLOCK_FLAG_METADATA && c->chunk_item->type > Vcb->metadata_flags)
3679  Vcb->metadata_flags = c->chunk_item->type;
3680 
3681  if (c->chunk_item->type & BLOCK_FLAG_SYSTEM && c->chunk_item->type > Vcb->system_flags)
3682  Vcb->system_flags = c->chunk_item->type;
3683 
3684  if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
3685  if (c->chunk_item->sub_stripes == 0 || c->chunk_item->sub_stripes > c->chunk_item->num_stripes) {
3686  ERR("chunk %I64x: invalid stripes (num_stripes %u, sub_stripes %u)\n", c->offset, c->chunk_item->num_stripes, c->chunk_item->sub_stripes);
3687  ExFreePool(c->chunk_item);
3688  ExFreePool(c);
3689  return STATUS_INTERNAL_ERROR;
3690  }
3691  }
3692 
3693  if (c->chunk_item->num_stripes > 0) {
3694  CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3695  uint16_t i;
3696 
3697  c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
3698 
3699  if (!c->devices) {
3700  ERR("out of memory\n");
3701  ExFreePool(c->chunk_item);
3702  ExFreePool(c);
3704  }
3705 
3706  for (i = 0; i < c->chunk_item->num_stripes; i++) {
3707  c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3708  TRACE("device %u = %p\n", i, c->devices[i]);
3709 
3710  if (!c->devices[i]) {
3711  ERR("missing device\n");
3712  ExFreePool(c->chunk_item);
3713  ExFreePool(c);
3714  return STATUS_INTERNAL_ERROR;
3715  }
3716 
3717  if (c->devices[i]->readonly)
3718  c->readonly = true;
3719  }
3720  } else {
3721  ERR("chunk %I64x: number of stripes is 0\n", c->offset);
3722  ExFreePool(c->chunk_item);
3723  ExFreePool(c);
3724  return STATUS_INTERNAL_ERROR;
3725  }
3726 
3727  ExInitializeResourceLite(&c->lock);
3728  ExInitializeResourceLite(&c->changed_extents_lock);
3729 
3730  InitializeListHead(&c->space);
3731  InitializeListHead(&c->space_size);
3732  InitializeListHead(&c->deleting);
3733  InitializeListHead(&c->changed_extents);
3734 
3735  InitializeListHead(&c->range_locks);
3736  ExInitializeResourceLite(&c->range_locks_lock);
3737  KeInitializeEvent(&c->range_locks_event, NotificationEvent, false);
3738 
3739  InitializeListHead(&c->partial_stripes);
3740  ExInitializeResourceLite(&c->partial_stripes_lock);
3741 
3742  c->last_alloc_set = false;
3743 
3744  c->last_stripe = 0;
3745 
3746  InsertTailList(&Vcb->chunks, &c->list_entry);
3747 
3748  c->list_entry_balance.Flink = NULL;
3749  }
3750  }
3751 
3752  b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3753 
3754  if (b)
3755  tp = next_tp;
3756  } while (b);
3757 
3758  Vcb->log_to_phys_loaded = true;
3759 
3760  if (Vcb->data_flags == 0)
3761  Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
3762 
3763  if (Vcb->metadata_flags == 0)
3764  Vcb->metadata_flags = BLOCK_FLAG_METADATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3765 
3766  if (Vcb->system_flags == 0)
3767  Vcb->system_flags = BLOCK_FLAG_SYSTEM | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3768 
3769  if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) {
3770  Vcb->metadata_flags |= BLOCK_FLAG_DATA;
3771  Vcb->data_flags = Vcb->metadata_flags;
3772  }
3773 
3774  return STATUS_SUCCESS;
3775 }
3776 
3778  uint16_t i = 0, j;
3779  uint64_t off_start, off_end;
3780 
3781  // The Linux driver also protects all the space before the first superblock.
3782  // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3783  // evidently Linux assumes the chunk at 0 is always SINGLE.
3784  if (c->offset < superblock_addrs[0])
3785  space_list_subtract(c, c->offset, superblock_addrs[0] - c->offset, NULL);
3786 
3787  while (superblock_addrs[i] != 0) {
3788  CHUNK_ITEM* ci = c->chunk_item;
3789  CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3790 
3791  if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
3792  for (j = 0; j < ci->num_stripes; j++) {
3793  uint16_t sub_stripes = max(ci->sub_stripes, 1);
3794 
3795  if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3796 #ifdef _DEBUG
3797  uint64_t startoff;
3798  uint16_t startoffstripe;
3799 #endif
3800 
3801  TRACE("cut out superblock in chunk %I64x\n", c->offset);
3802 
3803  off_start = superblock_addrs[i] - cis[j].offset;
3804  off_start -= off_start % ci->stripe_length;
3805  off_start *= ci->num_stripes / sub_stripes;
3806  off_start += (j / sub_stripes) * ci->stripe_length;
3807 
3808  off_end = off_start + ci->stripe_length;
3809 
3810 #ifdef _DEBUG
3811  get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
3812  TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
3813  TRACE("startoff = %I64x, superblock = %I64x\n", startoff + cis[j].offset, superblock_addrs[i]);
3814 #endif
3815 
3816  space_list_subtract(c, c->offset + off_start, off_end - off_start, NULL);
3817  }
3818  }
3819  } else if (ci->type & BLOCK_FLAG_RAID5) {
3820  uint64_t stripe_size = ci->size / (ci->num_stripes - 1);
3821 
3822  for (j = 0; j < ci->num_stripes; j++) {
3823  if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3824  TRACE("cut out superblock in chunk %I64x\n", c->offset);
3825 
3826  off_start = superblock_addrs[i] - cis[j].offset;
3827  off_start -= off_start % ci->stripe_length;
3828  off_start *= ci->num_stripes - 1;
3829 
3830  off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3831  off_end *= ci->num_stripes - 1;
3832 
3833  TRACE("cutting out %I64x, size %I64x\n", c->offset + off_start, off_end - off_start);
3834 
3835  space_list_subtract(c, c->offset + off_start, off_end - off_start, NULL);
3836  }
3837  }
3838  } else if (ci->type & BLOCK_FLAG_RAID6) {
3839  uint64_t stripe_size = ci->size / (ci->num_stripes - 2);
3840 
3841  for (j = 0; j < ci->num_stripes; j++) {
3842  if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3843  TRACE("cut out superblock in chunk %I64x\n", c->offset);
3844 
3845  off_start = superblock_addrs[i] - cis[j].offset;
3846  off_start -= off_start % ci->stripe_length;
3847  off_start *= ci->num_stripes - 2;
3848 
3849  off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3850  off_end *= ci->num_stripes - 2;
3851 
3852  TRACE("cutting out %I64x, size %I64x\n", c->offset + off_start, off_end - off_start);
3853 
3854  space_list_subtract(c, c->offset + off_start, off_end - off_start, NULL);
3855  }
3856  }
3857  } else { // SINGLE, DUPLICATE, RAID1, RAID1C3, RAID1C4
3858  for (j = 0; j < ci->num_stripes; j++) {
3859  if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3860  TRACE("cut out superblock in chunk %I64x\n", c->offset);
3861 
3862  // The Linux driver protects the whole stripe in which the superblock lives
3863 
3864  off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3865  off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3866 
3867  space_list_subtract(c, c->offset + off_start, off_end - off_start, NULL);
3868  }
3869  }
3870  }
3871 
3872  i++;
3873  }
3874 }
3875 
3877  LIST_ENTRY* le = Vcb->chunks.Flink;
3878  chunk* c;
3879  KEY searchkey;
3880  traverse_ptr tp;
3881  BLOCK_GROUP_ITEM* bgi;
3882  NTSTATUS Status;
3883 
3884  searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3885 
3886  Vcb->superblock.bytes_used = 0;
3887 
3888  while (le != &Vcb->chunks) {
3890 
3891  searchkey.obj_id = c->offset;
3892  searchkey.offset = c->chunk_item->size;