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