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