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