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