[BTRFS][UBTRFS][SHELLBTRFS] Upgrade to 1.7.2
[reactos.git] / drivers / filesystems / btrfs / btrfs.c
1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #ifdef _DEBUG
19 #define DEBUG
20 #endif
21
22 #include "btrfs_drv.h"
23 #include "xxhash.h"
24 #include "crc32c.h"
25 #ifndef __REACTOS__
26 #ifndef _MSC_VER
27 #include <cpuid.h>
28 #else
29 #include <intrin.h>
30 #endif
31 #endif // __REACTOS__
32 #include <ntddscsi.h>
33 #include "btrfs.h"
34 #include <ata.h>
35
36 #ifndef _MSC_VER
37 #include <initguid.h>
38 #include <ntddstor.h>
39 #undef INITGUID
40 #endif
41
42 #include <ntdddisk.h>
43 #include <ntddvol.h>
44
45 #ifdef _MSC_VER
46 #include <initguid.h>
47 #include <ntddstor.h>
48 #undef INITGUID
49 #endif
50
51 #ifdef _MSC_VER
52 #include <ntstrsafe.h>
53 #else
54 NTSTATUS RtlStringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList); // not in mingw
55 #endif
56
57 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
58 BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
59 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
60 BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD | BTRFS_INCOMPAT_FLAGS_METADATA_UUID | BTRFS_INCOMPAT_FLAGS_RAID1C34)
61 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
62
63 static const WCHAR device_name[] = {'\\','B','t','r','f','s',0};
64 static const WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
65
66 DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
67
68 PDRIVER_OBJECT drvobj;
69 PDEVICE_OBJECT master_devobj, busobj;
70 #ifndef __REACTOS__
71 bool have_sse2 = false;
72 #endif
73 uint64_t num_reads = 0;
74 LIST_ENTRY uid_map_list, gid_map_list;
75 LIST_ENTRY VcbList;
76 ERESOURCE global_loading_lock;
77 uint32_t debug_log_level = 0;
78 uint32_t mount_compress = 0;
79 uint32_t mount_compress_force = 0;
80 uint32_t mount_compress_type = 0;
81 uint32_t mount_zlib_level = 3;
82 uint32_t mount_zstd_level = 3;
83 uint32_t mount_flush_interval = 30;
84 uint32_t mount_max_inline = 2048;
85 uint32_t mount_skip_balance = 0;
86 uint32_t mount_no_barrier = 0;
87 uint32_t mount_no_trim = 0;
88 uint32_t mount_clear_cache = 0;
89 uint32_t mount_allow_degraded = 0;
90 uint32_t mount_readonly = 0;
91 uint32_t mount_no_root_dir = 0;
92 uint32_t no_pnp = 0;
93 bool log_started = false;
94 UNICODE_STRING log_device, log_file, registry_path;
95 tPsUpdateDiskCounters fPsUpdateDiskCounters;
96 tCcCopyReadEx fCcCopyReadEx;
97 tCcCopyWriteEx fCcCopyWriteEx;
98 tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx;
99 tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters;
100 tIoUnregisterPlugPlayNotificationEx fIoUnregisterPlugPlayNotificationEx;
101 tFsRtlGetEcpListFromIrp fFsRtlGetEcpListFromIrp;
102 tFsRtlGetNextExtraCreateParameter fFsRtlGetNextExtraCreateParameter;
103 tFsRtlValidateReparsePointBuffer fFsRtlValidateReparsePointBuffer;
104 tFsRtlCheckLockForOplockRequest fFsRtlCheckLockForOplockRequest;
105 tFsRtlAreThereCurrentOrInProgressFileLocks fFsRtlAreThereCurrentOrInProgressFileLocks;
106 bool diskacc = false;
107 void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL;
108 ERESOURCE pdo_list_lock, mapping_lock;
109 LIST_ENTRY pdo_list;
110 bool finished_probing = false;
111 HANDLE degraded_wait_handle = NULL, mountmgr_thread_handle = NULL;
112 bool degraded_wait = true;
113 KEVENT mountmgr_thread_event;
114 bool shutting_down = false;
115 ERESOURCE boot_lock;
116 extern uint64_t boot_subvol;
117
118 #ifdef _DEBUG
119 PFILE_OBJECT comfo = NULL;
120 PDEVICE_OBJECT comdo = NULL;
121 HANDLE log_handle = NULL;
122 ERESOURCE log_lock;
123 HANDLE serial_thread_handle = NULL;
124
125 static void init_serial(bool first_time);
126 #endif
127
128 static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp);
129
130 typedef struct {
131 KEVENT Event;
132 IO_STATUS_BLOCK iosb;
133 } read_context;
134
135 // no longer in Windows headers??
136 extern BOOLEAN WdmlibRtlIsNtDdiVersionAvailable(ULONG Version);
137
138 #ifdef _DEBUG
139 _Function_class_(IO_COMPLETION_ROUTINE)
140 static NTSTATUS __stdcall dbg_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
141 read_context* context = conptr;
142
143 UNUSED(DeviceObject);
144
145 context->iosb = Irp->IoStatus;
146 KeSetEvent(&context->Event, 0, false);
147
148 return STATUS_MORE_PROCESSING_REQUIRED;
149 }
150
151 #define DEBUG_MESSAGE_LEN 1024
152
153 #ifdef DEBUG_LONG_MESSAGES
154 void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) {
155 #else
156 void _debug_message(_In_ const char* func, _In_ char* s, ...) {
157 #endif
158 LARGE_INTEGER offset;
159 PIO_STACK_LOCATION IrpSp;
160 NTSTATUS Status;
161 PIRP Irp;
162 va_list ap;
163 char *buf2, *buf;
164 read_context context;
165 uint32_t length;
166
167 buf2 = ExAllocatePoolWithTag(NonPagedPool, DEBUG_MESSAGE_LEN, ALLOC_TAG);
168
169 if (!buf2) {
170 DbgPrint("Couldn't allocate buffer in debug_message\n");
171 return;
172 }
173
174 #ifdef DEBUG_LONG_MESSAGES
175 sprintf(buf2, "%p:%s:%s:%u:", (void*)PsGetCurrentThread(), func, file, line);
176 #else
177 sprintf(buf2, "%p:%s:", (void*)PsGetCurrentThread(), func);
178 #endif
179 buf = &buf2[strlen(buf2)];
180
181 va_start(ap, s);
182
183 RtlStringCbVPrintfA(buf, DEBUG_MESSAGE_LEN - strlen(buf2), s, ap);
184
185 ExAcquireResourceSharedLite(&log_lock, true);
186
187 if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
188 DbgPrint(buf2);
189 } else if (log_device.Length > 0) {
190 if (!comdo) {
191 DbgPrint(buf2);
192 goto exit2;
193 }
194
195 length = (uint32_t)strlen(buf2);
196
197 offset.u.LowPart = 0;
198 offset.u.HighPart = 0;
199
200 RtlZeroMemory(&context, sizeof(read_context));
201
202 KeInitializeEvent(&context.Event, NotificationEvent, false);
203
204 Irp = IoAllocateIrp(comdo->StackSize, false);
205
206 if (!Irp) {
207 DbgPrint("IoAllocateIrp failed\n");
208 goto exit2;
209 }
210
211 IrpSp = IoGetNextIrpStackLocation(Irp);
212 IrpSp->MajorFunction = IRP_MJ_WRITE;
213 IrpSp->FileObject = comfo;
214
215 if (comdo->Flags & DO_BUFFERED_IO) {
216 Irp->AssociatedIrp.SystemBuffer = buf2;
217
218 Irp->Flags = IRP_BUFFERED_IO;
219 } else if (comdo->Flags & DO_DIRECT_IO) {
220 Irp->MdlAddress = IoAllocateMdl(buf2, length, false, false, NULL);
221 if (!Irp->MdlAddress) {
222 DbgPrint("IoAllocateMdl failed\n");
223 goto exit;
224 }
225
226 MmBuildMdlForNonPagedPool(Irp->MdlAddress);
227 } else {
228 Irp->UserBuffer = buf2;
229 }
230
231 IrpSp->Parameters.Write.Length = length;
232 IrpSp->Parameters.Write.ByteOffset = offset;
233
234 Irp->UserIosb = &context.iosb;
235
236 Irp->UserEvent = &context.Event;
237
238 IoSetCompletionRoutine(Irp, dbg_completion, &context, true, true, true);
239
240 Status = IoCallDriver(comdo, Irp);
241
242 if (Status == STATUS_PENDING) {
243 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
244 Status = context.iosb.Status;
245 }
246
247 if (comdo->Flags & DO_DIRECT_IO)
248 IoFreeMdl(Irp->MdlAddress);
249
250 if (!NT_SUCCESS(Status)) {
251 DbgPrint("failed to write to COM1 - error %08lx\n", Status);
252 goto exit;
253 }
254
255 exit:
256 IoFreeIrp(Irp);
257 } else if (log_handle != NULL) {
258 IO_STATUS_BLOCK iosb;
259
260 length = (uint32_t)strlen(buf2);
261
262 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
263
264 if (!NT_SUCCESS(Status)) {
265 DbgPrint("failed to write to file - error %08lx\n", Status);
266 }
267 }
268
269 exit2:
270 ExReleaseResourceLite(&log_lock);
271
272 va_end(ap);
273
274 if (buf2)
275 ExFreePool(buf2);
276 }
277 #endif
278
279 bool is_top_level(_In_ PIRP Irp) {
280 if (!IoGetTopLevelIrp()) {
281 IoSetTopLevelIrp(Irp);
282 return true;
283 }
284
285 return false;
286 }
287
288 _Function_class_(DRIVER_UNLOAD)
289 static void __stdcall DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
290 UNICODE_STRING dosdevice_nameW;
291
292 TRACE("(%p)\n", DriverObject);
293
294 dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
295 dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) - sizeof(WCHAR);
296
297 IoDeleteSymbolicLink(&dosdevice_nameW);
298 IoDeleteDevice(DriverObject->DeviceObject);
299
300 while (!IsListEmpty(&uid_map_list)) {
301 LIST_ENTRY* le = RemoveHeadList(&uid_map_list);
302 uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
303
304 ExFreePool(um->sid);
305
306 ExFreePool(um);
307 }
308
309 while (!IsListEmpty(&gid_map_list)) {
310 gid_map* gm = CONTAINING_RECORD(RemoveHeadList(&gid_map_list), gid_map, listentry);
311
312 ExFreePool(gm->sid);
313 ExFreePool(gm);
314 }
315
316 // FIXME - free volumes and their devpaths
317
318 #ifdef _DEBUG
319 if (comfo)
320 ObDereferenceObject(comfo);
321
322 if (log_handle)
323 ZwClose(log_handle);
324 #endif
325
326 ExDeleteResourceLite(&global_loading_lock);
327 ExDeleteResourceLite(&pdo_list_lock);
328
329 if (log_device.Buffer)
330 ExFreePool(log_device.Buffer);
331
332 if (log_file.Buffer)
333 ExFreePool(log_file.Buffer);
334
335 if (registry_path.Buffer)
336 ExFreePool(registry_path.Buffer);
337
338 #ifdef _DEBUG
339 ExDeleteResourceLite(&log_lock);
340 #endif
341 ExDeleteResourceLite(&mapping_lock);
342 }
343
344 static bool get_last_inode(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_opt_ PIRP Irp) {
345 KEY searchkey;
346 traverse_ptr tp, prev_tp;
347 NTSTATUS Status;
348
349 // get last entry
350 searchkey.obj_id = 0xffffffffffffffff;
351 searchkey.obj_type = 0xff;
352 searchkey.offset = 0xffffffffffffffff;
353
354 Status = find_item(Vcb, r, &tp, &searchkey, false, Irp);
355 if (!NT_SUCCESS(Status)) {
356 ERR("error - find_item returned %08lx\n", Status);
357 return false;
358 }
359
360 if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
361 r->lastinode = tp.item->key.obj_id;
362 TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
363 return true;
364 }
365
366 while (find_prev_item(Vcb, &tp, &prev_tp, Irp)) {
367 tp = prev_tp;
368
369 TRACE("moving on to %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
370
371 if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
372 r->lastinode = tp.item->key.obj_id;
373 TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
374 return true;
375 }
376 }
377
378 r->lastinode = SUBVOL_ROOT_INODE;
379
380 WARN("no INODE_ITEMs in tree %I64x\n", r->id);
381
382 return true;
383 }
384
385 _Success_(return)
386 static bool extract_xattr(_In_reads_bytes_(size) void* item, _In_ USHORT size, _In_z_ char* name, _Out_ uint8_t** data, _Out_ uint16_t* datalen) {
387 DIR_ITEM* xa = (DIR_ITEM*)item;
388 USHORT xasize;
389
390 while (true) {
391 if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
392 WARN("DIR_ITEM is truncated\n");
393 return false;
394 }
395
396 if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
397 TRACE("found xattr %s\n", name);
398
399 *datalen = xa->m;
400
401 if (xa->m > 0) {
402 *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
403 if (!*data) {
404 ERR("out of memory\n");
405 return false;
406 }
407
408 RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
409 } else
410 *data = NULL;
411
412 return true;
413 }
414
415 xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
416
417 if (size > xasize) {
418 size -= xasize;
419 xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
420 } else
421 break;
422 }
423
424 TRACE("xattr %s not found\n", name);
425
426 return false;
427 }
428
429 _Success_(return)
430 bool get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* subvol, _In_ uint64_t inode, _In_z_ char* name, _In_ uint32_t crc32,
431 _Out_ uint8_t** data, _Out_ uint16_t* datalen, _In_opt_ PIRP Irp) {
432 KEY searchkey;
433 traverse_ptr tp;
434 NTSTATUS Status;
435
436 TRACE("(%p, %I64x, %I64x, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
437
438 searchkey.obj_id = inode;
439 searchkey.obj_type = TYPE_XATTR_ITEM;
440 searchkey.offset = crc32;
441
442 Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
443 if (!NT_SUCCESS(Status)) {
444 ERR("error - find_item returned %08lx\n", Status);
445 return false;
446 }
447
448 if (keycmp(tp.item->key, searchkey)) {
449 TRACE("could not find item (%I64x,%x,%I64x)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
450 return false;
451 }
452
453 if (tp.item->size < sizeof(DIR_ITEM)) {
454 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
455 return false;
456 }
457
458 return extract_xattr(tp.item->data, tp.item->size, name, data, datalen);
459 }
460
461 _Dispatch_type_(IRP_MJ_CLOSE)
462 _Function_class_(DRIVER_DISPATCH)
463 static NTSTATUS __stdcall drv_close(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
464 NTSTATUS Status;
465 PIO_STACK_LOCATION IrpSp;
466 device_extension* Vcb = DeviceObject->DeviceExtension;
467 bool top_level;
468
469 FsRtlEnterFileSystem();
470
471 TRACE("close\n");
472
473 top_level = is_top_level(Irp);
474
475 if (DeviceObject == master_devobj) {
476 TRACE("Closing file system\n");
477 Status = STATUS_SUCCESS;
478 goto end;
479 } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
480 Status = vol_close(DeviceObject, Irp);
481 goto end;
482 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
483 Status = STATUS_INVALID_PARAMETER;
484 goto end;
485 }
486
487 IrpSp = IoGetCurrentIrpStackLocation(Irp);
488
489 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
490
491 Status = close_file(IrpSp->FileObject, Irp);
492
493 end:
494 Irp->IoStatus.Status = Status;
495 Irp->IoStatus.Information = 0;
496
497 IoCompleteRequest( Irp, IO_DISK_INCREMENT );
498
499 if (top_level)
500 IoSetTopLevelIrp(NULL);
501
502 TRACE("returning %08lx\n", Status);
503
504 FsRtlExitFileSystem();
505
506 return Status;
507 }
508
509 _Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
510 _Function_class_(DRIVER_DISPATCH)
511 static NTSTATUS __stdcall drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
512 NTSTATUS Status;
513 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
514 PFILE_OBJECT FileObject = IrpSp->FileObject;
515 fcb* fcb = FileObject->FsContext;
516 device_extension* Vcb = DeviceObject->DeviceExtension;
517 bool top_level;
518
519 FsRtlEnterFileSystem();
520
521 TRACE("flush buffers\n");
522
523 top_level = is_top_level(Irp);
524
525 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
526 Status = vol_flush_buffers(DeviceObject, Irp);
527 goto end;
528 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
529 Status = STATUS_SUCCESS;
530 goto end;
531 }
532
533 if (!fcb) {
534 ERR("fcb was NULL\n");
535 Status = STATUS_SUCCESS;
536 goto end;
537 }
538
539 if (fcb == Vcb->volume_fcb) {
540 Status = STATUS_SUCCESS;
541 goto end;
542 }
543
544 FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
545
546 Irp->IoStatus.Information = 0;
547
548 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
549
550 Status = STATUS_SUCCESS;
551 Irp->IoStatus.Status = Status;
552
553 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
554 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &Irp->IoStatus);
555
556 if (fcb->Header.PagingIoResource) {
557 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
558 ExReleaseResourceLite(fcb->Header.PagingIoResource);
559 }
560
561 Status = Irp->IoStatus.Status;
562 }
563
564 end:
565 IoCompleteRequest(Irp, IO_NO_INCREMENT);
566
567 TRACE("returning %08lx\n", Status);
568
569 if (top_level)
570 IoSetTopLevelIrp(NULL);
571
572 FsRtlExitFileSystem();
573
574 return Status;
575 }
576
577 static void calculate_total_space(_In_ device_extension* Vcb, _Out_ uint64_t* totalsize, _Out_ uint64_t* freespace) {
578 uint64_t nfactor, dfactor, sectors_used;
579
580 if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) {
581 nfactor = 1;
582 dfactor = 2;
583 } else if (Vcb->data_flags & BLOCK_FLAG_RAID5) {
584 nfactor = Vcb->superblock.num_devices - 1;
585 dfactor = Vcb->superblock.num_devices;
586 } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
587 nfactor = Vcb->superblock.num_devices - 2;
588 dfactor = Vcb->superblock.num_devices;
589 } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C3) {
590 nfactor = 1;
591 dfactor = 3;
592 } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C4) {
593 nfactor = 1;
594 dfactor = 4;
595 } else {
596 nfactor = 1;
597 dfactor = 1;
598 }
599
600 sectors_used = (Vcb->superblock.bytes_used / Vcb->superblock.sector_size) * nfactor / dfactor;
601
602 *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) * nfactor / dfactor;
603 *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
604 }
605
606 #ifndef __REACTOS__
607 #define INIT_UNICODE_STRING(var, val) UNICODE_STRING us##var; us##var.Buffer = (WCHAR*)val; us##var.Length = us##var.MaximumLength = sizeof(val) - sizeof(WCHAR);
608
609 // This function exists because we have to lie about our FS type in certain situations.
610 // MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
611 // it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
612 // The command mklink refuses to create hard links on anything other than NTFS, so we have to
613 // blacklist cmd.exe too.
614
615 static bool lie_about_fs_type() {
616 NTSTATUS Status;
617 PROCESS_BASIC_INFORMATION pbi;
618 PPEB peb;
619 LIST_ENTRY* le;
620 ULONG retlen;
621 #ifdef _AMD64_
622 ULONG_PTR wow64info;
623 #endif
624
625 INIT_UNICODE_STRING(mpr, L"MPR.DLL");
626 INIT_UNICODE_STRING(cmd, L"CMD.EXE");
627 INIT_UNICODE_STRING(fsutil, L"FSUTIL.EXE");
628 INIT_UNICODE_STRING(storsvc, L"STORSVC.DLL");
629
630 if (!PsGetCurrentProcess())
631 return false;
632
633 #ifdef _AMD64_
634 Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &wow64info, sizeof(wow64info), NULL);
635
636 if (NT_SUCCESS(Status) && wow64info != 0)
637 return true;
638 #endif
639
640 Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
641
642 if (!NT_SUCCESS(Status)) {
643 ERR("ZwQueryInformationProcess returned %08lx\n", Status);
644 return false;
645 }
646
647 if (!pbi.PebBaseAddress)
648 return false;
649
650 peb = pbi.PebBaseAddress;
651
652 if (!peb->Ldr)
653 return false;
654
655 le = peb->Ldr->InMemoryOrderModuleList.Flink;
656 while (le != &peb->Ldr->InMemoryOrderModuleList) {
657 LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(le, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
658 bool blacklist = false;
659
660 if (entry->FullDllName.Length >= usmpr.Length) {
661 UNICODE_STRING name;
662
663 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usmpr.Length) / sizeof(WCHAR)];
664 name.Length = name.MaximumLength = usmpr.Length;
665
666 blacklist = FsRtlAreNamesEqual(&name, &usmpr, true, NULL);
667 }
668
669 if (!blacklist && entry->FullDllName.Length >= uscmd.Length) {
670 UNICODE_STRING name;
671
672 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - uscmd.Length) / sizeof(WCHAR)];
673 name.Length = name.MaximumLength = uscmd.Length;
674
675 blacklist = FsRtlAreNamesEqual(&name, &uscmd, true, NULL);
676 }
677
678 if (!blacklist && entry->FullDllName.Length >= usfsutil.Length) {
679 UNICODE_STRING name;
680
681 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usfsutil.Length) / sizeof(WCHAR)];
682 name.Length = name.MaximumLength = usfsutil.Length;
683
684 blacklist = FsRtlAreNamesEqual(&name, &usfsutil, true, NULL);
685 }
686
687 if (!blacklist && entry->FullDllName.Length >= usstorsvc.Length) {
688 UNICODE_STRING name;
689
690 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usstorsvc.Length) / sizeof(WCHAR)];
691 name.Length = name.MaximumLength = usstorsvc.Length;
692
693 blacklist = FsRtlAreNamesEqual(&name, &usstorsvc, true, NULL);
694 }
695
696 if (blacklist) {
697 void** frames;
698 ULONG i, num_frames;
699
700 frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
701 if (!frames) {
702 ERR("out of memory\n");
703 return false;
704 }
705
706 num_frames = RtlWalkFrameChain(frames, 256, 1);
707
708 for (i = 0; i < num_frames; i++) {
709 // entry->Reserved3[1] appears to be the image size
710 if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
711 ExFreePool(frames);
712 return true;
713 }
714 }
715
716 ExFreePool(frames);
717 }
718
719 le = le->Flink;
720 }
721
722 return false;
723 }
724 #endif // __REACTOS__
725
726 // version of RtlUTF8ToUnicodeN for Vista and below
727 NTSTATUS utf8_to_utf16(WCHAR* dest, ULONG dest_max, ULONG* dest_len, char* src, ULONG src_len) {
728 NTSTATUS Status = STATUS_SUCCESS;
729 uint8_t* in = (uint8_t*)src;
730 uint16_t* out = (uint16_t*)dest;
731 ULONG needed = 0, left = dest_max / sizeof(uint16_t);
732 #ifdef __REACTOS__
733 ULONG i;
734
735 for (i = 0; i < src_len; ++i) {
736 #else
737
738 for (ULONG i = 0; i < src_len; i++) {
739 #endif
740 uint32_t cp;
741
742 if (!(in[i] & 0x80))
743 cp = in[i];
744 else if ((in[i] & 0xe0) == 0xc0) {
745 if (i == src_len - 1 || (in[i+1] & 0xc0) != 0x80) {
746 cp = 0xfffd;
747 Status = STATUS_SOME_NOT_MAPPED;
748 } else {
749 cp = ((in[i] & 0x1f) << 6) | (in[i+1] & 0x3f);
750 i++;
751 }
752 } else if ((in[i] & 0xf0) == 0xe0) {
753 if (i >= src_len - 2 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80) {
754 cp = 0xfffd;
755 Status = STATUS_SOME_NOT_MAPPED;
756 } else {
757 cp = ((in[i] & 0xf) << 12) | ((in[i+1] & 0x3f) << 6) | (in[i+2] & 0x3f);
758 i += 2;
759 }
760 } else if ((in[i] & 0xf8) == 0xf0) {
761 if (i >= src_len - 3 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80 || (in[i+3] & 0xc0) != 0x80) {
762 cp = 0xfffd;
763 Status = STATUS_SOME_NOT_MAPPED;
764 } else {
765 cp = ((in[i] & 0x7) << 18) | ((in[i+1] & 0x3f) << 12) | ((in[i+2] & 0x3f) << 6) | (in[i+3] & 0x3f);
766 i += 3;
767 }
768 } else {
769 cp = 0xfffd;
770 Status = STATUS_SOME_NOT_MAPPED;
771 }
772
773 if (cp > 0x10ffff) {
774 cp = 0xfffd;
775 Status = STATUS_SOME_NOT_MAPPED;
776 }
777
778 if (dest) {
779 if (cp <= 0xffff) {
780 if (left < 1)
781 return STATUS_BUFFER_OVERFLOW;
782
783 *out = (uint16_t)cp;
784 out++;
785
786 left--;
787 } else {
788 if (left < 2)
789 return STATUS_BUFFER_OVERFLOW;
790
791 cp -= 0x10000;
792
793 *out = 0xd800 | ((cp & 0xffc00) >> 10);
794 out++;
795
796 *out = 0xdc00 | (cp & 0x3ff);
797 out++;
798
799 left -= 2;
800 }
801 }
802
803 if (cp <= 0xffff)
804 needed += sizeof(uint16_t);
805 else
806 needed += 2 * sizeof(uint16_t);
807 }
808
809 if (dest_len)
810 *dest_len = needed;
811
812 return Status;
813 }
814
815 // version of RtlUnicodeToUTF8N for Vista and below
816 NTSTATUS utf16_to_utf8(char* dest, ULONG dest_max, ULONG* dest_len, WCHAR* src, ULONG src_len) {
817 NTSTATUS Status = STATUS_SUCCESS;
818 uint16_t* in = (uint16_t*)src;
819 uint8_t* out = (uint8_t*)dest;
820 ULONG in_len = src_len / sizeof(uint16_t);
821 ULONG needed = 0, left = dest_max;
822 #ifdef __REACTOS__
823 ULONG i = 0;
824
825 for (i = 0; i < in_len; i++) {
826 #else
827
828 for (ULONG i = 0; i < in_len; i++) {
829 #endif
830 uint32_t cp = *in;
831 in++;
832
833 if ((cp & 0xfc00) == 0xd800) {
834 if (i == in_len - 1 || (*in & 0xfc00) != 0xdc00) {
835 cp = 0xfffd;
836 Status = STATUS_SOME_NOT_MAPPED;
837 } else {
838 cp = (cp & 0x3ff) << 10;
839 cp |= *in & 0x3ff;
840 cp += 0x10000;
841
842 in++;
843 i++;
844 }
845 } else if ((cp & 0xfc00) == 0xdc00) {
846 cp = 0xfffd;
847 Status = STATUS_SOME_NOT_MAPPED;
848 }
849
850 if (cp > 0x10ffff) {
851 cp = 0xfffd;
852 Status = STATUS_SOME_NOT_MAPPED;
853 }
854
855 if (dest) {
856 if (cp < 0x80) {
857 if (left < 1)
858 return STATUS_BUFFER_OVERFLOW;
859
860 *out = (uint8_t)cp;
861 out++;
862
863 left--;
864 } else if (cp < 0x800) {
865 if (left < 2)
866 return STATUS_BUFFER_OVERFLOW;
867
868 *out = 0xc0 | ((cp & 0x7c0) >> 6);
869 out++;
870
871 *out = 0x80 | (cp & 0x3f);
872 out++;
873
874 left -= 2;
875 } else if (cp < 0x10000) {
876 if (left < 3)
877 return STATUS_BUFFER_OVERFLOW;
878
879 *out = 0xe0 | ((cp & 0xf000) >> 12);
880 out++;
881
882 *out = 0x80 | ((cp & 0xfc0) >> 6);
883 out++;
884
885 *out = 0x80 | (cp & 0x3f);
886 out++;
887
888 left -= 3;
889 } else {
890 if (left < 4)
891 return STATUS_BUFFER_OVERFLOW;
892
893 *out = 0xf0 | ((cp & 0x1c0000) >> 18);
894 out++;
895
896 *out = 0x80 | ((cp & 0x3f000) >> 12);
897 out++;
898
899 *out = 0x80 | ((cp & 0xfc0) >> 6);
900 out++;
901
902 *out = 0x80 | (cp & 0x3f);
903 out++;
904
905 left -= 4;
906 }
907 }
908
909 if (cp < 0x80)
910 needed++;
911 else if (cp < 0x800)
912 needed += 2;
913 else if (cp < 0x10000)
914 needed += 3;
915 else
916 needed += 4;
917 }
918
919 if (dest_len)
920 *dest_len = needed;
921
922 return Status;
923 }
924
925 _Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION)
926 _Function_class_(DRIVER_DISPATCH)
927 static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
928 PIO_STACK_LOCATION IrpSp;
929 NTSTATUS Status;
930 ULONG BytesCopied = 0;
931 device_extension* Vcb = DeviceObject->DeviceExtension;
932 bool top_level;
933
934 FsRtlEnterFileSystem();
935
936 TRACE("query volume information\n");
937 top_level = is_top_level(Irp);
938
939 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
940 Status = vol_query_volume_information(DeviceObject, Irp);
941 goto end;
942 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
943 Status = STATUS_INVALID_PARAMETER;
944 goto end;
945 }
946
947 IrpSp = IoGetCurrentIrpStackLocation(Irp);
948
949 Status = STATUS_NOT_IMPLEMENTED;
950
951 switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
952 case FileFsAttributeInformation:
953 {
954 FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
955 bool overflow = false;
956 #ifndef __REACTOS__
957 static const WCHAR ntfs[] = L"NTFS";
958 #endif
959 static const WCHAR btrfs[] = L"Btrfs";
960 const WCHAR* fs_name;
961 ULONG fs_name_len, orig_fs_name_len;
962
963 #ifndef __REACTOS__
964 if (Irp->RequestorMode == UserMode && lie_about_fs_type()) {
965 fs_name = ntfs;
966 orig_fs_name_len = fs_name_len = sizeof(ntfs) - sizeof(WCHAR);
967 } else {
968 fs_name = btrfs;
969 orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
970 }
971 #else
972 fs_name = btrfs;
973 orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
974 #endif
975
976 TRACE("FileFsAttributeInformation\n");
977
978 if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
979 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
980 fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
981 else
982 fs_name_len = 0;
983
984 overflow = true;
985 }
986
987 data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
988 FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
989 FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS |
990 FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_BLOCK_REFCOUNTING |
991 FILE_SUPPORTS_POSIX_UNLINK_RENAME;
992 if (Vcb->readonly)
993 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
994
995 // should also be FILE_FILE_COMPRESSION when supported
996 data->MaximumComponentNameLength = 255; // FIXME - check
997 data->FileSystemNameLength = orig_fs_name_len;
998 RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
999
1000 BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
1001 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
1002 break;
1003 }
1004
1005 case FileFsDeviceInformation:
1006 {
1007 FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
1008
1009 TRACE("FileFsDeviceInformation\n");
1010
1011 ffdi->DeviceType = FILE_DEVICE_DISK;
1012
1013 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1014 ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
1015 ExReleaseResourceLite(&Vcb->tree_lock);
1016
1017 if (Vcb->readonly)
1018 ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
1019 else
1020 ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
1021
1022 BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
1023 Status = STATUS_SUCCESS;
1024
1025 break;
1026 }
1027
1028 case FileFsFullSizeInformation:
1029 {
1030 FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1031
1032 TRACE("FileFsFullSizeInformation\n");
1033
1034 calculate_total_space(Vcb, (uint64_t*)&ffsi->TotalAllocationUnits.QuadPart, (uint64_t*)&ffsi->ActualAvailableAllocationUnits.QuadPart);
1035 ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
1036 ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1037 ffsi->BytesPerSector = 512;
1038
1039 BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
1040 Status = STATUS_SUCCESS;
1041
1042 break;
1043 }
1044
1045 case FileFsObjectIdInformation:
1046 {
1047 FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
1048
1049 TRACE("FileFsObjectIdInformation\n");
1050
1051 RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
1052 RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
1053
1054 BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
1055 Status = STATUS_SUCCESS;
1056
1057 break;
1058 }
1059
1060 case FileFsSizeInformation:
1061 {
1062 FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1063
1064 TRACE("FileFsSizeInformation\n");
1065
1066 calculate_total_space(Vcb, (uint64_t*)&ffsi->TotalAllocationUnits.QuadPart, (uint64_t*)&ffsi->AvailableAllocationUnits.QuadPart);
1067 ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1068 ffsi->BytesPerSector = 512;
1069
1070 BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
1071 Status = STATUS_SUCCESS;
1072
1073 break;
1074 }
1075
1076 case FileFsVolumeInformation:
1077 {
1078 FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1079 FILE_FS_VOLUME_INFORMATION ffvi;
1080 bool overflow = false;
1081 ULONG label_len, orig_label_len;
1082
1083 TRACE("FileFsVolumeInformation\n");
1084 TRACE("max length = %lu\n", IrpSp->Parameters.QueryVolume.Length);
1085
1086 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1087
1088 Status = utf8_to_utf16(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1089 if (!NT_SUCCESS(Status)) {
1090 ERR("utf8_to_utf16 returned %08lx\n", Status);
1091 ExReleaseResourceLite(&Vcb->tree_lock);
1092 break;
1093 }
1094
1095 orig_label_len = label_len;
1096
1097 if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
1098 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
1099 label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
1100 else
1101 label_len = 0;
1102
1103 overflow = true;
1104 }
1105
1106 TRACE("label_len = %lu\n", label_len);
1107
1108 ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
1109 ffvi.VolumeSerialNumber = Vcb->superblock.uuid.uuid[12] << 24 | Vcb->superblock.uuid.uuid[13] << 16 | Vcb->superblock.uuid.uuid[14] << 8 | Vcb->superblock.uuid.uuid[15];
1110 ffvi.VolumeLabelLength = orig_label_len;
1111 ffvi.SupportsObjects = false;
1112
1113 RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
1114
1115 if (label_len > 0) {
1116 ULONG bytecount;
1117
1118 Status = utf8_to_utf16(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1119 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
1120 ERR("utf8_to_utf16 returned %08lx\n", Status);
1121 ExReleaseResourceLite(&Vcb->tree_lock);
1122 break;
1123 }
1124
1125 TRACE("label = %.*S\n", (int)(label_len / sizeof(WCHAR)), data->VolumeLabel);
1126 }
1127
1128 ExReleaseResourceLite(&Vcb->tree_lock);
1129
1130 BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
1131 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
1132 break;
1133 }
1134
1135 #ifndef __REACTOS__
1136 #ifdef _MSC_VER // not in mingw yet
1137 case FileFsSectorSizeInformation:
1138 {
1139 FILE_FS_SECTOR_SIZE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1140
1141 data->LogicalBytesPerSector = Vcb->superblock.sector_size;
1142 data->PhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1143 data->PhysicalBytesPerSectorForPerformance = Vcb->superblock.sector_size;
1144 data->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1145 data->ByteOffsetForSectorAlignment = 0;
1146 data->ByteOffsetForPartitionAlignment = 0;
1147
1148 data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
1149
1150 if (Vcb->trim && !Vcb->options.no_trim)
1151 data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
1152
1153 BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
1154 Status = STATUS_SUCCESS;
1155
1156 break;
1157 }
1158 #endif
1159 #endif /* __REACTOS__ */
1160
1161 default:
1162 Status = STATUS_INVALID_PARAMETER;
1163 WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
1164 break;
1165 }
1166
1167 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
1168 Irp->IoStatus.Information = 0;
1169 else
1170 Irp->IoStatus.Information = BytesCopied;
1171
1172 end:
1173 Irp->IoStatus.Status = Status;
1174
1175 IoCompleteRequest( Irp, IO_DISK_INCREMENT );
1176
1177 if (top_level)
1178 IoSetTopLevelIrp(NULL);
1179
1180 TRACE("query volume information returning %08lx\n", Status);
1181
1182 FsRtlExitFileSystem();
1183
1184 return Status;
1185 }
1186
1187 _Function_class_(IO_COMPLETION_ROUTINE)
1188 static NTSTATUS __stdcall read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
1189 read_context* context = conptr;
1190
1191 UNUSED(DeviceObject);
1192
1193 context->iosb = Irp->IoStatus;
1194 KeSetEvent(&context->Event, 0, false);
1195
1196 return STATUS_MORE_PROCESSING_REQUIRED;
1197 }
1198
1199 NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ uint64_t id,
1200 _Out_ root** rootptr, _In_ bool no_tree, _In_ uint64_t offset, _In_opt_ PIRP Irp) {
1201 NTSTATUS Status;
1202 root* r;
1203 tree* t = NULL;
1204 ROOT_ITEM* ri;
1205 traverse_ptr tp;
1206
1207 r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
1208 if (!r) {
1209 ERR("out of memory\n");
1210 return STATUS_INSUFFICIENT_RESOURCES;
1211 }
1212
1213 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
1214 if (!r->nonpaged) {
1215 ERR("out of memory\n");
1216 ExFreePool(r);
1217 return STATUS_INSUFFICIENT_RESOURCES;
1218 }
1219
1220 if (!no_tree) {
1221 t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
1222 if (!t) {
1223 ERR("out of memory\n");
1224 ExFreePool(r->nonpaged);
1225 ExFreePool(r);
1226 return STATUS_INSUFFICIENT_RESOURCES;
1227 }
1228
1229 t->nonpaged = NULL;
1230
1231 t->is_unique = true;
1232 t->uniqueness_determined = true;
1233 t->buf = NULL;
1234 }
1235
1236 ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
1237 if (!ri) {
1238 ERR("out of memory\n");
1239
1240 if (t)
1241 ExFreePool(t);
1242
1243 ExFreePool(r->nonpaged);
1244 ExFreePool(r);
1245 return STATUS_INSUFFICIENT_RESOURCES;
1246 }
1247
1248 r->id = id;
1249 r->treeholder.address = 0;
1250 r->treeholder.generation = Vcb->superblock.generation;
1251 r->treeholder.tree = t;
1252 r->lastinode = 0;
1253 r->dirty = false;
1254 r->received = false;
1255 r->reserved = NULL;
1256 r->parent = 0;
1257 r->send_ops = 0;
1258 RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
1259 r->root_item.num_references = 1;
1260 r->fcbs_version = 0;
1261 r->checked_for_orphans = true;
1262 r->dropped = false;
1263 InitializeListHead(&r->fcbs);
1264 RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
1265
1266 RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
1267
1268 // We ask here for a traverse_ptr to the item we're inserting, so we can
1269 // copy some of the tree's variables
1270
1271 Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
1272 if (!NT_SUCCESS(Status)) {
1273 ERR("insert_tree_item returned %08lx\n", Status);
1274 ExFreePool(ri);
1275
1276 if (t)
1277 ExFreePool(t);
1278
1279 ExFreePool(r->nonpaged);
1280 ExFreePool(r);
1281 return Status;
1282 }
1283
1284 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
1285
1286 InsertTailList(&Vcb->roots, &r->list_entry);
1287
1288 if (!no_tree) {
1289 RtlZeroMemory(&t->header, sizeof(tree_header));
1290 t->header.fs_uuid = tp.tree->header.fs_uuid;
1291 t->header.address = 0;
1292 t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
1293 t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
1294 t->header.generation = Vcb->superblock.generation;
1295 t->header.tree_id = id;
1296 t->header.num_items = 0;
1297 t->header.level = 0;
1298
1299 t->has_address = false;
1300 t->size = 0;
1301 t->Vcb = Vcb;
1302 t->parent = NULL;
1303 t->paritem = NULL;
1304 t->root = r;
1305
1306 InitializeListHead(&t->itemlist);
1307
1308 t->new_address = 0;
1309 t->has_new_address = false;
1310 t->updated_extents = false;
1311
1312 InsertTailList(&Vcb->trees, &t->list_entry);
1313 t->list_entry_hash.Flink = NULL;
1314
1315 t->write = true;
1316 Vcb->need_write = true;
1317 }
1318
1319 *rootptr = r;
1320
1321 return STATUS_SUCCESS;
1322 }
1323
1324 static NTSTATUS set_label(_In_ device_extension* Vcb, _In_ FILE_FS_LABEL_INFORMATION* ffli) {
1325 ULONG utf8len;
1326 NTSTATUS Status;
1327 ULONG vollen, i;
1328
1329 TRACE("label = %.*S\n", (int)(ffli->VolumeLabelLength / sizeof(WCHAR)), ffli->VolumeLabel);
1330
1331 vollen = ffli->VolumeLabelLength;
1332
1333 for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
1334 if (ffli->VolumeLabel[i] == 0) {
1335 vollen = i * sizeof(WCHAR);
1336 break;
1337 } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
1338 Status = STATUS_INVALID_VOLUME_LABEL;
1339 goto end;
1340 }
1341 }
1342
1343 if (vollen == 0) {
1344 utf8len = 0;
1345 } else {
1346 Status = utf16_to_utf8(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
1347 if (!NT_SUCCESS(Status))
1348 goto end;
1349
1350 if (utf8len > MAX_LABEL_SIZE) {
1351 Status = STATUS_INVALID_VOLUME_LABEL;
1352 goto end;
1353 }
1354 }
1355
1356 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1357
1358 if (utf8len > 0) {
1359 Status = utf16_to_utf8((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
1360 if (!NT_SUCCESS(Status))
1361 goto release;
1362 } else
1363 Status = STATUS_SUCCESS;
1364
1365 if (utf8len < MAX_LABEL_SIZE)
1366 RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
1367
1368 Vcb->need_write = true;
1369
1370 release:
1371 ExReleaseResourceLite(&Vcb->tree_lock);
1372
1373 end:
1374 TRACE("returning %08lx\n", Status);
1375
1376 return Status;
1377 }
1378
1379 _Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION)
1380 _Function_class_(DRIVER_DISPATCH)
1381 static NTSTATUS __stdcall drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
1382 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1383 device_extension* Vcb = DeviceObject->DeviceExtension;
1384 NTSTATUS Status;
1385 bool top_level;
1386
1387 FsRtlEnterFileSystem();
1388
1389 TRACE("set volume information\n");
1390
1391 top_level = is_top_level(Irp);
1392
1393 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1394 Status = vol_set_volume_information(DeviceObject, Irp);
1395 goto end;
1396 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1397 Status = STATUS_INVALID_PARAMETER;
1398 goto end;
1399 }
1400
1401 Status = STATUS_NOT_IMPLEMENTED;
1402
1403 if (Vcb->readonly) {
1404 Status = STATUS_MEDIA_WRITE_PROTECTED;
1405 goto end;
1406 }
1407
1408 if (Vcb->removing || Vcb->locked) {
1409 Status = STATUS_ACCESS_DENIED;
1410 goto end;
1411 }
1412
1413 switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
1414 case FileFsControlInformation:
1415 FIXME("STUB: FileFsControlInformation\n");
1416 break;
1417
1418 case FileFsLabelInformation:
1419 TRACE("FileFsLabelInformation\n");
1420
1421 Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
1422 break;
1423
1424 case FileFsObjectIdInformation:
1425 FIXME("STUB: FileFsObjectIdInformation\n");
1426 break;
1427
1428 default:
1429 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
1430 break;
1431 }
1432
1433 end:
1434 Irp->IoStatus.Status = Status;
1435 Irp->IoStatus.Information = 0;
1436
1437 TRACE("returning %08lx\n", Status);
1438
1439 IoCompleteRequest( Irp, IO_NO_INCREMENT );
1440
1441 if (top_level)
1442 IoSetTopLevelIrp(NULL);
1443
1444 FsRtlExitFileSystem();
1445
1446 return Status;
1447 }
1448
1449 void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
1450 UNICODE_STRING fn;
1451 NTSTATUS Status;
1452 ULONG reqlen;
1453 USHORT name_offset;
1454 fcb* fcb = fileref->fcb;
1455
1456 fn.Length = fn.MaximumLength = 0;
1457 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1458 if (Status != STATUS_BUFFER_OVERFLOW) {
1459 ERR("fileref_get_filename returned %08lx\n", Status);
1460 return;
1461 }
1462
1463 if (reqlen > 0xffff) {
1464 WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1465 return;
1466 }
1467
1468 fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1469 if (!fn.Buffer) {
1470 ERR("out of memory\n");
1471 return;
1472 }
1473
1474 fn.MaximumLength = (USHORT)reqlen;
1475 fn.Length = 0;
1476
1477 Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
1478 if (!NT_SUCCESS(Status)) {
1479 ERR("fileref_get_filename returned %08lx\n", Status);
1480 ExFreePool(fn.Buffer);
1481 return;
1482 }
1483
1484 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
1485 (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1486 ExFreePool(fn.Buffer);
1487 }
1488
1489 static void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
1490 fcb* fcb = fileref->fcb;
1491 LIST_ENTRY* le;
1492 NTSTATUS Status;
1493
1494 // no point looking for hardlinks if st_nlink == 1
1495 if (fileref->fcb->inode_item.st_nlink == 1) {
1496 send_notification_fileref(fileref, filter_match, action, stream);
1497 return;
1498 }
1499
1500 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
1501
1502 le = fcb->hardlinks.Flink;
1503 while (le != &fcb->hardlinks) {
1504 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1505 file_ref* parfr;
1506
1507 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
1508
1509 if (!NT_SUCCESS(Status))
1510 ERR("open_fileref_by_inode returned %08lx\n", Status);
1511 else if (!parfr->deleted) {
1512 UNICODE_STRING fn;
1513 ULONG pathlen;
1514
1515 fn.Length = fn.MaximumLength = 0;
1516 Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
1517 if (Status != STATUS_BUFFER_OVERFLOW) {
1518 ERR("fileref_get_filename returned %08lx\n", Status);
1519 free_fileref(parfr);
1520 break;
1521 }
1522
1523 if (parfr != fcb->Vcb->root_fileref)
1524 pathlen += sizeof(WCHAR);
1525
1526 if (pathlen + hl->name.Length > 0xffff) {
1527 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1528 free_fileref(parfr);
1529 break;
1530 }
1531
1532 fn.MaximumLength = (USHORT)(pathlen + hl->name.Length);
1533 fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
1534 if (!fn.Buffer) {
1535 ERR("out of memory\n");
1536 free_fileref(parfr);
1537 break;
1538 }
1539
1540 Status = fileref_get_filename(parfr, &fn, NULL, NULL);
1541 if (!NT_SUCCESS(Status)) {
1542 ERR("fileref_get_filename returned %08lx\n", Status);
1543 free_fileref(parfr);
1544 ExFreePool(fn.Buffer);
1545 break;
1546 }
1547
1548 if (parfr != fcb->Vcb->root_fileref) {
1549 fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
1550 fn.Length += sizeof(WCHAR);
1551 }
1552
1553 RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
1554 fn.Length += hl->name.Length;
1555
1556 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, (USHORT)pathlen,
1557 (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1558
1559 ExFreePool(fn.Buffer);
1560
1561 free_fileref(parfr);
1562 }
1563
1564 le = le->Flink;
1565 }
1566
1567 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
1568 }
1569
1570 typedef struct {
1571 file_ref* fileref;
1572 ULONG filter_match;
1573 ULONG action;
1574 PUNICODE_STRING stream;
1575 PIO_WORKITEM work_item;
1576 } notification_fcb;
1577
1578 _Function_class_(IO_WORKITEM_ROUTINE)
1579 static void __stdcall notification_work_item(PDEVICE_OBJECT DeviceObject, PVOID con) {
1580 notification_fcb* nf = con;
1581
1582 UNUSED(DeviceObject);
1583
1584 ExAcquireResourceSharedLite(&nf->fileref->fcb->Vcb->tree_lock, TRUE); // protect us from fileref being reaped
1585
1586 send_notification_fcb(nf->fileref, nf->filter_match, nf->action, nf->stream);
1587
1588 free_fileref(nf->fileref);
1589
1590 ExReleaseResourceLite(&nf->fileref->fcb->Vcb->tree_lock);
1591
1592 IoFreeWorkItem(nf->work_item);
1593
1594 ExFreePool(nf);
1595 }
1596
1597 void queue_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
1598 notification_fcb* nf;
1599 PIO_WORKITEM work_item;
1600
1601 nf = ExAllocatePoolWithTag(PagedPool, sizeof(notification_fcb), ALLOC_TAG);
1602 if (!nf) {
1603 ERR("out of memory\n");
1604 return;
1605 }
1606
1607 work_item = IoAllocateWorkItem(master_devobj);
1608 if (!work_item) {
1609 ERR("out of memory\n");
1610 ExFreePool(nf);
1611 return;
1612 }
1613
1614 InterlockedIncrement(&fileref->refcount);
1615
1616 nf->fileref = fileref;
1617 nf->filter_match = filter_match;
1618 nf->action = action;
1619 nf->stream = stream;
1620 nf->work_item = work_item;
1621
1622 IoQueueWorkItem(work_item, notification_work_item, DelayedWorkQueue, nf);
1623 }
1624
1625 void mark_fcb_dirty(_In_ fcb* fcb) {
1626 if (!fcb->dirty) {
1627 #ifdef DEBUG_FCB_REFCOUNTS
1628 LONG rc;
1629 #endif
1630 fcb->dirty = true;
1631
1632 #ifdef DEBUG_FCB_REFCOUNTS
1633 rc = InterlockedIncrement(&fcb->refcount);
1634 WARN("fcb %p: refcount now %i\n", fcb, rc);
1635 #else
1636 InterlockedIncrement(&fcb->refcount);
1637 #endif
1638
1639 ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, true);
1640 InsertTailList(&fcb->Vcb->dirty_fcbs, &fcb->list_entry_dirty);
1641 ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
1642 }
1643
1644 fcb->Vcb->need_write = true;
1645 }
1646
1647 void mark_fileref_dirty(_In_ file_ref* fileref) {
1648 if (!fileref->dirty) {
1649 fileref->dirty = true;
1650 increase_fileref_refcount(fileref);
1651
1652 ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, true);
1653 InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &fileref->list_entry_dirty);
1654 ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
1655 }
1656
1657 fileref->fcb->Vcb->need_write = true;
1658 }
1659
1660 #ifdef DEBUG_FCB_REFCOUNTS
1661 void _free_fcb(_Inout_ fcb* fcb, _In_ const char* func) {
1662 LONG rc = InterlockedDecrement(&fcb->refcount);
1663 #else
1664 void free_fcb(_Inout_ fcb* fcb) {
1665 InterlockedDecrement(&fcb->refcount);
1666 #endif
1667
1668 #ifdef DEBUG_FCB_REFCOUNTS
1669 ERR("fcb %p (%s): refcount now %i (subvol %I64x, inode %I64x)\n", fcb, func, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1670 #endif
1671 }
1672
1673 void reap_fcb(fcb* fcb) {
1674 uint8_t c = fcb->hash >> 24;
1675
1676 if (fcb->subvol && fcb->subvol->fcbs_ptrs[c] == &fcb->list_entry) {
1677 if (fcb->list_entry.Flink != &fcb->subvol->fcbs && (CONTAINING_RECORD(fcb->list_entry.Flink, struct _fcb, list_entry)->hash >> 24) == c)
1678 fcb->subvol->fcbs_ptrs[c] = fcb->list_entry.Flink;
1679 else
1680 fcb->subvol->fcbs_ptrs[c] = NULL;
1681 }
1682
1683 if (fcb->list_entry.Flink) {
1684 RemoveEntryList(&fcb->list_entry);
1685
1686 if (fcb->subvol && fcb->subvol->dropped && IsListEmpty(&fcb->subvol->fcbs)) {
1687 ExDeleteResourceLite(&fcb->subvol->nonpaged->load_tree_lock);
1688 ExFreePool(fcb->subvol->nonpaged);
1689 ExFreePool(fcb->subvol);
1690 }
1691 }
1692
1693 if (fcb->list_entry_all.Flink)
1694 RemoveEntryList(&fcb->list_entry_all);
1695
1696 ExDeleteResourceLite(&fcb->nonpaged->resource);
1697 ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
1698 ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
1699
1700 ExFreeToNPagedLookasideList(&fcb->Vcb->fcb_np_lookaside, fcb->nonpaged);
1701
1702 if (fcb->sd)
1703 ExFreePool(fcb->sd);
1704
1705 if (fcb->adsxattr.Buffer)
1706 ExFreePool(fcb->adsxattr.Buffer);
1707
1708 if (fcb->reparse_xattr.Buffer)
1709 ExFreePool(fcb->reparse_xattr.Buffer);
1710
1711 if (fcb->ea_xattr.Buffer)
1712 ExFreePool(fcb->ea_xattr.Buffer);
1713
1714 if (fcb->adsdata.Buffer)
1715 ExFreePool(fcb->adsdata.Buffer);
1716
1717 while (!IsListEmpty(&fcb->extents)) {
1718 LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
1719 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1720
1721 if (ext->csum)
1722 ExFreePool(ext->csum);
1723
1724 ExFreePool(ext);
1725 }
1726
1727 while (!IsListEmpty(&fcb->hardlinks)) {
1728 LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
1729 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1730
1731 if (hl->name.Buffer)
1732 ExFreePool(hl->name.Buffer);
1733
1734 if (hl->utf8.Buffer)
1735 ExFreePool(hl->utf8.Buffer);
1736
1737 ExFreePool(hl);
1738 }
1739
1740 while (!IsListEmpty(&fcb->xattrs)) {
1741 xattr* xa = CONTAINING_RECORD(RemoveHeadList(&fcb->xattrs), xattr, list_entry);
1742
1743 ExFreePool(xa);
1744 }
1745
1746 while (!IsListEmpty(&fcb->dir_children_index)) {
1747 LIST_ENTRY* le = RemoveHeadList(&fcb->dir_children_index);
1748 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
1749
1750 ExFreePool(dc->utf8.Buffer);
1751 ExFreePool(dc->name.Buffer);
1752 ExFreePool(dc->name_uc.Buffer);
1753 ExFreePool(dc);
1754 }
1755
1756 if (fcb->hash_ptrs)
1757 ExFreePool(fcb->hash_ptrs);
1758
1759 if (fcb->hash_ptrs_uc)
1760 ExFreePool(fcb->hash_ptrs_uc);
1761
1762 FsRtlUninitializeFileLock(&fcb->lock);
1763 FsRtlUninitializeOplock(fcb_oplock(fcb));
1764
1765 if (fcb->pool_type == NonPagedPool)
1766 ExFreePool(fcb);
1767 else
1768 ExFreeToPagedLookasideList(&fcb->Vcb->fcb_lookaside, fcb);
1769 }
1770
1771 void reap_fcbs(device_extension* Vcb) {
1772 LIST_ENTRY* le;
1773
1774 le = Vcb->all_fcbs.Flink;
1775 while (le != &Vcb->all_fcbs) {
1776 fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
1777 LIST_ENTRY* le2 = le->Flink;
1778
1779 if (fcb->refcount == 0)
1780 reap_fcb(fcb);
1781
1782 le = le2;
1783 }
1784 }
1785
1786 void free_fileref(_Inout_ file_ref* fr) {
1787 LONG rc;
1788
1789 rc = InterlockedDecrement(&fr->refcount);
1790 #ifdef __REACTOS__
1791 (void)rc;
1792 #endif
1793
1794 #ifdef DEBUG_FCB_REFCOUNTS
1795 ERR("fileref %p: refcount now %i\n", fr, rc);
1796 #endif
1797
1798 #ifdef _DEBUG
1799 if (rc < 0) {
1800 ERR("fileref %p: refcount now %li\n", fr, rc);
1801 int3;
1802 }
1803 #endif
1804 }
1805
1806 void reap_fileref(device_extension* Vcb, file_ref* fr) {
1807 // FIXME - do we need a file_ref lock?
1808
1809 // FIXME - do delete if needed
1810
1811 ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
1812
1813 ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
1814
1815 // FIXME - throw error if children not empty
1816
1817 if (fr->fcb->fileref == fr)
1818 fr->fcb->fileref = NULL;
1819
1820 if (fr->dc) {
1821 if (fr->fcb->ads)
1822 fr->dc->size = fr->fcb->adsdata.Length;
1823
1824 fr->dc->fileref = NULL;
1825 }
1826
1827 if (fr->list_entry.Flink)
1828 RemoveEntryList(&fr->list_entry);
1829
1830 if (fr->parent)
1831 free_fileref(fr->parent);
1832
1833 free_fcb(fr->fcb);
1834
1835 if (fr->oldutf8.Buffer)
1836 ExFreePool(fr->oldutf8.Buffer);
1837
1838 ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
1839 }
1840
1841 void reap_filerefs(device_extension* Vcb, file_ref* fr) {
1842 LIST_ENTRY* le;
1843
1844 // FIXME - recursion is a bad idea in kernel mode
1845
1846 le = fr->children.Flink;
1847 while (le != &fr->children) {
1848 file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry);
1849 LIST_ENTRY* le2 = le->Flink;
1850
1851 reap_filerefs(Vcb, c);
1852
1853 le = le2;
1854 }
1855
1856 if (fr->refcount == 0)
1857 reap_fileref(Vcb, fr);
1858 }
1859
1860 static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp) {
1861 fcb* fcb;
1862 ccb* ccb;
1863 file_ref* fileref = NULL;
1864 LONG open_files;
1865
1866 UNUSED(Irp);
1867
1868 TRACE("FileObject = %p\n", FileObject);
1869
1870 fcb = FileObject->FsContext;
1871 if (!fcb) {
1872 TRACE("FCB was NULL, returning success\n");
1873 return STATUS_SUCCESS;
1874 }
1875
1876 open_files = InterlockedDecrement(&fcb->Vcb->open_files);
1877
1878 ccb = FileObject->FsContext2;
1879
1880 TRACE("close called for fcb %p)\n", fcb);
1881
1882 // FIXME - make sure notification gets sent if file is being deleted
1883
1884 if (ccb) {
1885 if (ccb->query_string.Buffer)
1886 RtlFreeUnicodeString(&ccb->query_string);
1887
1888 if (ccb->filename.Buffer)
1889 ExFreePool(ccb->filename.Buffer);
1890
1891 // FIXME - use refcounts for fileref
1892 fileref = ccb->fileref;
1893
1894 if (fcb->Vcb->running_sends > 0) {
1895 bool send_cancelled = false;
1896
1897 ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1898
1899 if (ccb->send) {
1900 ccb->send->cancelling = true;
1901 send_cancelled = true;
1902 KeSetEvent(&ccb->send->cleared_event, 0, false);
1903 }
1904
1905 ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1906
1907 if (send_cancelled) {
1908 while (ccb->send) {
1909 ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1910 ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1911 }
1912 }
1913 }
1914
1915 ExFreePool(ccb);
1916 }
1917
1918 CcUninitializeCacheMap(FileObject, NULL, NULL);
1919
1920 if (open_files == 0 && fcb->Vcb->removing) {
1921 uninit(fcb->Vcb);
1922 return STATUS_SUCCESS;
1923 }
1924
1925 if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
1926 return STATUS_SUCCESS;
1927
1928 if (fileref)
1929 free_fileref(fileref);
1930 else
1931 free_fcb(fcb);
1932
1933 return STATUS_SUCCESS;
1934 }
1935
1936 void uninit(_In_ device_extension* Vcb) {
1937 uint64_t i;
1938 KIRQL irql;
1939 NTSTATUS Status;
1940 LIST_ENTRY* le;
1941 LARGE_INTEGER time;
1942
1943 if (!Vcb->removing) {
1944 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1945 Vcb->removing = true;
1946 ExReleaseResourceLite(&Vcb->tree_lock);
1947 }
1948
1949 if (Vcb->vde && Vcb->vde->mounted_device == Vcb->devobj)
1950 Vcb->vde->mounted_device = NULL;
1951
1952 IoAcquireVpbSpinLock(&irql);
1953 Vcb->Vpb->Flags &= ~VPB_MOUNTED;
1954 Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
1955 Vcb->Vpb->DeviceObject = NULL;
1956 IoReleaseVpbSpinLock(irql);
1957
1958 // FIXME - needs global_loading_lock to be held
1959 if (Vcb->list_entry.Flink)
1960 RemoveEntryList(&Vcb->list_entry);
1961
1962 if (Vcb->balance.thread) {
1963 Vcb->balance.paused = false;
1964 Vcb->balance.stopping = true;
1965 KeSetEvent(&Vcb->balance.event, 0, false);
1966 KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, false, NULL);
1967 }
1968
1969 if (Vcb->scrub.thread) {
1970 Vcb->scrub.paused = false;
1971 Vcb->scrub.stopping = true;
1972 KeSetEvent(&Vcb->scrub.event, 0, false);
1973 KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, false, NULL);
1974 }
1975
1976 if (Vcb->running_sends != 0) {
1977 bool send_cancelled = false;
1978
1979 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
1980
1981 le = Vcb->send_ops.Flink;
1982 while (le != &Vcb->send_ops) {
1983 send_info* send = CONTAINING_RECORD(le, send_info, list_entry);
1984
1985 if (!send->cancelling) {
1986 send->cancelling = true;
1987 send_cancelled = true;
1988 send->ccb = NULL;
1989 KeSetEvent(&send->cleared_event, 0, false);
1990 }
1991
1992 le = le->Flink;
1993 }
1994
1995 ExReleaseResourceLite(&Vcb->send_load_lock);
1996
1997 if (send_cancelled) {
1998 while (Vcb->running_sends != 0) {
1999 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
2000 ExReleaseResourceLite(&Vcb->send_load_lock);
2001 }
2002 }
2003 }
2004
2005 Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
2006 if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
2007 WARN("registry_mark_volume_unmounted returned %08lx\n", Status);
2008
2009 for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2010 Vcb->calcthreads.threads[i].quit = true;
2011 }
2012
2013 KeSetEvent(&Vcb->calcthreads.event, 0, false);
2014
2015 for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2016 KeWaitForSingleObject(&Vcb->calcthreads.threads[i].finished, Executive, KernelMode, false, NULL);
2017
2018 ZwClose(Vcb->calcthreads.threads[i].handle);
2019 }
2020
2021 ExFreePool(Vcb->calcthreads.threads);
2022
2023 time.QuadPart = 0;
2024 KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
2025 KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, false, NULL);
2026
2027 reap_fcb(Vcb->volume_fcb);
2028 reap_fcb(Vcb->dummy_fcb);
2029
2030 if (Vcb->root_file)
2031 ObDereferenceObject(Vcb->root_file);
2032
2033 le = Vcb->chunks.Flink;
2034 while (le != &Vcb->chunks) {
2035 chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
2036
2037 if (c->cache) {
2038 reap_fcb(c->cache);
2039 c->cache = NULL;
2040 }
2041
2042 le = le->Flink;
2043 }
2044
2045 while (!IsListEmpty(&Vcb->all_fcbs)) {
2046 fcb* fcb = CONTAINING_RECORD(Vcb->all_fcbs.Flink, struct _fcb, list_entry_all);
2047
2048 reap_fcb(fcb);
2049 }
2050
2051 while (!IsListEmpty(&Vcb->sys_chunks)) {
2052 sys_chunk* sc = CONTAINING_RECORD(RemoveHeadList(&Vcb->sys_chunks), sys_chunk, list_entry);
2053
2054 if (sc->data)
2055 ExFreePool(sc->data);
2056
2057 ExFreePool(sc);
2058 }
2059
2060 while (!IsListEmpty(&Vcb->roots)) {
2061 root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->roots), root, list_entry);
2062
2063 ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
2064 ExFreePool(r->nonpaged);
2065 ExFreePool(r);
2066 }
2067
2068 while (!IsListEmpty(&Vcb->chunks)) {
2069 chunk* c = CONTAINING_RECORD(RemoveHeadList(&Vcb->chunks), chunk, list_entry);
2070
2071 while (!IsListEmpty(&c->space)) {
2072 LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2073 space* s = CONTAINING_RECORD(le2, space, list_entry);
2074
2075 ExFreePool(s);
2076 }
2077
2078 while (!IsListEmpty(&c->deleting)) {
2079 LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
2080 space* s = CONTAINING_RECORD(le2, space, list_entry);
2081
2082 ExFreePool(s);
2083 }
2084
2085 if (c->devices)
2086 ExFreePool(c->devices);
2087
2088 if (c->cache)
2089 reap_fcb(c->cache);
2090
2091 ExDeleteResourceLite(&c->range_locks_lock);
2092 ExDeleteResourceLite(&c->partial_stripes_lock);
2093 ExDeleteResourceLite(&c->lock);
2094 ExDeleteResourceLite(&c->changed_extents_lock);
2095
2096 ExFreePool(c->chunk_item);
2097 ExFreePool(c);
2098 }
2099
2100 while (!IsListEmpty(&Vcb->devices)) {
2101 device* dev = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
2102
2103 while (!IsListEmpty(&dev->space)) {
2104 LIST_ENTRY* le2 = RemoveHeadList(&dev->space);
2105 space* s = CONTAINING_RECORD(le2, space, list_entry);
2106
2107 ExFreePool(s);
2108 }
2109
2110 ExFreePool(dev);
2111 }
2112
2113 ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, true);
2114 while (!IsListEmpty(&Vcb->scrub.errors)) {
2115 scrub_error* err = CONTAINING_RECORD(RemoveHeadList(&Vcb->scrub.errors), scrub_error, list_entry);
2116
2117 ExFreePool(err);
2118 }
2119 ExReleaseResourceLite(&Vcb->scrub.stats_lock);
2120
2121 ExDeleteResourceLite(&Vcb->fcb_lock);
2122 ExDeleteResourceLite(&Vcb->fileref_lock);
2123 ExDeleteResourceLite(&Vcb->load_lock);
2124 ExDeleteResourceLite(&Vcb->tree_lock);
2125 ExDeleteResourceLite(&Vcb->chunk_lock);
2126 ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
2127 ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
2128 ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
2129 ExDeleteResourceLite(&Vcb->scrub.stats_lock);
2130 ExDeleteResourceLite(&Vcb->send_load_lock);
2131
2132 ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
2133 ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
2134 ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
2135 ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
2136 ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
2137 ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
2138 ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
2139 ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
2140 ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
2141
2142 ZwClose(Vcb->flush_thread_handle);
2143
2144 if (Vcb->devobj->AttachedDevice)
2145 IoDetachDevice(Vcb->devobj);
2146
2147 IoDeleteDevice(Vcb->devobj);
2148 }
2149
2150 static NTSTATUS delete_fileref_fcb(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
2151 NTSTATUS Status;
2152 LIST_ENTRY* le;
2153
2154 // excise extents
2155
2156 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
2157 Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
2158 if (!NT_SUCCESS(Status)) {
2159 ERR("excise_extents returned %08lx\n", Status);
2160 return Status;
2161 }
2162 }
2163
2164 fileref->fcb->Header.AllocationSize.QuadPart = 0;
2165 fileref->fcb->Header.FileSize.QuadPart = 0;
2166 fileref->fcb->Header.ValidDataLength.QuadPart = 0;
2167
2168 if (FileObject) {
2169 CC_FILE_SIZES ccfs;
2170
2171 ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
2172 ccfs.FileSize = fileref->fcb->Header.FileSize;
2173 ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
2174
2175 Status = STATUS_SUCCESS;
2176
2177 _SEH2_TRY {
2178 CcSetFileSizes(FileObject, &ccfs);
2179 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2180 Status = _SEH2_GetExceptionCode();
2181 } _SEH2_END;
2182
2183 if (!NT_SUCCESS(Status)) {
2184 ERR("CcSetFileSizes threw exception %08lx\n", Status);
2185 return Status;
2186 }
2187 }
2188
2189 fileref->fcb->deleted = true;
2190
2191 le = fileref->children.Flink;
2192 while (le != &fileref->children) {
2193 file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
2194
2195 if (fr2->fcb->ads) {
2196 fr2->fcb->deleted = true;
2197 mark_fcb_dirty(fr2->fcb);
2198 }
2199
2200 le = le->Flink;
2201 }
2202
2203 return STATUS_SUCCESS;
2204 }
2205
2206 NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_ bool make_orphan, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
2207 LARGE_INTEGER newlength, time;
2208 BTRFS_TIME now;
2209 NTSTATUS Status;
2210 ULONG utf8len = 0;
2211
2212 KeQuerySystemTime(&time);
2213 win_time_to_unix(time, &now);
2214
2215 ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, true);
2216
2217 if (fileref->deleted) {
2218 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2219 return STATUS_SUCCESS;
2220 }
2221
2222 if (fileref->fcb->subvol->send_ops > 0) {
2223 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2224 return STATUS_ACCESS_DENIED;
2225 }
2226
2227 fileref->deleted = true;
2228 mark_fileref_dirty(fileref);
2229
2230 // delete INODE_ITEM (0x1)
2231
2232 TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
2233
2234 if (!fileref->fcb->ads) {
2235 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
2236 LIST_ENTRY* le;
2237
2238 mark_fcb_dirty(fileref->fcb);
2239
2240 fileref->fcb->inode_item_changed = true;
2241
2242 if (fileref->fcb->inode_item.st_nlink > 1 || make_orphan) {
2243 fileref->fcb->inode_item.st_nlink--;
2244 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2245 fileref->fcb->inode_item.sequence++;
2246 fileref->fcb->inode_item.st_ctime = now;
2247 } else {
2248 Status = delete_fileref_fcb(fileref, FileObject, Irp, rollback);
2249 if (!NT_SUCCESS(Status)) {
2250 ERR("delete_fileref_fcb returned %08lx\n", Status);
2251 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2252 return Status;
2253 }
2254 }
2255
2256 if (fileref->dc) {
2257 le = fileref->fcb->hardlinks.Flink;
2258 while (le != &fileref->fcb->hardlinks) {
2259 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
2260
2261 if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
2262 RemoveEntryList(&hl->list_entry);
2263
2264 if (hl->name.Buffer)
2265 ExFreePool(hl->name.Buffer);
2266
2267 if (hl->utf8.Buffer)
2268 ExFreePool(hl->utf8.Buffer);
2269
2270 ExFreePool(hl);
2271 break;
2272 }
2273
2274 le = le->Flink;
2275 }
2276 }
2277 } else if (fileref->fcb->subvol->parent == fileref->parent->fcb->subvol->id) { // valid subvolume
2278 if (fileref->fcb->subvol->root_item.num_references > 1) {
2279 fileref->fcb->subvol->root_item.num_references--;
2280
2281 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2282 } else {
2283 LIST_ENTRY* le;
2284
2285 // FIXME - we need a lock here
2286
2287 RemoveEntryList(&fileref->fcb->subvol->list_entry);
2288
2289 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2290
2291 le = fileref->children.Flink;
2292 while (le != &fileref->children) {
2293 file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
2294
2295 if (fr2->fcb->ads) {
2296 fr2->fcb->deleted = true;
2297 mark_fcb_dirty(fr2->fcb);
2298 }
2299
2300 le = le->Flink;
2301 }
2302 }
2303 }
2304 } else {
2305 fileref->fcb->deleted = true;
2306 mark_fcb_dirty(fileref->fcb);
2307 }
2308
2309 // remove dir_child from parent
2310
2311 if (fileref->dc) {
2312 TRACE("delete file %.*S\n", (int)(fileref->dc->name.Length / sizeof(WCHAR)), fileref->dc->name.Buffer);
2313
2314 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2315 RemoveEntryList(&fileref->dc->list_entry_index);
2316
2317 if (!fileref->fcb->ads)
2318 remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
2319
2320 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2321
2322 if (!fileref->oldutf8.Buffer)
2323 fileref->oldutf8 = fileref->dc->utf8;
2324 else
2325 ExFreePool(fileref->dc->utf8.Buffer);
2326
2327 utf8len = fileref->dc->utf8.Length;
2328
2329 fileref->oldindex = fileref->dc->index;
2330
2331 ExFreePool(fileref->dc->name.Buffer);
2332 ExFreePool(fileref->dc->name_uc.Buffer);
2333 ExFreePool(fileref->dc);
2334
2335 fileref->dc = NULL;
2336 }
2337
2338 // update INODE_ITEM of parent
2339
2340 ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, true);
2341
2342 fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2343 fileref->parent->fcb->inode_item.sequence++;
2344 fileref->parent->fcb->inode_item.st_ctime = now;
2345
2346 if (!fileref->fcb->ads) {
2347 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2348 fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
2349 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2350 fileref->parent->fcb->inode_item.st_mtime = now;
2351 }
2352
2353 fileref->parent->fcb->inode_item_changed = true;
2354 ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2355
2356 if (!fileref->fcb->ads && fileref->parent->dc)
2357 send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
2358
2359 mark_fcb_dirty(fileref->parent->fcb);
2360
2361 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2362 fileref->fcb->subvol->root_item.ctime = now;
2363
2364 newlength.QuadPart = 0;
2365
2366 if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2367 TRACE("CcUninitializeCacheMap failed\n");
2368
2369 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2370
2371 return STATUS_SUCCESS;
2372 }
2373
2374 _Dispatch_type_(IRP_MJ_CLEANUP)
2375 _Function_class_(DRIVER_DISPATCH)
2376 static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
2377 NTSTATUS Status;
2378 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2379 PFILE_OBJECT FileObject = IrpSp->FileObject;
2380 device_extension* Vcb = DeviceObject->DeviceExtension;
2381 fcb* fcb = FileObject->FsContext;
2382 bool top_level;
2383
2384 FsRtlEnterFileSystem();
2385
2386 TRACE("cleanup\n");
2387
2388 top_level = is_top_level(Irp);
2389
2390 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2391 Status = vol_cleanup(DeviceObject, Irp);
2392 goto exit;
2393 } else if (DeviceObject == master_devobj) {
2394 TRACE("closing file system\n");
2395 Status = STATUS_SUCCESS;
2396 goto exit;
2397 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2398 Status = STATUS_INVALID_PARAMETER;
2399 goto exit;
2400 }
2401
2402 if (FileObject->Flags & FO_CLEANUP_COMPLETE) {
2403 TRACE("FileObject %p already cleaned up\n", FileObject);
2404 Status = STATUS_SUCCESS;
2405 goto exit;
2406 }
2407
2408 if (!fcb) {
2409 ERR("fcb was NULL\n");
2410 Status = STATUS_INVALID_PARAMETER;
2411 goto exit;
2412 }
2413
2414 FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
2415
2416 // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2417 // messages belonging to other devices.
2418
2419 if (FileObject && FileObject->FsContext) {
2420 LONG oc;
2421 ccb* ccb;
2422 file_ref* fileref;
2423 bool locked = true;
2424
2425 ccb = FileObject->FsContext2;
2426 fileref = ccb ? ccb->fileref : NULL;
2427
2428 TRACE("cleanup called for FileObject %p\n", FileObject);
2429 TRACE("fileref %p, refcount = %li, open_count = %li\n", fileref, fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
2430
2431 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
2432
2433 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
2434
2435 IoRemoveShareAccess(FileObject, &fcb->share_access);
2436
2437 FsRtlFastUnlockAll(&fcb->lock, FileObject, IoGetRequestorProcess(Irp), NULL);
2438
2439 if (ccb)
2440 FsRtlNotifyCleanup(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, ccb);
2441
2442 if (fileref) {
2443 oc = InterlockedDecrement(&fileref->open_count);
2444 #ifdef DEBUG_FCB_REFCOUNTS
2445 ERR("fileref %p: open_count now %i\n", fileref, oc);
2446 #endif
2447 }
2448
2449 if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2450 fileref->delete_on_close = true;
2451
2452 if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && fcb != fcb->Vcb->dummy_fcb)
2453 fileref->delete_on_close = false;
2454
2455 if (fcb->Vcb->locked && fcb->Vcb->locked_fileobj == FileObject) {
2456 TRACE("unlocking volume\n");
2457 do_unlock_volume(fcb->Vcb);
2458 FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
2459 }
2460
2461 if (ccb && ccb->reserving) {
2462 fcb->subvol->reserved = NULL;
2463 ccb->reserving = false;
2464 // FIXME - flush all of subvol's fcbs
2465 }
2466
2467 if (fileref && (oc == 0 || (fileref->delete_on_close && fileref->posix_delete))) {
2468 if (!fcb->Vcb->removing) {
2469 if (oc == 0 && fileref->fcb->inode_item.st_nlink == 0 && fileref != fcb->Vcb->root_fileref &&
2470 fcb != fcb->Vcb->volume_fcb && !fcb->ads) { // last handle closed on POSIX-deleted file
2471 LIST_ENTRY rollback;
2472
2473 InitializeListHead(&rollback);
2474
2475 Status = delete_fileref_fcb(fileref, FileObject, Irp, &rollback);
2476 if (!NT_SUCCESS(Status)) {
2477 ERR("delete_fileref_fcb returned %08lx\n", Status);
2478 do_rollback(fcb->Vcb, &rollback);
2479 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2480 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2481 goto exit;
2482 }
2483
2484 clear_rollback(&rollback);
2485
2486 mark_fcb_dirty(fileref->fcb);
2487 } else if (fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2488 LIST_ENTRY rollback;
2489
2490 InitializeListHead(&rollback);
2491
2492 if (!fileref->fcb->ads || fileref->dc) {
2493 if (fileref->fcb->ads) {
2494 send_notification_fileref(fileref->parent, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
2495 FILE_ACTION_REMOVED, &fileref->dc->name);
2496 } else
2497 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
2498 }
2499
2500 ExReleaseResourceLite(fcb->Header.Resource);
2501 locked = false;
2502
2503 // fileref_lock needs to be acquired before fcb->Header.Resource
2504 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
2505
2506 Status = delete_fileref(fileref, FileObject, oc > 0 && fileref->posix_delete, Irp, &rollback);
2507 if (!NT_SUCCESS(Status)) {
2508 ERR("delete_fileref returned %08lx\n", Status);
2509 do_rollback(fcb->Vcb, &rollback);
2510 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2511 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2512 goto exit;
2513 }
2514
2515 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2516
2517 clear_rollback(&rollback);
2518 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && FileObject->SectionObjectPointer->DataSectionObject) {
2519 IO_STATUS_BLOCK iosb;
2520
2521 if (locked) {
2522 ExReleaseResourceLite(fcb->Header.Resource);
2523 locked = false;
2524 }
2525
2526 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2527
2528 if (!NT_SUCCESS(iosb.Status))
2529 ERR("CcFlushCache returned %08lx\n", iosb.Status);
2530
2531 if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2532 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
2533 ExReleaseResourceLite(fcb->Header.PagingIoResource);
2534 }
2535
2536 CcPurgeCacheSection(FileObject->SectionObjectPointer, NULL, 0, false);
2537
2538 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x)\n",
2539 FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2540 }
2541 }
2542
2543 if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2544 CcUninitializeCacheMap(FileObject, NULL, NULL);
2545 }
2546
2547 if (locked)
2548 ExReleaseResourceLite(fcb->Header.Resource);
2549
2550 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2551
2552 FileObject->Flags |= FO_CLEANUP_COMPLETE;
2553 }
2554
2555 Status = STATUS_SUCCESS;
2556
2557 exit:
2558 TRACE("returning %08lx\n", Status);
2559
2560 Irp->IoStatus.Status = Status;
2561 Irp->IoStatus.Information = 0;
2562
2563 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2564
2565 if (top_level)
2566 IoSetTopLevelIrp(NULL);
2567
2568 FsRtlExitFileSystem();
2569
2570 return Status;
2571 }
2572
2573 _Success_(return)
2574 bool get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ uint16_t len, _Out_ ULONG* atts) {
2575 if (len > 2 && val[0] == '0' && val[1] == 'x') {
2576 int i;
2577 ULONG dosnum = 0;
2578
2579 for (i = 2; i < len; i++) {
2580 dosnum *= 0x10;
2581
2582 if (val[i] >= '0' && val[i] <= '9')
2583 dosnum |= val[i] - '0';
2584 else if (val[i] >= 'a' && val[i] <= 'f')
2585 dosnum |= val[i] + 10 - 'a';
2586 else if (val[i] >= 'A' && val[i] <= 'F')
2587 dosnum |= val[i] + 10 - 'a';
2588 }
2589
2590 TRACE("DOSATTRIB: %08lx\n", dosnum);
2591
2592 *atts = dosnum;
2593
2594 return true;
2595 }
2596
2597 return false;
2598 }
2599
2600 ULONG get_file_attributes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_ uint64_t inode,
2601 _In_ uint8_t type, _In_ bool dotfile, _In_ bool ignore_xa, _In_opt_ PIRP Irp) {
2602 ULONG att;
2603 char* eaval;
2604 uint16_t ealen;
2605
2606 if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (uint8_t**)&eaval, &ealen, Irp)) {
2607 ULONG dosnum = 0;
2608
2609 if (get_file_attributes_from_xattr(eaval, ealen, &dosnum)) {
2610 ExFreePool(eaval);
2611
2612 if (type == BTRFS_TYPE_DIRECTORY)
2613 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2614 else if (type == BTRFS_TYPE_SYMLINK)
2615 dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2616
2617 if (type != BTRFS_TYPE_DIRECTORY)
2618 dosnum &= ~FILE_ATTRIBUTE_DIRECTORY;
2619
2620 if (inode == SUBVOL_ROOT_INODE) {
2621 if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2622 dosnum |= FILE_ATTRIBUTE_READONLY;
2623 else
2624 dosnum &= ~FILE_ATTRIBUTE_READONLY;
2625 }
2626
2627 return dosnum;
2628 }
2629
2630 ExFreePool(eaval);
2631 }
2632
2633 switch (type) {
2634 case BTRFS_TYPE_DIRECTORY:
2635 att = FILE_ATTRIBUTE_DIRECTORY;
2636 break;
2637
2638 case BTRFS_TYPE_SYMLINK:
2639 att = FILE_ATTRIBUTE_REPARSE_POINT;
2640 break;
2641
2642 default:
2643 att = 0;
2644 break;
2645 }
2646
2647 if (dotfile || (r->id == BTRFS_ROOT_FSTREE && inode == SUBVOL_ROOT_INODE))
2648 att |= FILE_ATTRIBUTE_HIDDEN;
2649
2650 att |= FILE_ATTRIBUTE_ARCHIVE;
2651
2652 if (inode == SUBVOL_ROOT_INODE) {
2653 if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2654 att |= FILE_ATTRIBUTE_READONLY;
2655 else
2656 att &= ~FILE_ATTRIBUTE_READONLY;
2657 }
2658
2659 // FIXME - get READONLY from ii->st_mode
2660 // FIXME - return SYSTEM for block/char devices?
2661
2662 if (att == 0)
2663 att = FILE_ATTRIBUTE_NORMAL;
2664
2665 return att;
2666 }
2667
2668 NTSTATUS sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject, _In_ PFILE_OBJECT FileObject, _In_ uint64_t StartingOffset, _In_ ULONG Length,
2669 _Out_writes_bytes_(Length) PUCHAR Buffer, _In_ bool override) {
2670 IO_STATUS_BLOCK IoStatus;
2671 LARGE_INTEGER Offset;
2672 PIRP Irp;
2673 PIO_STACK_LOCATION IrpSp;
2674 NTSTATUS Status;
2675 read_context context;
2676
2677 num_reads++;
2678
2679 RtlZeroMemory(&context, sizeof(read_context));
2680 KeInitializeEvent(&context.Event, NotificationEvent, false);
2681
2682 Offset.QuadPart = (LONGLONG)StartingOffset;
2683
2684 Irp = IoAllocateIrp(DeviceObject->StackSize, false);
2685
2686 if (!Irp) {
2687 ERR("IoAllocateIrp failed\n");
2688 return STATUS_INSUFFICIENT_RESOURCES;
2689 }
2690
2691 Irp->Flags |= IRP_NOCACHE;
2692 IrpSp = IoGetNextIrpStackLocation(Irp);
2693 IrpSp->MajorFunction = IRP_MJ_READ;
2694 IrpSp->FileObject = FileObject;
2695
2696 if (override)
2697 IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2698
2699 if (DeviceObject->Flags & DO_BUFFERED_IO) {
2700 Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
2701 if (!Irp->AssociatedIrp.SystemBuffer) {
2702 ERR("out of memory\n");
2703 Status = STATUS_INSUFFICIENT_RESOURCES;
2704 goto exit;
2705 }
2706
2707 Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
2708
2709 Irp->UserBuffer = Buffer;
2710 } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2711 Irp->MdlAddress = IoAllocateMdl(Buffer, Length, false, false, NULL);
2712 if (!Irp->MdlAddress) {
2713 ERR("IoAllocateMdl failed\n");
2714 Status = STATUS_INSUFFICIENT_RESOURCES;
2715 goto exit;
2716 }
2717
2718 Status = STATUS_SUCCESS;
2719
2720 _SEH2_TRY {
2721 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
2722 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2723 Status = _SEH2_GetExceptionCode();
2724 } _SEH2_END;
2725
2726 if (!NT_SUCCESS(Status)) {
2727 ERR("MmProbeAndLockPages threw exception %08lx\n", Status);
2728 IoFreeMdl(Irp->MdlAddress);
2729 goto exit;
2730 }
2731 } else
2732 Irp->UserBuffer = Buffer;
2733
2734 IrpSp->Parameters.Read.Length = Length;
2735 IrpSp->Parameters.Read.ByteOffset = Offset;
2736
2737 Irp->UserIosb = &IoStatus;
2738
2739 Irp->UserEvent = &context.Event;
2740
2741 IoSetCompletionRoutine(Irp, read_completion, &context, true, true, true);
2742
2743 Status = IoCallDriver(DeviceObject, Irp);
2744
2745 if (Status == STATUS_PENDING) {
2746 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL);
2747 Status = context.iosb.Status;
2748 }
2749
2750 if (DeviceObject->Flags & DO_DIRECT_IO) {
2751 MmUnlockPages(Irp->MdlAddress);
2752 IoFreeMdl(Irp->MdlAddress);
2753 }
2754
2755 exit:
2756 IoFreeIrp(Irp);
2757
2758 return Status;
2759 }
2760
2761 bool check_superblock_checksum(superblock* sb) {
2762 switch (sb->csum_type) {
2763 case CSUM_TYPE_CRC32C: {
2764 uint32_t crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2765
2766 if (crc32 == *((uint32_t*)sb->checksum))
2767 return true;
2768
2769 WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
2770
2771 break;
2772 }
2773
2774 case CSUM_TYPE_XXHASH: {
2775 uint64_t hash = XXH64(&sb->uuid, sizeof(superblock) - sizeof(sb->checksum), 0);
2776
2777 if (hash == *((uint64_t*)sb->checksum))
2778 return true;
2779
2780 WARN("superblock hash was %I64x, expected %I64x\n", hash, *((uint64_t*)sb->checksum));
2781
2782 break;
2783 }
2784
2785 case CSUM_TYPE_SHA256: {
2786 uint8_t hash[SHA256_HASH_SIZE];
2787
2788 calc_sha256(hash, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
2789
2790 if (RtlCompareMemory(hash, sb, SHA256_HASH_SIZE) == SHA256_HASH_SIZE)
2791 return true;
2792
2793 WARN("superblock hash was invalid\n");
2794
2795 break;
2796 }
2797
2798 case CSUM_TYPE_BLAKE2: {
2799 uint8_t hash[BLAKE2_HASH_SIZE];
2800
2801 blake2b(hash, sizeof(hash), &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
2802
2803 if (RtlCompareMemory(hash, sb, BLAKE2_HASH_SIZE) == BLAKE2_HASH_SIZE)
2804 return true;
2805
2806 WARN("superblock hash was invalid\n");
2807
2808 break;
2809 }
2810
2811 default:
2812 WARN("unrecognized csum type %x\n", sb->csum_type);
2813 }
2814
2815 return false;
2816 }
2817
2818 static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT fileobj, _In_ uint64_t length) {
2819 NTSTATUS Status;
2820 superblock* sb;
2821 ULONG i, to_read;
2822 uint8_t valid_superblocks;
2823
2824 to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
2825
2826 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
2827 if (!sb) {
2828 ERR("out of memory\n");
2829 return STATUS_INSUFFICIENT_RESOURCES;
2830 }
2831
2832 if (superblock_addrs[0] + to_read > length) {
2833 WARN("device was too short to have any superblock\n");
2834 ExFreePool(sb);
2835 return STATUS_UNRECOGNIZED_VOLUME;
2836 }
2837
2838 i = 0;
2839 valid_superblocks = 0;
2840
2841 while (superblock_addrs[i] > 0) {
2842 if (i > 0 && superblock_addrs[i] + to_read > length)
2843 break;
2844
2845 Status = sync_read_phys(device, fileobj, superblock_addrs[i], to_read, (PUCHAR)sb, false);
2846 if (!NT_SUCCESS(Status)) {
2847 ERR("Failed to read superblock %lu: %08lx\n", i, Status);
2848 ExFreePool(sb);
2849 return Status;
2850 }
2851
2852 if (sb->magic != BTRFS_MAGIC) {
2853 if (i == 0) {
2854 TRACE("not a BTRFS volume\n");
2855 ExFreePool(sb);
2856 return STATUS_UNRECOGNIZED_VOLUME;
2857 }
2858 } else {
2859 TRACE("got superblock %lu!\n", i);
2860
2861 if (sb->sector_size == 0)
2862 WARN("superblock sector size was 0\n");
2863 else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
2864 WARN("invalid node size %x\n", sb->node_size);
2865 else if ((sb->node_size % sb->sector_size) != 0)
2866 WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
2867 else if (check_superblock_checksum(sb) && (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation)) {
2868 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2869 valid_superblocks++;
2870 }
2871 }
2872
2873 i++;
2874 }
2875
2876 ExFreePool(sb);
2877
2878 if (valid_superblocks == 0) {
2879 ERR("could not find any valid superblocks\n");
2880 return STATUS_INTERNAL_ERROR;
2881 }
2882
2883 TRACE("label is %s\n", Vcb->superblock.label);
2884
2885 return STATUS_SUCCESS;
2886 }
2887
2888 NTSTATUS dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject, _In_ ULONG ControlCode, _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize,
2889 _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ bool Override, _Out_opt_ IO_STATUS_BLOCK* iosb) {
2890 PIRP Irp;
2891 KEVENT Event;
2892 NTSTATUS Status;
2893 PIO_STACK_LOCATION IrpSp;
2894 IO_STATUS_BLOCK IoStatus;
2895
2896 KeInitializeEvent(&Event, NotificationEvent, false);
2897
2898 Irp = IoBuildDeviceIoControlRequest(ControlCode,
2899 DeviceObject,
2900 InputBuffer,
2901 InputBufferSize,
2902 OutputBuffer,
2903 OutputBufferSize,
2904 false,
2905 &Event,
2906 &IoStatus);
2907
2908 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2909
2910 if (Override) {
2911 IrpSp = IoGetNextIrpStackLocation(Irp);
2912 IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2913 }
2914
2915 Status = IoCallDriver(DeviceObject, Irp);
2916
2917 if (Status == STATUS_PENDING) {
2918 KeWaitForSingleObject(&Event, Executive, KernelMode, false, NULL);
2919 Status = IoStatus.Status;
2920 }
2921
2922 if (iosb)
2923 *iosb = IoStatus;
2924
2925 return Status;
2926 }
2927
2928 _Requires_exclusive_lock_held_(Vcb->tree_lock)
2929 static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ uint64_t id, _In_ uint64_t addr,
2930 _In_ uint64_t generation, _In_opt_ traverse_ptr* tp) {
2931 root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
2932 if (!r) {
2933 ERR("out of memory\n");
2934 return STATUS_INSUFFICIENT_RESOURCES;
2935 }
2936
2937 r->id = id;
2938 r->dirty = false;
2939 r->received = false;
2940 r->reserved = NULL;
2941 r->treeholder.address = addr;
2942 r->treeholder.tree = NULL;
2943 r->treeholder.generation = generation;
2944 r->parent = 0;
2945 r->send_ops = 0;
2946 r->fcbs_version = 0;
2947 r->checked_for_orphans = false;
2948 r->dropped = false;
2949 InitializeListHead(&r->fcbs);
2950 RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
2951
2952 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
2953 if (!r->nonpaged) {
2954 ERR("out of memory\n");
2955 ExFreePool(r);
2956 return STATUS_INSUFFICIENT_RESOURCES;
2957 }
2958
2959 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
2960
2961 r->lastinode = 0;
2962
2963 if (tp) {
2964 RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
2965 if (tp->item->size < sizeof(ROOT_ITEM))
2966 RtlZeroMemory(((uint8_t*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
2967 } else
2968 RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
2969
2970 if (!Vcb->readonly && (r->id == BTRFS_ROOT_ROOT || r->id == BTRFS_ROOT_FSTREE || (r->id >= 0x100 && !(r->id & 0xf000000000000000)))) { // FS tree root
2971 // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
2972 get_last_inode(Vcb, r, NULL);
2973
2974 if (r->id == BTRFS_ROOT_ROOT && r->lastinode < 0x100)
2975 r->lastinode = 0x100;
2976 }
2977
2978 InsertTailList(&Vcb->roots, &r->list_entry);
2979
2980 switch (r->id) {
2981 case BTRFS_ROOT_ROOT:
2982 Vcb->root_root = r;
2983 break;
2984
2985 case BTRFS_ROOT_EXTENT:
2986 Vcb->extent_root = r;
2987 break;
2988
2989 case BTRFS_ROOT_CHUNK:
2990 Vcb->chunk_root = r;
2991 break;
2992
2993 case BTRFS_ROOT_DEVTREE:
2994 Vcb->dev_root = r;
2995 break;
2996
2997 case BTRFS_ROOT_CHECKSUM:
2998 Vcb->checksum_root = r;
2999 break;
3000
3001 case BTRFS_ROOT_UUID:
3002 Vcb->uuid_root = r;
3003 break;
3004
3005 case BTRFS_ROOT_FREE_SPACE:
3006 Vcb->space_root = r;
3007 break;
3008
3009 case BTRFS_ROOT_DATA_RELOC:
3010 Vcb->data_reloc_root = r;
3011 break;
3012 }
3013
3014 return STATUS_SUCCESS;
3015 }
3016
3017 static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock) _In_ device_extension* Vcb, _In_opt_ PIRP Irp) {
3018 traverse_ptr tp, next_tp;
3019 KEY searchkey;
3020 bool b;
3021 NTSTATUS Status;
3022
3023 searchkey.obj_id = 0;
3024 searchkey.obj_type = 0;
3025 searchkey.offset = 0;
3026
3027 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
3028 if (!NT_SUCCESS(Status)) {
3029 ERR("error - find_item returned %08lx\n", Status);
3030 return Status;
3031 }
3032
3033 do {
3034 TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3035
3036 if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
3037 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
3038
3039 if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
3040 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_ITEM, byte_limit));
3041 } else {
3042 TRACE("root %I64x - address %I64x\n", tp.item->key.obj_id, ri->block_number);
3043
3044 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, ri->generation, &tp);
3045 if (!NT_SUCCESS(Status)) {
3046 ERR("add_root returned %08lx\n", Status);
3047 return Status;
3048 }
3049 }
3050 } else if (tp.item->key.obj_type == TYPE_ROOT_BACKREF && !IsListEmpty(&Vcb->roots)) {
3051 root* lastroot = CONTAINING_RECORD(Vcb->roots.Blink, root, list_entry);
3052
3053 if (lastroot->id == tp.item->key.obj_id)
3054 lastroot->parent = tp.item->key.offset;
3055 }
3056
3057 b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3058
3059 if (b)
3060 tp = next_tp;
3061 } while (b);
3062
3063 if (!Vcb->readonly && !Vcb->data_reloc_root) {
3064 root* reloc_root;
3065 INODE_ITEM* ii;
3066 uint16_t irlen;
3067 INODE_REF* ir;
3068 LARGE_INTEGER time;
3069 BTRFS_TIME now;
3070
3071 WARN("data reloc root doesn't exist, creating it\n");
3072
3073 Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, false, 0, Irp);
3074
3075 if (!NT_SUCCESS(Status)) {
3076 ERR("create_root returned %08lx\n", Status);
3077 return Status;
3078 }
3079
3080 reloc_root->root_item.inode.generation = 1;
3081 reloc_root->root_item.inode.st_size = 3;
3082 reloc_root->root_item.inode.st_blocks = Vcb->superblock.node_size;
3083 reloc_root->root_item.inode.st_nlink = 1;
3084 reloc_root->root_item.inode.st_mode = 040755;
3085 reloc_root->root_item.inode.flags = 0xffffffff80000000;
3086 reloc_root->root_item.objid = SUBVOL_ROOT_INODE;
3087 reloc_root->root_item.bytes_used = Vcb->superblock.node_size;
3088
3089 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
3090 if (!ii) {
3091 ERR("out of memory\n");
3092 return STATUS_INSUFFICIENT_RESOURCES;
3093 }
3094
3095 KeQuerySystemTime(&time);
3096 win_time_to_unix(time, &now);
3097
3098 RtlZeroMemory(ii, sizeof(INODE_ITEM));
3099 ii->generation = Vcb->superblock.generation;
3100 ii->st_blocks = Vcb->superblock.node_size;
3101 ii->st_nlink = 1;
3102 ii->st_mode = 040755;
3103 ii->st_atime = now;
3104 ii->st_ctime = now;
3105 ii->st_mtime = now;
3106
3107 Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
3108 if (!NT_SUCCESS(Status)) {
3109 ERR("insert_tree_item returned %08lx\n", Status);
3110 ExFreePool(ii);
3111 return Status;
3112 }
3113
3114 irlen = (uint16_t)offsetof(INODE_REF, name[0]) + 2;
3115 ir = ExAllocatePoolWithTag(PagedPool, irlen, ALLOC_TAG);
3116 if (!ir) {
3117 ERR("out of memory\n");
3118 return STATUS_INSUFFICIENT_RESOURCES;
3119 }
3120
3121 ir->index = 0;
3122 ir->n = 2;
3123 ir->name[0] = '.';
3124 ir->name[1] = '.';
3125
3126 Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp);
3127 if (!NT_SUCCESS(Status)) {
3128 ERR("insert_tree_item returned %08lx\n", Status);
3129 ExFreePool(ir);
3130 return Status;
3131 }
3132
3133 Vcb->data_reloc_root = reloc_root;
3134 Vcb->need_write = true;
3135 }
3136
3137 return STATUS_SUCCESS;
3138 }
3139
3140 static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ device* dev, _In_opt_ PIRP Irp) {
3141 KEY searchkey;
3142 traverse_ptr tp, next_tp;
3143 bool b;
3144 uint64_t lastaddr;
3145 NTSTATUS Status;
3146
3147 InitializeListHead(&dev->space);
3148
3149 searchkey.obj_id = 0;
3150 searchkey.obj_type = TYPE_DEV_STATS;
3151 searchkey.offset = dev->devitem.dev_id;
3152
3153 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3154 if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
3155 RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(uint64_t) * 5, tp.item->size));
3156
3157 searchkey.obj_id = dev->devitem.dev_id;
3158 searchkey.obj_type = TYPE_DEV_EXTENT;
3159 searchkey.offset = 0;
3160
3161 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3162 if (!NT_SUCCESS(Status)) {
3163 ERR("error - find_item returned %08lx\n", Status);
3164 return Status;
3165 }
3166
3167 lastaddr = 0;
3168
3169 do {
3170 if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
3171 if (tp.item->size >= sizeof(DEV_EXTENT)) {
3172 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
3173
3174 if (tp.item->key.offset > lastaddr) {
3175 Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
3176 if (!NT_SUCCESS(Status)) {
3177 ERR("add_space_entry returned %08lx\n", Status);
3178 return Status;
3179 }
3180 }
3181
3182 lastaddr = tp.item->key.offset + de->length;
3183 } else {
3184 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
3185 }
3186 }
3187
3188 b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3189
3190 if (b) {
3191 tp = next_tp;
3192 if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
3193 break;
3194 }
3195 } while (b);
3196
3197 if (lastaddr < dev->devitem.num_bytes) {
3198 Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
3199 if (!NT_SUCCESS(Status)) {
3200 ERR("add_space_entry returned %08lx\n", Status);
3201 return Status;
3202 }
3203 }
3204
3205 // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
3206
3207 space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
3208
3209 return STATUS_SUCCESS;
3210 }
3211
3212 static void add_device_to_list(_In_ device_extension* Vcb, _In_ device* dev) {
3213 LIST_ENTRY* le;
3214
3215 le = Vcb->devices.Flink;
3216
3217 while (le != &Vcb->devices) {
3218 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
3219
3220 if (dev2->devitem.dev_id > dev->devitem.dev_id) {
3221 InsertHeadList(le->Blink, &dev->list_entry);
3222 return;
3223 }
3224
3225 le = le->Flink;
3226 }
3227
3228 InsertTailList(&Vcb->devices, &dev->list_entry);
3229 }
3230
3231 _Ret_maybenull_
3232 device* find_device_from_uuid(_In_ device_extension* Vcb, _In_ BTRFS_UUID* uuid) {
3233 volume_device_extension* vde;
3234 pdo_device_extension* pdode;
3235 LIST_ENTRY* le;
3236
3237 le = Vcb->devices.Flink;
3238 while (le != &Vcb->devices) {
3239 device* dev = CONTAINING_RECORD(le, device, list_entry);
3240
3241 TRACE("device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev->devitem.dev_id,
3242 dev->devitem.device_uuid.uuid[0], dev->devitem.device_uuid.uuid[1], dev->devitem.device_uuid.uuid[2], dev->devitem.device_uuid.uuid[3], dev->devitem.device_uuid.uuid[4], dev->devitem.device_uuid.uuid[5], dev->devitem.device_uuid.uuid[6], dev->devitem.device_uuid.uuid[7],
3243 dev->devitem.device_uuid.uuid[8], dev->devitem.device_uuid.uuid[9], dev->devitem.device_uuid.uuid[10], dev->devitem.device_uuid.uuid[11], dev->devitem.device_uuid.uuid[12], dev->devitem.device_uuid.uuid[13], dev->devitem.device_uuid.uuid[14], dev->devitem.device_uuid.uuid[15]);
3244
3245 if (RtlCompareMemory(&dev->devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3246 TRACE("returning device %I64x\n", dev->devitem.dev_id);
3247 return dev;
3248 }
3249
3250 le = le->Flink;
3251 }
3252
3253 vde = Vcb->vde;
3254
3255 if (!vde)
3256 goto end;
3257
3258 pdode = vde->pdode;
3259
3260 ExAcquireResourceSharedLite(&pdode->child_lock, true);
3261
3262 if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3263 le = pdode->children.Flink;
3264
3265 while (le != &pdode->children) {
3266 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
3267
3268 if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3269 device* dev;
3270
3271 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3272 if (!dev) {
3273 ExReleaseResourceLite(&pdode->child_lock);
3274 ERR("out of memory\n");
3275 return NULL;
3276 }
3277
3278 RtlZeroMemory(dev, sizeof(device));
3279 dev->devobj = vc->devobj;
3280 dev->fileobj = vc->fileobj;
3281 dev->devitem.device_uuid = *uuid;
3282 dev->devitem.dev_id = vc->devid;
3283 dev->devitem.num_bytes = vc->size;
3284 dev->seeding = vc->seeding;
3285 dev->readonly = dev->seeding;
3286 dev->reloc = false;
3287 dev->removable = false;
3288 dev->disk_num = vc->disk_num;
3289 dev->part_num = vc->part_num;
3290 dev->num_trim_entries = 0;
3291 InitializeListHead(&dev->trim_list);
3292
3293 add_device_to_list(Vcb, dev);
3294 Vcb->devices_loaded++;
3295
3296 ExReleaseResourceLite(&pdode->child_lock);
3297
3298 return dev;
3299 }
3300
3301 le = le->Flink;
3302 }
3303 }
3304
3305 ExReleaseResourceLite(&pdode->child_lock);
3306
3307 end:
3308 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3309 uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
3310 uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
3311
3312 return NULL;
3313 }
3314
3315 static bool is_device_removable(_In_ PDEVICE_OBJECT devobj) {
3316 NTSTATUS Status;
3317 STORAGE_HOTPLUG_INFO shi;
3318
3319 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), true, NULL);
3320
3321 if (!NT_SUCCESS(Status)) {
3322 ERR("dev_ioctl returned %08lx\n", Status);
3323 return false;
3324 }
3325
3326 return shi.MediaRemovable != 0 ? true : false;
3327 }
3328
3329 static ULONG get_device_change_count(_In_ PDEVICE_OBJECT devobj) {
3330 NTSTATUS Status;
3331 ULONG cc;
3332 IO_STATUS_BLOCK iosb;
3333
3334 Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
3335
3336 if (!NT_SUCCESS(Status)) {
3337 ERR("dev_ioctl returned %08lx\n", Status);
3338 return 0;
3339 }
3340
3341 if (iosb.Information < sizeof(ULONG)) {
3342 ERR("iosb.Information was too short\n");
3343 return 0;
3344 }
3345
3346 return cc;
3347 }
3348
3349 void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ bool get_nums) {
3350 NTSTATUS Status;
3351 ULONG aptelen;
3352 ATA_PASS_THROUGH_EX* apte;
3353 STORAGE_PROPERTY_QUERY spq;
3354 DEVICE_TRIM_DESCRIPTOR dtd;
3355
3356 dev->removable = is_device_removable(dev->devobj);
3357 dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
3358
3359 if (get_nums) {
3360 STORAGE_DEVICE_NUMBER sdn;
3361
3362 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
3363 &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
3364
3365 if (!NT_SUCCESS(Status)) {
3366 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status);
3367 dev->disk_num = 0xffffffff;
3368 dev->part_num = 0xffffffff;
3369 } else {
3370 dev->disk_num = sdn.DeviceNumber;
3371 dev->part_num = sdn.PartitionNumber;
3372 }
3373 }
3374
3375 dev->trim = false;
3376 dev->readonly = dev->seeding;
3377 dev->reloc = false;
3378 dev->num_trim_entries = 0;
3379 dev->stats_changed = false;
3380 InitializeListHead(&dev->trim_list);
3381
3382 if (!dev->readonly) {
3383 Status = dev_ioctl(dev->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0,
3384 NULL, 0, true, NULL);
3385 if (Status == STATUS_MEDIA_WRITE_PROTECTED)
3386 dev->readonly = true;
3387 }
3388
3389 aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
3390 apte = ExAllocatePoolWithTag(NonPagedPool, aptelen, ALLOC_TAG);
3391 if (!apte) {
3392 ERR("out of memory\n");
3393 return;
3394 }
3395
3396 RtlZeroMemory(apte, aptelen);
3397
3398 apte->Length = sizeof(ATA_PASS_THROUGH_EX);
3399 apte->AtaFlags = ATA_FLAGS_DATA_IN;
3400 apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
3401 apte->TimeOutValue = 3;
3402 apte->DataBufferOffset = apte->Length;
3403 apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY;
3404
3405 Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
3406 apte, aptelen, true, NULL);
3407
3408 if (!NT_SUCCESS(Status))
3409 TRACE("IOCTL_ATA_PASS_THROUGH returned %08lx for IDENTIFY DEVICE\n", Status);
3410 else {
3411 IDENTIFY_DEVICE_DATA* idd = (IDENTIFY_DEVICE_DATA*)((uint8_t*)apte + sizeof(ATA_PASS_THROUGH_EX));
3412
3413 if (idd->CommandSetSupport.FlushCache) {
3414 dev->can_flush = true;
3415 TRACE("FLUSH CACHE supported\n");
3416 } else
3417 TRACE("FLUSH CACHE not supported\n");
3418 }
3419
3420 ExFreePool(apte);
3421
3422 #ifdef DEBUG_TRIM_EMULATION
3423 dev->trim = true;
3424 Vcb->trim = true;
3425 #else
3426 spq.PropertyId = StorageDeviceTrimProperty;
3427 spq.QueryType = PropertyStandardQuery;
3428 spq.AdditionalParameters[0] = 0;
3429
3430 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(STORAGE_PROPERTY_QUERY),
3431 &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), true, NULL);
3432
3433 if (NT_SUCCESS(Status)) {
3434 if (dtd.TrimEnabled) {
3435 dev->trim = true;
3436 Vcb->trim = true;
3437 TRACE("TRIM supported\n");
3438 } else
3439 TRACE("TRIM not supported\n");
3440 }
3441 #endif
3442
3443 RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3444 }
3445
3446 static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3447 traverse_ptr tp, next_tp;
3448 KEY searchkey;
3449 bool b;
3450 chunk* c;
3451 NTSTATUS Status;
3452
3453 searchkey.obj_id = 0;
3454 searchkey.obj_type = 0;
3455 searchkey.offset = 0;
3456
3457 Vcb->data_flags = 0;
3458 Vcb->metadata_flags = 0;
3459 Vcb->system_flags = 0;
3460
3461 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp);
3462 if (!NT_SUCCESS(Status)) {
3463 ERR("error - find_item returned %08lx\n", Status);
3464 return Status;
3465 }
3466
3467 do {
3468 TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3469
3470 if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
3471 if (tp.item->size < sizeof(DEV_ITEM)) {
3472 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
3473 } else {
3474 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
3475 LIST_ENTRY* le;
3476 bool done = false;
3477
3478 le = Vcb->devices.Flink;
3479 while (le != &Vcb->devices) {
3480 device* dev = CONTAINING_RECORD(le, device, list_entry);
3481
3482 if (dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3483 RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
3484
3485 if (le != Vcb->devices.Flink)
3486 init_device(Vcb, dev, true);
3487
3488 done = true;
3489 break;
3490 }
3491
3492 le = le->Flink;
3493 }
3494
3495 if (!done && Vcb->vde) {
3496 volume_device_extension* vde = Vcb->vde;
3497 pdo_device_extension* pdode = vde->pdode;
3498
3499 ExAcquireResourceSharedLite(&pdode->child_lock, true);
3500
3501 if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3502 le = pdode->children.Flink;
3503
3504 while (le != &pdode->children) {
3505 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
3506
3507 if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3508 device* dev;
3509
3510 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3511 if (!dev) {
3512 ExReleaseResourceLite(&pdode->child_lock);
3513 ERR("out of memory\n");
3514 return STATUS_INSUFFICIENT_RESOURCES;
3515 }
3516
3517 RtlZeroMemory(dev, sizeof(device));
3518
3519 dev->devobj = vc->devobj;
3520 dev->fileobj = vc->fileobj;
3521 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3522 dev->seeding = vc->seeding;
3523 init_device(Vcb, dev, false);
3524
3525 if (dev->devitem.num_bytes > vc->size) {
3526 WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", tp.item->key.offset,
3527 dev->devitem.num_bytes, vc->size);
3528
3529 dev->devitem.num_bytes = vc->size;
3530 }
3531
3532 dev->disk_num = vc->disk_num;
3533 dev->part_num = vc->part_num;
3534 add_device_to_list(Vcb, dev);
3535 Vcb->devices_loaded++;
3536
3537 done = true;
3538 break;
3539 }
3540
3541 le = le->Flink;
3542 }
3543
3544 if (!done) {
3545 if (!Vcb->options.allow_degraded) {
3546 ERR("volume not found: device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset,
3547 di->device_uuid.uuid[0], di->device_uuid.uuid[1], di->device_uuid.uuid[2], di->device_uuid.uuid[3], di->device_uuid.uuid[4], di->device_uuid.uuid[5], di->device_uuid.uuid[6], di->device_uuid.uuid[7],
3548 di->device_uuid.uuid[8], di->device_uuid.uuid[9], di->device_uuid.uuid[10], di->device_uuid.uuid[11], di->device_uuid.uuid[12], di->device_uuid.uuid[13], di->device_uuid.uuid[14], di->device_uuid.uuid[15]);
3549 } else {
3550 device* dev;
3551
3552 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
3553 if (!dev) {
3554 ExReleaseResourceLite(&pdode->child_lock);
3555 ERR("out of memory\n");
3556 return STATUS_INSUFFICIENT_RESOURCES;
3557 }
3558
3559 RtlZeroMemory(dev, sizeof(device));
3560
3561 // Missing device, so we keep dev->devobj as NULL
3562 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3563 InitializeListHead(&dev->trim_list);
3564
3565 add_device_to_list(Vcb, dev);
3566 Vcb->devices_loaded++;
3567 }
3568 }
3569 } else
3570 ERR("unexpected device %I64x found\n", tp.item->key.offset);
3571
3572 ExReleaseResourceLite(&pdode->child_lock);
3573 }
3574 }
3575 } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
3576 if (tp.item->size < sizeof(CHUNK_ITEM)) {
3577 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
3578 } else {
3579 c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
3580
3581 if (!c) {
3582 ERR("out of memory\n");
3583 return STATUS_INSUFFICIENT_RESOURCES;
3584 }
3585
3586 c->size = tp.item->size;
3587 c->offset = tp.item->key.offset;
3588 c->used = c->oldused = 0;
3589 c->cache = c->old_cache = NULL;
3590 c->created = false;
3591 c->readonly = false;
3592 c->reloc = false;
3593 c->cache_loaded = false;
3594 c->changed = false;
3595 c->space_changed = false;
3596 c->balance_num = 0;
3597
3598 c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
3599
3600 if (!c->chunk_item) {
3601 ERR("out of memory\n");
3602 ExFreePool(c);
3603 return STATUS_INSUFFICIENT_RESOURCES;
3604 }
3605
3606 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
3607
3608 if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
3609 Vcb->data_flags = c->chunk_item->type;
3610
3611 if (c->chunk_item->type & BLOCK_FLAG_METADATA && c->chunk_item->type > Vcb->metadata_flags)
3612 Vcb->metadata_flags = c->chunk_item->type;
3613
3614 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM && c->chunk_item->type > Vcb->system_flags)
3615 Vcb->system_flags = c->chunk_item->type;
3616
3617 if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
3618 if (c->chunk_item->sub_stripes == 0 || c->chunk_item->sub_stripes > c->chunk_item->num_stripes) {
3619 ERR("chunk %I64x: invalid stripes (num_stripes %u, sub_stripes %u)\n", c->offset, c->chunk_item->num_stripes, c->chunk_item->sub_stripes);
3620 ExFreePool(c->chunk_item);
3621 ExFreePool(c);
3622 return STATUS_INTERNAL_ERROR;
3623 }
3624 }
3625
3626 if (c->chunk_item->num_stripes > 0) {
3627 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3628 uint16_t i;
3629
3630 c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
3631
3632 if (!c->devices) {
3633 ERR("out of memory\n");
3634 ExFreePool(c->chunk_item);
3635 ExFreePool(c);
3636 return STATUS_INSUFFICIENT_RESOURCES;
3637 }
3638
3639 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3640 c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3641 TRACE("device %u = %p\n", i, c->devices[i]);
3642
3643 if (!c->devices[i]) {
3644 ERR("missing device\n");
3645 ExFreePool(c->chunk_item);
3646 ExFreePool(c);
3647 return STATUS_INTERNAL_ERROR;
3648 }
3649
3650 if (c->devices[i]->readonly)
3651 c->readonly = true;
3652 }
3653 } else {
3654 ERR("chunk %I64x: number of stripes is 0\n", c->offset);
3655 ExFreePool(c->chunk_item);
3656 ExFreePool(c);
3657 return STATUS_INTERNAL_ERROR;
3658 }
3659
3660 ExInitializeResourceLite(&c->lock);
3661 ExInitializeResourceLite(&c->changed_extents_lock);
3662
3663 InitializeListHead(&c->space);
3664 InitializeListHead(&c->space_size);
3665 InitializeListHead(&c->deleting);
3666 InitializeListHead(&c->changed_extents);
3667
3668 InitializeListHead(&c->range_locks);
3669 ExInitializeResourceLite(&c->range_locks_lock);
3670 KeInitializeEvent(&c->range_locks_event, NotificationEvent, false);
3671
3672 InitializeListHead(&c->partial_stripes);
3673 ExInitializeResourceLite(&c->partial_stripes_lock);
3674
3675 c->last_alloc_set = false;
3676
3677 c->last_stripe = 0;
3678
3679 InsertTailList(&Vcb->chunks, &c->list_entry);
3680
3681 c->list_entry_balance.Flink = NULL;
3682 }
3683 }
3684
3685 b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3686
3687 if (b)
3688 tp = next_tp;
3689 } while (b);
3690
3691 Vcb->log_to_phys_loaded = true;
3692
3693 if (Vcb->data_flags == 0)
3694 Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
3695
3696 if (Vcb->metadata_flags == 0)
3697 Vcb->metadata_flags = BLOCK_FLAG_METADATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3698
3699 if (Vcb->system_flags == 0)
3700 Vcb->system_flags = BLOCK_FLAG_SYSTEM | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
3701
3702 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) {
3703 Vcb->metadata_flags |= BLOCK_FLAG_DATA;
3704 Vcb->data_flags = Vcb->metadata_flags;
3705 }
3706
3707 return STATUS_SUCCESS;
3708 }
3709
3710 void protect_superblocks(_Inout_ chunk* c) {
3711 uint16_t i = 0, j;
3712 uint64_t off_start, off_end;
3713
3714 // The Linux driver also protects all the space before the first superblock.
3715 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3716 // evidently Linux assumes the chunk at 0 is always SINGLE.
3717 if (c->offset < superblock_addrs[0])
3718 space_list_subtract(c, false, c->offset, superblock_addrs[0] - c->offset, NULL);
3719
3720 while (superblock_addrs[i] != 0) {
3721 CHUNK_ITEM* ci = c->chunk_item;
3722 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3723
3724 if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
3725 for (j = 0; j < ci->num_stripes; j++) {
3726 uint16_t sub_stripes = max(ci->sub_stripes, 1);
3727
3728 if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3729 #ifdef _DEBUG
3730 uint64_t startoff;
3731 uint16_t startoffstripe;
3732 #endif
3733
3734 TRACE("cut out superblock in chunk %I64x\n", c->offset);
3735
3736 off_start = superblock_addrs[i] - cis[j].offset;
3737 off_start -= off_start % ci->stripe_length;
3738 off_start *= ci->num_stripes / sub_stripes;
3739 off_start += (j / sub_stripes) * ci->stripe_length;
3740
3741 off_end = off_start + ci->stripe_length;
3742
3743 #ifdef _DEBUG
3744 get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
3745 TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
3746 TRACE("startoff = %I64x, superblock = %I64x\n", startoff + cis[j].offset, superblock_addrs[i]);
3747 #endif
3748
3749 space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3750 }
3751 }
3752 } else if (ci->type & BLOCK_FLAG_RAID5) {
3753 uint64_t stripe_size = ci->size / (ci->num_stripes - 1);
3754
3755 for (j = 0; j < ci->num_stripes; j++) {
3756 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3757 TRACE("cut out superblock in chunk %I64x\n", c->offset);
3758
3759 off_start = superblock_addrs[i] - cis[j].offset;
3760 off_start -= off_start % ci->stripe_length;
3761 off_start *= ci->num_stripes - 1;
3762
3763 off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3764 off_end *= ci->num_stripes - 1;
3765
3766 TRACE("cutting out %I64x, size %I64x\n", c->offset + off_start, off_end - off_start);
3767
3768 space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3769 }
3770 }
3771 } else if (ci->type & BLOCK_FLAG_RAID6) {
3772 uint64_t stripe_size = ci->size / (ci->num_stripes - 2);
3773
3774 for (j = 0; j < ci->num_stripes; j++) {
3775 if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3776 TRACE("cut out superblock in chunk %I64x\n", c->offset);
3777
3778 off_start = superblock_addrs[i] - cis[j].offset;
3779 off_start -= off_start % ci->stripe_length;
3780 off_start *= ci->num_stripes - 2;
3781
3782 off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
3783 off_end *= ci->num_stripes - 2;
3784
3785 TRACE("cutting out %I64x, size %I64x\n", c->offset + off_start, off_end - off_start);
3786
3787 space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3788 }
3789 }
3790 } else { // SINGLE, DUPLICATE, RAID1, RAID1C3, RAID1C4
3791 for (j = 0; j < ci->num_stripes; j++) {
3792 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3793 TRACE("cut out superblock in chunk %I64x\n", c->offset);
3794
3795 // The Linux driver protects the whole stripe in which the superblock lives
3796
3797 off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3798 off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3799
3800 space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
3801 }
3802 }
3803 }
3804
3805 i++;
3806 }
3807 }
3808
3809 uint64_t chunk_estimate_phys_size(device_extension* Vcb, chunk* c, uint64_t u) {
3810 uint64_t nfactor, dfactor;
3811
3812 if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE || c->chunk_item->type & BLOCK_FLAG_RAID1 || c->chunk_item->type & BLOCK_FLAG_RAID10) {
3813 nfactor = 1;
3814 dfactor = 2;
3815 } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
3816 nfactor = Vcb->superblock.num_devices - 1;
3817 dfactor = Vcb->superblock.num_devices;
3818 } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
3819 nfactor = Vcb->superblock.num_devices - 2;
3820 dfactor = Vcb->superblock.num_devices;
3821 } else if (c->chunk_item->type & BLOCK_FLAG_RAID1C3) {
3822 nfactor = 1;
3823 dfactor = 3;
3824 } else if (c->chunk_item->type & BLOCK_FLAG_RAID1C4) {
3825 nfactor = 1;
3826 dfactor = 4;
3827 } else {
3828 nfactor = 1;
3829 dfactor = 1;
3830 }
3831
3832 return u * dfactor / nfactor;
3833 }
3834
3835 NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3836 LIST_ENTRY* le = Vcb->chunks.Flink;
3837 chunk* c;
3838 KEY searchkey;
3839 traverse_ptr tp;
3840 BLOCK_GROUP_ITEM* bgi;
3841 NTSTATUS Status;
3842
3843 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3844
3845 Vcb->superblock.bytes_used = 0;
3846
3847 while (le != &Vcb->chunks) {
3848 c = CONTAINING_RECORD(le, chunk, list_entry);
3849
3850 searchkey.obj_id = c->offset;
3851 searchkey.offset = c->chunk_item->size;
3852
3853 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
3854 if (!NT_SUCCESS(Status)) {
3855 ERR("error - find_item returned %08lx\n", Status);
3856 return Status;
3857 }
3858
3859 if (!keycmp(searchkey, tp.item->key)) {
3860 if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
3861 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
3862
3863 c->used = c->oldused = bgi->used;
3864
3865 TRACE("chunk %I64x has %I64x bytes used\n", c->offset, c->used);
3866
3867 Vcb->superblock.bytes_used += chunk_estimate_phys_size(Vcb, c, bgi->used);
3868 } else {
3869 ERR("(%I64x;%I64x,%x,%I64x) is %u bytes, expected %Iu\n",
3870 Vcb->extent_root->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
3871 }
3872 }
3873
3874 le = le->Flink;
3875 }
3876
3877 Vcb->chunk_usage_found = true;
3878
3879 return STATUS_SUCCESS;
3880 }
3881
3882 static NTSTATUS load_sys_chunks(_In_ device_extension* Vcb) {
3883 KEY key;
3884 ULONG n = Vcb->superblock.n;
3885
3886 while (n > 0) {
3887 if (n > sizeof(KEY)) {
3888 RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
3889 n -= sizeof(KEY);
3890 } else
3891 return STATUS_SUCCESS;
3892
3893 TRACE("bootstrap: %I64x,%x,%I64x\n", key.obj_id, key.obj_type, key.offset);
3894
3895 if (key.obj_type == TYPE_CHUNK_ITEM) {
3896 CHUNK_ITEM* ci;
3897 USHORT cisize;
3898 sys_chunk* sc;
3899
3900 if (n < sizeof(CHUNK_ITEM))
3901 return STATUS_SUCCESS;
3902
3903 ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
3904 cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
3905
3906 if (n < cisize)
3907 return STATUS_SUCCESS;
3908
3909 sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
3910
3911 if (!sc) {
3912 ERR("out of memory\n");
3913 return STATUS_INSUFFICIENT_RESOURCES;
3914 }
3915
3916 sc->key = key;
3917 sc->size = cisize;
3918 sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
3919
3920 if (!sc->data) {
3921 ERR("out of memory\n");
3922 ExFreePool(sc);
3923 return STATUS_INSUFFICIENT_RESOURCES;
3924 }
3925
3926 RtlCopyMemory(sc->data, ci, sc->size);
3927 InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
3928
3929 n -= cisize;
3930 } else {
3931 ERR("unexpected item %I64x,%x,%I64x in bootstrap\n", key.obj_id, key.obj_type, key.offset);
3932 return STATUS_INTERNAL_ERROR;
3933 }
3934 }
3935
3936 return STATUS_SUCCESS;
3937 }
3938
3939 _Ret_maybenull_
3940 root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
3941 LIST_ENTRY* le;
3942
3943 static const char fn[] = "default";
3944 static uint32_t crc32 = 0x8dbfc2d2;
3945
3946 if (Vcb->options.subvol_id != 0) {
3947 le = Vcb->roots.Flink;
3948 while (le != &Vcb->roots) {
3949 root* r = CONTAINING_RECORD(le, root, list_entry);
3950
3951 if (r->id == Vcb->options.subvol_id)
3952 return r;
3953
3954 le = le->Flink;
3955 }
3956 }
3957
3958 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
3959 NTSTATUS Status;
3960 KEY searchkey;
3961 traverse_ptr tp;
3962 DIR_ITEM* di;
3963
3964 searchkey.obj_id = Vcb->superblock.root_dir_objectid;
3965 searchkey.obj_type = TYPE_DIR_ITEM;
3966 searchkey.offset = crc32;
3967
3968 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
3969 if (!NT_SUCCESS(Status)) {
3970 ERR("error - find_item returned %08lx\n", Status);
3971 goto end;
3972 }
3973
3974 if (keycmp(tp.item->key, searchkey)) {
3975 ERR("could not find (%I64x,%x,%I64x) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3976 goto end;
3977 }
3978
3979 if (tp.item->size < sizeof(DIR_ITEM)) {
3980 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
3981 goto end;
3982 }
3983
3984 di = (DIR_ITEM*)tp.item->data;
3985
3986 if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
3987 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM) - 1 + di->n);
3988 goto end;
3989 }
3990
3991 if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
3992 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3993 goto end;
3994 }
3995
3996 if (di->key.obj_type != TYPE_ROOT_ITEM) {
3997 ERR("default root has key (%I64x,%x,%I64x), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
3998 goto end;
3999 }
4000
4001 le = Vcb->roots.Flink;
4002 while (le != &Vcb->roots) {
4003 root* r = CONTAINING_RECORD(le, root, list_entry);
4004
4005 if (r->id == di->key.obj_id)
4006 return r;
4007
4008 le = le->Flink;
4009 }
4010
4011 ERR("could not find root %I64x, using default instead\n", di->key.obj_id);
4012 }
4013
4014 end:
4015 le = Vcb->roots.Flink;
4016 while (le != &Vcb->roots) {
4017 root* r = CONTAINING_RECORD(le, root, list_entry);
4018
4019 if (r->id == BTRFS_ROOT_FSTREE)
4020 return r;
4021
4022 le = le->Flink;
4023 }
4024
4025 return NULL;
4026 }
4027
4028 void init_file_cache(_In_ PFILE_OBJECT FileObject, _In_ CC_FILE_SIZES* ccfs) {
4029 TRACE("(%p, %p)\n", FileObject, ccfs);
4030
4031 CcInitializeCacheMap(FileObject, ccfs, false, &cache_callbacks, FileObject);
4032
4033 if (diskacc)
4034 fCcSetAdditionalCacheAttributesEx(FileObject, CC_ENABLE_DISK_IO_ACCOUNTING);
4035
4036 CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
4037 }
4038
4039 uint32_t get_num_of_processors() {
4040 KAFFINITY p = KeQueryActiveProcessors();
4041 uint32_t r = 0;
4042
4043 while (p != 0) {
4044 if (p & 1)
4045 r++;
4046
4047 p >>= 1;
4048 }
4049
4050 return r;
4051 }
4052
4053 static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
4054 device_extension* Vcb = DeviceObject->DeviceExtension;
4055 OBJECT_ATTRIBUTES oa;
4056 ULONG i;
4057
4058 Vcb->calcthreads.num_threads = get_num_of_processors();
4059
4060 Vcb->calcthreads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads, ALLOC_TAG);
4061 if (!Vcb->calcthreads.threads) {
4062 ERR("out of memory\n");
4063 return STATUS_INSUFFICIENT_RESOURCES;
4064 }
4065
4066 InitializeListHead(&Vcb->calcthreads.job_list);
4067 KeInitializeSpinLock(&Vcb->calcthreads.spinlock);
4068 KeInitializeEvent(&Vcb->calcthreads.event, NotificationEvent, false);
4069
4070 RtlZeroMemory(Vcb->calcthreads.threads, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads);
4071
4072 InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
4073
4074 for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
4075 NTSTATUS Status;
4076
4077 Vcb->calcthreads.threads[i].DeviceObject = DeviceObject;
4078 Vcb->calcthreads.threads[i].number = i;
4079 KeInitializeEvent(&Vcb->calcthreads.threads[i].finished, NotificationEvent, false);
4080
4081 Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, &oa, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
4082 if (!NT_SUCCESS(Status)) {
4083 ULONG j;
4084
4085 ERR("PsCreateSystemThread returned %08lx\n", Status);
4086
4087 for (j = 0; j < i; j++) {
4088 Vcb->calcthreads.threads[i].quit = true;
4089 }
4090
4091 KeSetEvent(&Vcb->calcthreads.event, 0, false);
4092
4093 return Status;
4094 }
4095 }
4096
4097 return STATUS_SUCCESS;
4098 }
4099
4100 static bool is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
4101 NTSTATUS Status;
4102 MOUNTDEV_NAME mdn, *mdn2;
4103 ULONG mdnsize;
4104
4105 Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL);
4106 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
4107 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
4108 return false;
4109 }
4110
4111 mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
4112
4113 mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
4114 if (!mdn2) {
4115 ERR("out of memory\n");
4116 return false;
4117 }
4118
4119 Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL);
4120 if (!NT_SUCCESS(Status)) {
4121 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
4122 ExFreePool(mdn2);
4123 return false;
4124 }
4125
4126 if (mdn2->NameLength > (sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) &&
4127 RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) == sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) {
4128 ExFreePool(mdn2);
4129 return true;
4130 }
4131
4132 ExFreePool(mdn2);
4133
4134 return false;
4135 }
4136
4137 static NTSTATUS get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _In_ const GUID* guid) {
4138 NTSTATUS Status;
4139 WCHAR *list = NULL, *s;
4140
4141 Status = IoGetDeviceInterfaces((PVOID)guid, NULL, 0, &list);
4142 if (!NT_SUCCESS(Status)) {
4143 ERR("IoGetDeviceInterfaces returned %08lx\n", Status);
4144 return Status;
4145 }
4146
4147 s = list;
4148 while (s[0] != 0) {
4149 PFILE_OBJECT FileObject;
4150 PDEVICE_OBJECT devobj;
4151 UNICODE_STRING name;
4152
4153 name.Length = name.MaximumLength = (USHORT)wcslen(s) * sizeof(WCHAR);
4154 name.Buffer = s;
4155
4156 if (NT_SUCCESS(IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &FileObject, &devobj))) {
4157 if (DeviceObject == devobj || DeviceObject == FileObject->DeviceObject) {
4158 ObDereferenceObject(FileObject);
4159
4160 pnp_name->Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
4161 if (!pnp_name->Buffer) {
4162 ERR("out of memory\n");
4163 Status = STATUS_INSUFFICIENT_RESOURCES;
4164 goto end;
4165 }
4166
4167 RtlCopyMemory(pnp_name->Buffer, name.Buffer, name.Length);
4168 pnp_name->Length = pnp_name->MaximumLength = name.Length;
4169
4170 Status = STATUS_SUCCESS;
4171 goto end;
4172 }
4173
4174 ObDereferenceObject(FileObject);
4175 }
4176
4177 s = &s[wcslen(s) + 1];
4178 }
4179
4180 pnp_name->Length = pnp_name->MaximumLength = 0;
4181 pnp_name->Buffer = 0;
4182
4183 Status = STATUS_NOT_FOUND;
4184
4185 end:
4186 if (list)
4187 ExFreePool(list);
4188
4189 return Status;
4190 }
4191
4192 NTSTATUS get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _Out_ const GUID** guid) {
4193 NTSTATUS Status;
4194
4195 Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_VOLUME);
4196 if (NT_SUCCESS(Status)) {
4197 *guid = &GUID_DEVINTERFACE_VOLUME;
4198 return Status;
4199 }
4200
4201 Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_HIDDEN_VOLUME);
4202 if (NT_SUCCESS(Status)) {
4203 *guid = &GUID_DEVINTERFACE_HIDDEN_VOLUME;
4204 return Status;
4205 }
4206
4207 Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_DISK);
4208 if (NT_SUCCESS(Status)) {
4209 *guid = &GUID_DEVINTERFACE_DISK;
4210 return Status;
4211 }
4212
4213 return STATUS_NOT_FOUND;
4214 }
4215
4216 _Success_(return>=0)
4217 static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ bool* pno_pnp) {
4218 NTSTATUS Status;
4219 ULONG to_read;
4220 superblock* sb;
4221 UNICODE_STRING pnp_name;
4222 const GUID* guid;
4223
4224 to_read = DeviceObject->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), DeviceObject->SectorSize);
4225
4226 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
4227 if (!sb) {
4228 ERR("out of memory\n");
4229 return STATUS_INSUFFICIENT_RESOURCES;
4230 }
4231
4232 Status = sync_read_phys(DeviceObject, NULL, superblock_addrs[0], to_read, (PUCHAR)sb, true);
4233 if (!NT_SUCCESS(Status)) {
4234 ERR("sync_read_phys returned %08lx\n", Status);
4235 goto end;
4236 }
4237
4238 if (sb->magic != BTRFS_MAGIC) {
4239 Status = STATUS_SUCCESS;
4240 goto end;
4241 }
4242
4243 if (!check_superblock_checksum(sb)) {
4244 Status = STATUS_SUCCESS;
4245 goto end;
4246 }
4247
4248 DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
4249
4250 pnp_name.Buffer = NULL;
4251
4252 Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
4253 if (!NT_SUCCESS(Status)) {
4254 WARN("get_device_pnp_name returned %08lx\n", Status);
4255 pnp_name.Length = 0;
4256 }
4257
4258 *pno_pnp = pnp_name.Length == 0;
4259
4260 if (pnp_name.Buffer)
4261 ExFreePool(pnp_name.Buffer);
4262
4263 Status = STATUS_SUCCESS;
4264
4265 end:
4266 ExFreePool(sb);
4267
4268 return Status;
4269 }
4270
4271 static bool still_has_superblock(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT fileobj) {
4272 NTSTATUS Status;
4273 ULONG to_read;
4274 superblock* sb;
4275
4276 if (!device)
4277 return false;
4278
4279 to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
4280
4281 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
4282 if (!sb) {
4283 ERR("out of memory\n");
4284 return false;
4285 }
4286
4287 Status = sync_read_phys(device, fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, true);
4288 if (!NT_SUCCESS(Status)) {
4289 ERR("Failed to read superblock: %08lx\n", Status);
4290 ExFreePool(sb);
4291 return false;
4292 }
4293
4294 if (sb->magic != BTRFS_MAGIC) {
4295 TRACE("not a BTRFS volume\n");
4296 ExFreePool(sb);
4297 return false;
4298 } else {
4299 if (!check_superblock_checksum(sb)) {
4300 ExFreePool(sb);
4301 return false;
4302 }
4303 }
4304
4305 ObReferenceObject(device);
4306
4307 while (device) {
4308 PDEVICE_OBJECT device2 = IoGetLowerDeviceObject(device);
4309
4310 device->Flags &= ~DO_VERIFY_VOLUME;
4311
4312 ObDereferenceObject(device);
4313
4314 device = device2;
4315 }
4316
4317 ExFreePool(sb);
4318
4319 return true;
4320 }
4321
4322 static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
4323 PIO_STACK_LOCATION IrpSp;
4324 PDEVICE_OBJECT NewDeviceObject = NULL;
4325 PDEVICE_OBJECT DeviceToMount, readobj;
4326 PFILE_OBJECT fileobj;
4327 NTSTATUS Status;
4328 device_extension* Vcb = NULL;
4329 LIST_ENTRY *le, batchlist;
4330 KEY searchkey;
4331 traverse_ptr tp;
4332 fcb* root_fcb = NULL;
4333 ccb* root_ccb = NULL;
4334 bool init_lookaside = false;
4335 device* dev;
4336 volume_device_extension* vde = NULL;
4337 pdo_device_extension* pdode = NULL;
4338 volume_child* vc;
4339 uint64_t readobjsize;
4340 OBJECT_ATTRIBUTES oa;
4341 device_extension* real_devext;
4342 KIRQL irql;
4343
4344 TRACE("(%p, %p)\n", DeviceObject, Irp);
4345
4346 if (DeviceObject != master_devobj) {
4347 Status = STATUS_INVALID_DEVICE_REQUEST;
4348 goto exit;
4349 }
4350
4351 IrpSp = IoGetCurrentIrpStackLocation(Irp);
4352 DeviceToMount = IrpSp->Parameters.MountVolume.DeviceObject;
4353
4354 real_devext = IrpSp->Parameters.MountVolume.Vpb->RealDevice->DeviceExtension;
4355
4356 // Make sure we're not trying to mount the PDO
4357 if (IrpSp->Parameters.MountVolume.Vpb->RealDevice->DriverObject == drvobj && real_devext->type == VCB_TYPE_PDO)
4358 return STATUS_UNRECOGNIZED_VOLUME;
4359
4360 if (!is_btrfs_volume(DeviceToMount)) {
4361 bool not_pnp = false;
4362
4363 Status = check_mount_device(DeviceToMount, &not_pnp);
4364 if (!NT_SUCCESS(Status))
4365 WARN("check_mount_device returned %08lx\n", Status);
4366
4367 if (!not_pnp) {
4368 Status = STATUS_UNRECOGNIZED_VOLUME;
4369 goto exit2;
4370 }
4371 } else {
4372 PDEVICE_OBJECT pdo;
4373
4374 pdo = DeviceToMount;
4375
4376 ObReferenceObject(pdo);
4377
4378 while (true) {
4379 PDEVICE_OBJECT pdo2 = IoGetLowerDeviceObject(pdo);
4380
4381 ObDereferenceObject(pdo);
4382
4383 if (!pdo2)
4384 break;
4385 else
4386 pdo = pdo2;
4387 }
4388
4389 ExAcquireResourceSharedLite(&pdo_list_lock, true);
4390
4391 le = pdo_list.Flink;
4392 while (le != &pdo_list) {
4393 pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
4394
4395 if (pdode2->pdo == pdo) {
4396 vde = pdode2->vde;
4397 break;
4398 }
4399
4400 le = le->Flink;
4401 }
4402
4403 ExReleaseResourceLite(&pdo_list_lock);
4404
4405 if (!vde || vde->type != VCB_TYPE_VOLUME) {
4406 vde = NULL;
4407 Status = STATUS_UNRECOGNIZED_VOLUME;
4408 goto exit2;
4409 }
4410 }
4411
4412 if (vde) {
4413 pdode = vde->pdode;
4414
4415 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
4416
4417 le = pdode->children.Flink;
4418 while (le != &pdode->children) {
4419 LIST_ENTRY* le2 = le->Flink;
4420
4421 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
4422
4423 if (!still_has_superblock(vc->devobj, vc->fileobj)) {
4424 remove_volume_child(vde, vc, false);
4425
4426 if (pdode->num_children == 0) {
4427 ERR("error - number of devices is zero\n");
4428 Status = STATUS_INTERNAL_ERROR;
4429 goto exit2;
4430 }
4431
4432 Status = STATUS_DEVICE_NOT_READY;
4433 goto exit2;
4434 }
4435
4436 le = le2;
4437 }
4438
4439 if (pdode->num_children == 0 || pdode->children_loaded == 0) {
4440 ERR("error - number of devices is zero\n");
4441 Status = STATUS_INTERNAL_ERROR;
4442 goto exit;
4443 }
4444
4445 ExConvertExclusiveToSharedLite(&pdode->child_lock);
4446
4447 vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
4448
4449 readobj = vc->devobj;
4450 fileobj = vc->fileobj;
4451 readobjsize = vc->size;
4452
4453 vde->device->Characteristics &= ~FILE_DEVICE_SECURE_OPEN;
4454 } else {
4455 GET_LENGTH_INFORMATION gli;
4456
4457 vc = NULL;
4458 readobj = DeviceToMount;
4459 fileobj = NULL;
4460
4461 Status = dev_ioctl(readobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
4462 &gli, sizeof(gli), true, NULL);
4463
4464 if (!NT_SUCCESS(Status)) {
4465 ERR("error reading length information: %08lx\n", Status);
4466 goto exit;
4467 }
4468
4469 readobjsize = gli.Length.QuadPart;
4470 }
4471
4472 Status = IoCreateDevice(drvobj, sizeof(device_extension), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, false, &NewDeviceObject);
4473 if (!NT_SUCCESS(Status)) {
4474 ERR("IoCreateDevice returned %08lx\n", Status);
4475 Status = STATUS_UNRECOGNIZED_VOLUME;
4476 goto exit;
4477 }
4478
4479 NewDeviceObject->Flags |= DO_DIRECT_IO;
4480
4481 // Some programs seem to expect that the sector size will be 512, for
4482 // FILE_NO_INTERMEDIATE_BUFFERING and the like.
4483 NewDeviceObject->SectorSize = min(DeviceToMount->SectorSize, 512);
4484
4485 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
4486 RtlZeroMemory(Vcb, sizeof(device_extension));
4487 Vcb->type = VCB_TYPE_FS;
4488 Vcb->vde = vde;
4489
4490 ExInitializeResourceLite(&Vcb->tree_lock);
4491 Vcb->need_write = false;
4492
4493 ExInitializeResourceLite(&Vcb->fcb_lock);
4494 ExInitializeResourceLite(&Vcb->fileref_lock);
4495 ExInitializeResourceLite(&Vcb->chunk_lock);
4496 ExInitializeResourceLite(&Vcb->dirty_fcbs_lock);
4497 ExInitializeResourceLite(&Vcb->dirty_filerefs_lock);
4498 ExInitializeResourceLite(&Vcb->dirty_subvols_lock);
4499 ExInitializeResourceLite(&Vcb->scrub.stats_lock);
4500
4501 ExInitializeResourceLite(&Vcb->load_lock);
4502 ExAcquireResourceExclusiveLite(&Vcb->load_lock, true);
4503
4504 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
4505
4506 DeviceToMount->Flags |= DO_DIRECT_IO;
4507
4508 Status = read_superblock(Vcb, readobj, fileobj, readobjsize);
4509 if (!NT_SUCCESS(Status)) {
4510 if (!IoIsErrorUserInduced(Status))
4511 Status = STATUS_UNRECOGNIZED_VOLUME;
4512 else if (Irp->Tail.Overlay.Thread)
4513 IoSetHardErrorOrVerifyDevice(Irp, readobj);
4514
4515 goto exit;
4516 }
4517
4518 if (!vde && Vcb->superblock.num_devices > 1) {
4519 ERR("cannot mount multi-device FS with non-PNP device\n");
4520 Status = STATUS_UNRECOGNIZED_VOLUME;
4521 goto exit;
4522 }
4523
4524 Status = registry_load_volume_options(Vcb);
4525 if (!NT_SUCCESS(Status)) {
4526 ERR("registry_load_volume_options returned %08lx\n", Status);
4527 goto exit;
4528 }
4529
4530 if (pdode && RtlCompareMemory(&boot_uuid, &pdode->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && boot_subvol != 0)
4531 Vcb->options.subvol_id = boot_subvol;
4532
4533 if (pdode && pdode->children_loaded < pdode->num_children && (!Vcb->options.allow_degraded || !finished_probing || degraded_wait)) {
4534 ERR("could not mount as %I64u device(s) missing\n", pdode->num_children - pdode->children_loaded);
4535 Status = STATUS_DEVICE_NOT_READY;
4536 goto exit;
4537 }
4538
4539 if (Vcb->options.ignore) {
4540 TRACE("ignoring volume\n");
4541 Status = STATUS_UNRECOGNIZED_VOLUME;
4542 goto exit;
4543 }
4544
4545 if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
4546 WARN("cannot mount because of unsupported incompat flags (%I64x)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
4547 Status = STATUS_UNRECOGNIZED_VOLUME;
4548 goto exit;
4549 }
4550
4551 if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_METADATA_UUID))
4552 Vcb->superblock.metadata_uuid = Vcb->superblock.uuid;
4553
4554 Vcb->readonly = false;
4555 if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
4556 WARN("mounting read-only because of unsupported flags (%I64x)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
4557 Vcb->readonly = true;
4558 }
4559
4560 if (Vcb->options.readonly)
4561 Vcb->readonly = true;
4562
4563 Vcb->superblock.generation++;
4564 Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
4565
4566 if (Vcb->superblock.log_tree_addr != 0) {
4567 FIXME("FIXME - replay transaction log (clearing for now)\n");
4568 Vcb->superblock.log_tree_addr = 0;
4569 }
4570
4571 switch (Vcb->superblock.csum_type) {
4572 case CSUM_TYPE_CRC32C:
4573 Vcb->csum_size = sizeof(uint32_t);
4574 break;
4575
4576 case CSUM_TYPE_XXHASH:
4577 Vcb->csum_size = sizeof(uint64_t);
4578 break;
4579
4580 case CSUM_TYPE_SHA256:
4581 Vcb->csum_size = SHA256_HASH_SIZE;
4582 break;
4583
4584 case CSUM_TYPE_BLAKE2:
4585 Vcb->csum_size = BLAKE2_HASH_SIZE;
4586 break;
4587
4588 default:
4589 ERR("unrecognized csum type %x\n", Vcb->superblock.csum_type);
4590 break;
4591 }
4592
4593 InitializeListHead(&Vcb->devices);
4594 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
4595 if (!dev) {
4596 ERR("out of memory\n");
4597 Status = STATUS_INSUFFICIENT_RESOURCES;
4598 goto exit;
4599 }
4600
4601 dev->devobj = readobj;
4602 dev->fileobj = fileobj;
4603 RtlCopyMemory(&dev->devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
4604
4605 if (dev->devitem.num_bytes > readobjsize) {
4606 WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", dev->devitem.dev_id,
4607 dev->devitem.num_bytes, readobjsize);
4608
4609 dev->devitem.num_bytes = readobjsize;
4610 }
4611
4612 dev->seeding = Vcb->superblock.flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? true : false;
4613
4614 init_device(Vcb, dev, true);
4615
4616 InsertTailList(&Vcb->devices, &dev->list_entry);
4617 Vcb->devices_loaded = 1;
4618
4619 if (DeviceToMount->Flags & DO_SYSTEM_BOOT_PARTITION)
4620 Vcb->disallow_dismount = true;
4621
4622 TRACE("DeviceToMount = %p\n", DeviceToMount);
4623 TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp->Parameters.MountVolume.Vpb);
4624
4625 NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
4626 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
4627
4628 InitializeListHead(&Vcb->roots);
4629 InitializeListHead(&Vcb->drop_roots);
4630
4631 Vcb->log_to_phys_loaded = false;
4632
4633 add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, Vcb->superblock.chunk_root_generation, NULL);
4634
4635 if (!Vcb->chunk_root) {
4636 ERR("Could not load chunk root.\n");
4637 Status = STATUS_INTERNAL_ERROR;
4638 goto exit;
4639 }
4640
4641 InitializeListHead(&Vcb->sys_chunks);
4642 Status = load_sys_chunks(Vcb);
4643 if (!NT_SUCCESS(Status)) {
4644 ERR("load_sys_chunks returned %08lx\n", Status);
4645 goto exit;
4646 }
4647
4648 InitializeListHead(&Vcb->chunks);
4649 InitializeListHead(&Vcb->trees);
4650 InitializeListHead(&Vcb->trees_hash);
4651 InitializeListHead(&Vcb->all_fcbs);
4652 InitializeListHead(&Vcb->dirty_fcbs);
4653 InitializeListHead(&Vcb->dirty_filerefs);
4654 InitializeListHead(&Vcb->dirty_subvols);
4655 InitializeListHead(&Vcb->send_ops);
4656
4657 ExInitializeFastMutex(&Vcb->trees_list_mutex);
4658
4659 InitializeListHead(&Vcb->DirNotifyList);
4660 InitializeListHead(&Vcb->scrub.errors);
4661
4662 FsRtlNotifyInitializeSync(&Vcb->NotifySync);
4663
4664 ExInitializePagedLookasideList(&Vcb->tree_data_lookaside, NULL, NULL, 0, sizeof(tree_data), ALLOC_TAG, 0);
4665 ExInitializePagedLookasideList(&Vcb->traverse_ptr_lookaside, NULL, NULL, 0, sizeof(traverse_ptr), ALLOC_TAG, 0);
4666 ExInitializePagedLookasideList(&Vcb->batch_item_lookaside, NULL, NULL, 0, sizeof(batch_item), ALLOC_TAG, 0);
4667 ExInitializePagedLookasideList(&Vcb->fileref_lookaside, NULL, NULL, 0, sizeof(file_ref), ALLOC_TAG, 0);
4668 ExInitializePagedLookasideList(&Vcb->fcb_lookaside, NULL, NULL, 0, sizeof(fcb), ALLOC_TAG, 0);
4669 ExInitializePagedLookasideList(&Vcb->name_bit_lookaside, NULL, NULL, 0, sizeof(name_bit), ALLOC_TAG, 0);
4670 ExInitializeNPagedLookasideList(&Vcb->range_lock_lookaside, NULL, NULL, 0, sizeof(range_lock), ALLOC_TAG, 0);
4671 ExInitializeNPagedLookasideList(&Vcb->fileref_np_lookaside, NULL, NULL, 0, sizeof(file_ref_nonpaged), ALLOC_TAG, 0);
4672 ExInitializeNPagedLookasideList(&Vcb->fcb_np_lookaside, NULL, NULL, 0, sizeof(fcb_nonpaged), ALLOC_TAG, 0);
4673 init_lookaside = true;
4674
4675 Vcb->Vpb = IrpSp->Parameters.MountVolume.Vpb;
4676
4677 Status = load_chunk_root(Vcb, Irp);
4678 if (!NT_SUCCESS(Status)) {
4679 ERR("load_chunk_root returned %08lx\n", Status);
4680 goto exit;
4681 }
4682
4683 if (Vcb->superblock.num_devices > 1) {
4684 if (Vcb->devices_loaded < Vcb->superblock.num_devices && (!Vcb->options.allow_degraded || !finished_probing)) {
4685 ERR("could not mount as %I64u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
4686
4687 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
4688
4689 Status = STATUS_INTERNAL_ERROR;
4690 goto exit;
4691 }
4692
4693 if (dev->readonly && !Vcb->readonly) {
4694 Vcb->readonly = true;
4695
4696 le = Vcb->devices.Flink;
4697 while (le != &Vcb->devices) {
4698 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4699
4700 if (dev2->readonly && !dev2->seeding)
4701 break;
4702
4703 if (!dev2->readonly) {
4704 Vcb->readonly = false;
4705 break;
4706 }
4707
4708 le = le->Flink;
4709 }
4710
4711 if (Vcb->readonly)
4712 WARN("setting volume to readonly\n");
4713 }
4714 } else {
4715 if (dev->readonly) {
4716 WARN("setting volume to readonly as device is readonly\n");
4717 Vcb->readonly = true;
4718 }
4719 }
4720
4721 add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, Vcb->superblock.generation - 1, NULL);
4722
4723 if (!Vcb->root_root) {
4724 ERR("Could not load root of roots.\n");
4725 Status = STATUS_INTERNAL_ERROR;
4726 goto exit;
4727 }
4728
4729 Status = look_for_roots(Vcb, Irp);
4730 if (!NT_SUCCESS(Status)) {
4731 ERR("look_for_roots returned %08lx\n", Status);
4732 goto exit;
4733 }
4734
4735 if (!Vcb->readonly) {
4736 Status = find_chunk_usage(Vcb, Irp);
4737 if (!NT_SUCCESS(Status)) {
4738 ERR("find_chunk_usage returned %08lx\n", Status);
4739 goto exit;
4740 }
4741 }
4742
4743 InitializeListHead(&batchlist);
4744
4745 // We've already increased the generation by one
4746 if (!Vcb->readonly && (
4747 Vcb->options.clear_cache ||
4748 (!(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE) && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) ||
4749 (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)))) {
4750 if (Vcb->options.clear_cache)
4751 WARN("ClearCache option was set, clearing cache...\n");
4752 else if (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID))
4753 WARN("clearing free-space tree created by buggy Linux driver\n");
4754 else
4755 WARN("generation was %I64x, free-space cache generation was %I64x; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
4756
4757 Status = clear_free_space_cache(Vcb, &batchlist, Irp);
4758 if (!NT_SUCCESS(Status)) {
4759 ERR("clear_free_space_cache returned %08lx\n", Status);
4760 clear_batch_list(Vcb, &batchlist);
4761 goto exit;
4762 }
4763 }
4764
4765 Status = commit_batch_list(Vcb, &batchlist, Irp);
4766 if (!NT_SUCCESS(Status)) {
4767 ERR("commit_batch_list returned %08lx\n", Status);
4768 goto exit;
4769 }
4770
4771 Vcb->volume_fcb = create_fcb(Vcb, NonPagedPool);
4772 if (!Vcb->volume_fcb) {
4773 ERR("out of memory\n");
4774 Status = STATUS_INSUFFICIENT_RESOURCES;
4775 goto exit;
4776 }
4777
4778 Vcb->volume_fcb->Vcb = Vcb;
4779 Vcb->volume_fcb->sd = NULL;
4780
4781 Vcb->dummy_fcb = create_fcb(Vcb, NonPagedPool);
4782 if (!Vcb->dummy_fcb) {
4783 ERR("out of memory\n");
4784 Status = STATUS_INSUFFICIENT_RESOURCES;
4785 goto exit;
4786 }
4787
4788 Vcb->dummy_fcb->Vcb = Vcb;
4789 Vcb->dummy_fcb->type = BTRFS_TYPE_DIRECTORY;
4790 Vcb->dummy_fcb->inode = 2;
4791 Vcb->dummy_fcb->subvol = Vcb->root_root;
4792 Vcb->dummy_fcb->atts = FILE_ATTRIBUTE_DIRECTORY;
4793 Vcb->dummy_fcb->inode_item.st_nlink = 1;
4794 Vcb->dummy_fcb->inode_item.st_mode = __S_IFDIR;
4795
4796 Vcb->dummy_fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4797 if (!Vcb->dummy_fcb->hash_ptrs) {
4798 ERR("out of memory\n");
4799 Status = STATUS_INSUFFICIENT_RESOURCES;
4800 goto exit;
4801 }
4802
4803 RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
4804
4805 Vcb->dummy_fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
4806 if (!Vcb->dummy_fcb->hash_ptrs_uc) {
4807 ERR("out of memory\n");
4808 Status = STATUS_INSUFFICIENT_RESOURCES;
4809 goto exit;
4810 }
4811
4812 RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
4813
4814 root_fcb = create_fcb(Vcb, NonPagedPool);
4815 if (!root_fcb) {
4816 ERR("out of memory\n");
4817 Status = STATUS_INSUFFICIENT_RESOURCES;
4818 goto exit;
4819 }
4820
4821 root_fcb->Vcb = Vcb;
4822 root_fcb->inode = SUBVOL_ROOT_INODE;
4823 root_fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&root_fcb->inode, sizeof(uint64_t));
4824 root_fcb->type = BTRFS_TYPE_DIRECTORY;
4825
4826 #ifdef DEBUG_FCB_REFCOUNTS
4827 WARN("volume FCB = %p\n", Vcb->volume_fcb);
4828 WARN("root FCB = %p\n", root_fcb);
4829 #endif
4830
4831 root_fcb->subvol = find_default_subvol(Vcb, Irp);
4832
4833 if (!root_fcb->subvol) {
4834 ERR("could not find top subvol\n");
4835 Status = STATUS_INTERNAL_ERROR;
4836 goto exit;
4837 }
4838
4839 Status = load_dir_children(Vcb, root_fcb, true, Irp);
4840 if (!NT_SUCCESS(Status)) {
4841 ERR("load_dir_children returned %08lx\n", Status);
4842 goto exit;
4843 }
4844
4845 searchkey.obj_id = root_fcb->inode;
4846 searchkey.obj_type = TYPE_INODE_ITEM;
4847 searchkey.offset = 0xffffffffffffffff;
4848
4849 Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, false, Irp);
4850 if (!NT_SUCCESS(Status)) {
4851 ERR("error - find_item returned %08lx\n", Status);
4852 goto exit;
4853 }
4854
4855 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
4856 ERR("couldn't find INODE_ITEM for root directory\n");
4857 Status = STATUS_INTERNAL_ERROR;
4858 goto exit;
4859 }
4860
4861 if (tp.item->size > 0)
4862 RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
4863
4864 fcb_get_sd(root_fcb, NULL, true, Irp);
4865
4866 root_fcb->atts = get_file_attributes(Vcb, root_fcb->subvol, root_fcb->inode, root_fcb->type, false, false, Irp);
4867
4868 if (root_fcb->subvol->id == BTRFS_ROOT_FSTREE)
4869 root_fcb->atts &= ~FILE_ATTRIBUTE_HIDDEN;
4870
4871 Vcb->root_fileref = create_fileref(Vcb);
4872 if (!Vcb->root_fileref) {
4873 ERR("out of memory\n");
4874 Status = STATUS_INSUFFICIENT_RESOURCES;
4875 goto exit;
4876 }
4877
4878 Vcb->root_fileref->fcb = root_fcb;
4879 InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
4880 InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
4881
4882 root_fcb->subvol->fcbs_ptrs[root_fcb->hash >> 24] = &root_fcb->list_entry;
4883
4884 root_fcb->fileref = Vcb->root_fileref;
4885
4886 root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
4887 if (!root_ccb) {
4888 ERR("out of memory\n");
4889 Status = STATUS_INSUFFICIENT_RESOURCES;
4890 goto exit;
4891 }
4892
4893 Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
4894 Vcb->root_file->FsContext = root_fcb;
4895 Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
4896 Vcb->root_file->Vpb = DeviceObject->Vpb;
4897
4898 RtlZeroMemory(root_ccb, sizeof(ccb));
4899 root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
4900 root_ccb->NodeSize = sizeof(ccb);
4901
4902 Vcb->root_file->FsContext2 = root_ccb;
4903
4904 _SEH2_TRY {
4905 CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), false, &cache_callbacks, Vcb->root_file);
4906 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4907 Status = _SEH2_GetExceptionCode();
4908 goto exit;
4909 } _SEH2_END;
4910
4911 le = Vcb->devices.Flink;
4912 while (le != &Vcb->devices) {
4913 device* dev2 = CONTAINING_RECORD(le, device, list_entry);
4914
4915 Status = find_disk_holes(Vcb, dev2, Irp);
4916 if (!NT_SUCCESS(Status)) {
4917 ERR("find_disk_holes returned %08lx\n", Status);
4918 goto exit;
4919 }
4920
4921 le = le->Flink;
4922 }
4923
4924 IoAcquireVpbSpinLock(&irql);
4925
4926 NewDeviceObject->Vpb = IrpSp->Parameters.MountVolume.Vpb;
4927 IrpSp->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
4928 IrpSp->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
4929 NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
4930 NewDeviceObject->Vpb->VolumeLabel[0] = '?';
4931 NewDeviceObject->Vpb->VolumeLabel[1] = 0;
4932 NewDeviceObject->Vpb->ReferenceCount++;
4933
4934 IoReleaseVpbSpinLock(irql);
4935
4936 KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, false);
4937
4938 InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
4939
4940 Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, &oa, NULL, NULL, flush_thread, NewDeviceObject);
4941 if (!NT_SUCCESS(Status)) {
4942 ERR("PsCreateSystemThread returned %08lx\n", Status);
4943 goto exit;
4944 }
4945
4946 Status = create_calc_threads(NewDeviceObject);
4947 if (!NT_SUCCESS(Status)) {
4948 ERR("create_calc_threads returned %08lx\n", Status);
4949 goto exit;
4950 }
4951
4952 Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
4953 if (!NT_SUCCESS(Status))
4954 WARN("registry_mark_volume_mounted returned %08lx\n", Status);
4955
4956 Status = look_for_balance_item(Vcb);
4957 if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
4958 WARN("look_for_balance_item returned %08lx\n", Status);
4959
4960 Status = STATUS_SUCCESS;
4961
4962 if (vde)
4963 vde->mounted_device = NewDeviceObject;
4964
4965 Vcb->devobj = NewDeviceObject;
4966
4967 ExInitializeResourceLite(&Vcb->send_load_lock);
4968
4969 exit:
4970 if (pdode)
4971 ExReleaseResourceLite(&pdode->child_lock);
4972
4973 exit2:
4974 if (Vcb) {
4975 ExReleaseResourceLite(&Vcb->tree_lock);
4976 ExReleaseResourceLite(&Vcb->load_lock);
4977 }
4978
4979 if (!NT_SUCCESS(Status)) {
4980 if (Vcb) {
4981 if (init_lookaside) {
4982 ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
4983 ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
4984 ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
4985 ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
4986 ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
4987 ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
4988 ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
4989 ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
4990 ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
4991 }
4992
4993 if (Vcb->root_file)
4994 ObDereferenceObject(Vcb->root_file);
4995 else if (Vcb->root_fileref)
4996 free_fileref(Vcb->root_fileref);
4997 else if (root_fcb)
4998 free_fcb(root_fcb);
4999
5000 if (root_fcb && root_fcb->refcount == 0)
5001 reap_fcb(root_fcb);
5002
5003 if (Vcb->volume_fcb)
5004 reap_fcb(Vcb->volume_fcb);
5005
5006 ExDeleteResourceLite(&Vcb->tree_lock);
5007 ExDeleteResourceLite(&Vcb->load_lock);
5008 ExDeleteResourceLite(&Vcb->fcb_lock);
5009 ExDeleteResourceLite(&Vcb->fileref_lock);
5010 ExDeleteResourceLite(&Vcb->chunk_lock);
5011 ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
5012 ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
5013 ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
5014 ExDeleteResourceLite(&Vcb->scrub.stats_lock);
5015
5016 if (Vcb->devices.Flink) {
5017 while (!IsListEmpty(&Vcb->devices)) {
5018 device* dev2 = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
5019
5020 ExFreePool(dev2);
5021 }
5022 }
5023 }
5024
5025 if (NewDeviceObject)
5026 IoDeleteDevice(NewDeviceObject);
5027 } else {
5028 ExAcquireResourceExclusiveLite(&global_loading_lock, true);
5029 InsertTailList(&VcbList, &Vcb->list_entry);
5030 ExReleaseResourceLite(&global_loading_lock);
5031
5032 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
5033 }
5034
5035 TRACE("mount_vol done (status: %lx)\n", Status);
5036
5037 return Status;
5038 }
5039
5040 static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
5041 NTSTATUS Status;
5042 superblock* sb;
5043 ULONG to_read, cc;
5044
5045 if (!dev->devobj)
5046 return STATUS_WRONG_VOLUME;
5047
5048 if (dev->removable) {
5049 IO_STATUS_BLOCK iosb;
5050
5051 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
5052
5053 if (IoIsErrorUserInduced(Status)) {
5054 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx (user-induced)\n", Status);
5055
5056 if (Vcb->vde) {
5057 pdo_device_extension* pdode = Vcb->vde->pdode;
5058 LIST_ENTRY* le2;
5059 bool changed = false;
5060
5061 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
5062
5063 le2 = pdode->children.Flink;
5064 while (le2 != &pdode->children) {
5065 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
5066
5067 if (vc->devobj == dev->devobj) {
5068 TRACE("removing device\n");
5069
5070 remove_volume_child(Vcb->vde, vc, true);
5071 changed = true;
5072
5073 break;
5074 }
5075
5076 le2 = le2->Flink;
5077 }
5078
5079 if (!changed)
5080 ExReleaseResourceLite(&pdode->child_lock);
5081 }
5082 } else if (!NT_SUCCESS(Status)) {
5083 ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx\n", Status);
5084 return Status;
5085 } else if (iosb.Information < sizeof(ULONG)) {
5086 ERR("iosb.Information was too short\n");
5087 return STATUS_INTERNAL_ERROR;
5088 }
5089
5090 dev->change_count = cc;
5091 }
5092
5093 to_read = dev->devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), dev->devobj->SectorSize);
5094
5095 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
5096 if (!sb) {
5097 ERR("out of memory\n");
5098 return STATUS_INSUFFICIENT_RESOURCES;
5099 }
5100
5101 Status = sync_read_phys(dev->devobj, dev->fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, true);
5102 if (!NT_SUCCESS(Status)) {
5103 ERR("Failed to read superblock: %08lx\n", Status);
5104 ExFreePool(sb);
5105 return Status;
5106 }
5107
5108 if (sb->magic != BTRFS_MAGIC) {
5109 ERR("not a BTRFS volume\n");
5110 ExFreePool(sb);
5111 return STATUS_WRONG_VOLUME;
5112 }
5113
5114 if (!check_superblock_checksum(sb)) {
5115 ExFreePool(sb);
5116 return STATUS_WRONG_VOLUME;
5117 }
5118
5119 if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
5120 ERR("different UUIDs\n");
5121 ExFreePool(sb);
5122 return STATUS_WRONG_VOLUME;
5123 }
5124
5125 ExFreePool(sb);
5126
5127 dev->devobj->Flags &= ~DO_VERIFY_VOLUME;
5128
5129 return STATUS_SUCCESS;
5130 }
5131
5132 static NTSTATUS verify_volume(_In_ PDEVICE_OBJECT devobj) {
5133 device_extension* Vcb = devobj->DeviceExtension;
5134 NTSTATUS Status;
5135 LIST_ENTRY* le;
5136 uint64_t failed_devices = 0;
5137 bool locked = false, remove = false;
5138
5139 if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
5140 return STATUS_WRONG_VOLUME;
5141
5142 if (!ExIsResourceAcquiredExclusive(&Vcb->tree_lock)) {
5143 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
5144 locked = true;
5145 }
5146
5147 if (Vcb->removing) {
5148 if (locked) ExReleaseResourceLite(&Vcb->tree_lock);
5149 return STATUS_WRONG_VOLUME;
5150 }
5151
5152 InterlockedIncrement(&Vcb->open_files); // so pnp_surprise_removal doesn't uninit the device while we're still using it
5153
5154 le = Vcb->devices.Flink;
5155 while (le != &Vcb->devices) {
5156 device* dev = CONTAINING_RECORD(le, device, list_entry);
5157
5158 Status = verify_device(Vcb, dev);
5159 if (!NT_SUCCESS(Status)) {
5160 failed_devices++;
5161
5162 if (dev->devobj && Vcb->options.allow_degraded)
5163 dev->devobj = NULL;
5164 }
5165
5166 le = le->Flink;
5167 }
5168
5169 InterlockedDecrement(&Vcb->open_files);
5170
5171 if (Vcb->removing && Vcb->open_files == 0)
5172 remove = true;
5173
5174 if (locked)
5175 ExReleaseResourceLite(&Vcb->tree_lock);
5176
5177 if (remove) {
5178 uninit(Vcb);
5179 return Status;
5180 }
5181
5182 if (failed_devices == 0 || (Vcb->options.allow_degraded && failed_devices < Vcb->superblock.num_devices)) {
5183 Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
5184
5185 return STATUS_SUCCESS;
5186 }
5187
5188 return Status;
5189 }
5190
5191 _Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL)
5192 _Function_class_(DRIVER_DISPATCH)
5193 static NTSTATUS __stdcall drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5194 PIO_STACK_LOCATION IrpSp;
5195 NTSTATUS Status;
5196 device_extension* Vcb = DeviceObject->DeviceExtension;
5197 bool top_level;
5198
5199 FsRtlEnterFileSystem();
5200
5201 TRACE("file system control\n");
5202
5203 top_level = is_top_level(Irp);
5204
5205 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5206 Status = vol_file_system_control(DeviceObject, Irp);
5207 goto end;
5208 } else if (!Vcb || (Vcb->type != VCB_TYPE_FS && Vcb->type != VCB_TYPE_CONTROL)) {
5209 Status = STATUS_INVALID_PARAMETER;
5210 goto end;
5211 }
5212
5213 Status = STATUS_NOT_IMPLEMENTED;
5214
5215 IrpSp = IoGetCurrentIrpStackLocation( Irp );
5216
5217 Irp->IoStatus.Information = 0;
5218
5219 switch (IrpSp->MinorFunction) {
5220 case IRP_MN_MOUNT_VOLUME:
5221 TRACE("IRP_MN_MOUNT_VOLUME\n");
5222
5223 Status = mount_vol(DeviceObject, Irp);
5224 break;
5225
5226 case IRP_MN_KERNEL_CALL:
5227 TRACE("IRP_MN_KERNEL_CALL\n");
5228
5229 Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
5230 break;
5231
5232 case IRP_MN_USER_FS_REQUEST:
5233 TRACE("IRP_MN_USER_FS_REQUEST\n");
5234
5235 Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
5236 break;
5237
5238 case IRP_MN_VERIFY_VOLUME:
5239 TRACE("IRP_MN_VERIFY_VOLUME\n");
5240
5241 Status = verify_volume(DeviceObject);
5242
5243 if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
5244 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
5245 Vcb->removing = true;
5246 ExReleaseResourceLite(&Vcb->tree_lock);
5247 }
5248
5249 break;
5250
5251 default:
5252 break;
5253 }
5254
5255 end:
5256 TRACE("returning %08lx\n", Status);
5257
5258 if (Irp) {
5259 Irp->IoStatus.Status = Status;
5260
5261 IoCompleteRequest(Irp, IO_NO_INCREMENT);
5262 }
5263
5264 if (top_level)
5265 IoSetTopLevelIrp(NULL);
5266
5267 FsRtlExitFileSystem();
5268
5269 return Status;
5270 }
5271
5272 _Dispatch_type_(IRP_MJ_LOCK_CONTROL)
5273 _Function_class_(DRIVER_DISPATCH)
5274 static NTSTATUS __stdcall drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5275 NTSTATUS Status;
5276 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5277 fcb* fcb = IrpSp->FileObject ? IrpSp->FileObject->FsContext : NULL;
5278 device_extension* Vcb = DeviceObject->DeviceExtension;
5279 bool top_level;
5280
5281 FsRtlEnterFileSystem();
5282
5283 top_level = is_top_level(Irp);
5284
5285 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5286 Status = vol_lock_control(DeviceObject, Irp);
5287
5288 Irp->IoStatus.Status = Status;
5289 IoCompleteRequest(Irp, IO_NO_INCREMENT);
5290
5291 goto exit;
5292 }
5293
5294 TRACE("lock control\n");
5295
5296 if (!fcb) {
5297 ERR("fcb was NULL\n");
5298 Status = STATUS_INVALID_PARAMETER;
5299 goto exit;
5300 }
5301
5302 FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL);
5303
5304 Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
5305
5306 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
5307
5308 exit:
5309 TRACE("returning %08lx\n", Status);
5310
5311 if (top_level)
5312 IoSetTopLevelIrp(NULL);
5313
5314 FsRtlExitFileSystem();
5315
5316 return Status;
5317 }
5318
5319 void do_shutdown(PIRP Irp) {
5320 LIST_ENTRY* le;
5321 bus_device_extension* bde;
5322
5323 shutting_down = true;
5324 KeSetEvent(&mountmgr_thread_event, 0, false);
5325
5326 le = VcbList.Flink;
5327 while (le != &VcbList) {
5328 LIST_ENTRY* le2 = le->Flink;
5329
5330 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
5331 volume_device_extension* vde = Vcb->vde;
5332 PDEVICE_OBJECT devobj = vde ? vde->device : NULL;
5333
5334 TRACE("shutting down Vcb %p\n", Vcb);
5335
5336 if (vde)
5337 InterlockedIncrement(&vde->open_count);
5338
5339 if (devobj)
5340 ObReferenceObject(devobj);
5341
5342 dismount_volume(Vcb, true, Irp);
5343
5344 if (vde) {
5345 NTSTATUS Status;
5346 UNICODE_STRING mmdevpath;
5347 PDEVICE_OBJECT mountmgr;
5348 PFILE_OBJECT mountmgrfo;
5349 KIRQL irql;
5350 PVPB newvpb;
5351
5352 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
5353 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
5354 if (!NT_SUCCESS(Status))
5355 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
5356 else {
5357 remove_drive_letter(mountmgr, &vde->name);
5358
5359 ObDereferenceObject(mountmgrfo);
5360 }
5361
5362 vde->removing = true;
5363
5364 newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
5365 if (!newvpb) {
5366 ERR("out of memory\n");
5367 return;
5368 }
5369
5370 RtlZeroMemory(newvpb, sizeof(VPB));
5371
5372 newvpb->Type = IO_TYPE_VPB;
5373 newvpb->Size = sizeof(VPB);
5374 newvpb->RealDevice = newvpb->DeviceObject = vde->device;
5375 newvpb->Flags = VPB_DIRECT_WRITES_ALLOWED;
5376
5377 IoAcquireVpbSpinLock(&irql);
5378 vde->device->Vpb = newvpb;
5379 IoReleaseVpbSpinLock(irql);
5380
5381 if (InterlockedDecrement(&vde->open_count) == 0)
5382 free_vol(vde);
5383 }
5384
5385 if (devobj)
5386 ObDereferenceObject(devobj);
5387
5388 le = le2;
5389 }
5390
5391 #ifdef _DEBUG
5392 if (comfo) {
5393 ObDereferenceObject(comfo);
5394 comdo = NULL;
5395 comfo = NULL;
5396 }
5397 #endif
5398
5399 IoUnregisterFileSystem(master_devobj);
5400
5401 if (notification_entry2) {
5402 if (fIoUnregisterPlugPlayNotificationEx)
5403 fIoUnregisterPlugPlayNotificationEx(notification_entry2);
5404 else
5405 IoUnregisterPlugPlayNotification(notification_entry2);
5406
5407 notification_entry2 = NULL;
5408 }
5409
5410 if (notification_entry3) {
5411 if (fIoUnregisterPlugPlayNotificationEx)
5412 fIoUnregisterPlugPlayNotificationEx(notification_entry3);
5413 else
5414 IoUnregisterPlugPlayNotification(notification_entry3);
5415
5416 notification_entry3 = NULL;
5417 }
5418
5419 if (notification_entry) {
5420 if (fIoUnregisterPlugPlayNotificationEx)
5421 fIoUnregisterPlugPlayNotificationEx(notification_entry);
5422 else
5423 IoUnregisterPlugPlayNotification(notification_entry);
5424
5425 notification_entry = NULL;
5426 }
5427
5428 bde = busobj->DeviceExtension;
5429
5430 if (bde->attached_device)
5431 IoDetachDevice(bde->attached_device);
5432
5433 IoDeleteDevice(busobj);
5434 IoDeleteDevice(master_devobj);
5435 }
5436
5437 _Dispatch_type_(IRP_MJ_SHUTDOWN)
5438 _Function_class_(DRIVER_DISPATCH)
5439 static NTSTATUS __stdcall drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5440 NTSTATUS Status;
5441 bool top_level;
5442 device_extension* Vcb = DeviceObject->DeviceExtension;
5443
5444 FsRtlEnterFileSystem();
5445
5446 TRACE("shutdown\n");
5447
5448 top_level = is_top_level(Irp);
5449
5450 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5451 Status = vol_shutdown(DeviceObject, Irp);
5452 goto end;
5453 }
5454
5455 Status = STATUS_SUCCESS;
5456
5457 do_shutdown(Irp);
5458
5459 end:
5460 Irp->IoStatus.Status = Status;
5461 Irp->IoStatus.Information = 0;
5462
5463 IoCompleteRequest( Irp, IO_NO_INCREMENT );
5464
5465 if (top_level)
5466 IoSetTopLevelIrp(NULL);
5467
5468 FsRtlExitFileSystem();
5469
5470 return Status;
5471 }
5472
5473 static bool device_still_valid(device* dev, uint64_t expected_generation) {
5474 NTSTATUS Status;
5475 unsigned int to_read;
5476 superblock* sb;
5477
5478 to_read = (unsigned int)(dev->devobj->SectorSize == 0 ? sizeof(superblock) : sector_align(sizeof(superblock), dev->devobj->SectorSize));
5479
5480 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
5481 if (!sb) {
5482 ERR("out of memory\n");
5483 return false;
5484 }
5485
5486 Status = sync_read_phys(dev->devobj, dev->fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, false);
5487 if (!NT_SUCCESS(Status)) {
5488 ERR("sync_read_phys returned %08lx\n", Status);
5489 ExFreePool(sb);
5490 return false;
5491 }
5492
5493 if (sb->magic != BTRFS_MAGIC) {
5494 ERR("magic not found\n");
5495 ExFreePool(sb);
5496 return false;
5497 }
5498
5499 if (!check_superblock_checksum(sb)) {
5500 ExFreePool(sb);
5501 return false;
5502 }
5503
5504 if (sb->generation > expected_generation) {
5505 ERR("generation was %I64x, expected %I64x\n", sb->generation, expected_generation);
5506 ExFreePool(sb);
5507 return false;
5508 }
5509
5510 ExFreePool(sb);
5511
5512 return true;
5513 }
5514
5515 _Function_class_(IO_WORKITEM_ROUTINE)
5516 static void __stdcall check_after_wakeup(PDEVICE_OBJECT DeviceObject, PVOID con) {
5517 device_extension* Vcb = (device_extension*)con;
5518 LIST_ENTRY* le;
5519
5520 UNUSED(DeviceObject);
5521
5522 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
5523
5524 le = Vcb->devices.Flink;
5525
5526 // FIXME - do reads in parallel?
5527
5528 while (le != &Vcb->devices) {
5529 device* dev = CONTAINING_RECORD(le, device, list_entry);
5530
5531 if (dev->devobj) {
5532 if (!device_still_valid(dev, Vcb->superblock.generation - 1)) {
5533 PDEVICE_OBJECT voldev = Vcb->Vpb->RealDevice;
5534 KIRQL irql;
5535 PVPB newvpb;
5536
5537 WARN("forcing remount\n");
5538
5539 newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
5540 if (!newvpb) {
5541 ERR("out of memory\n");
5542 return;
5543 }
5544
5545 RtlZeroMemory(newvpb, sizeof(VPB));
5546
5547 newvpb->Type = IO_TYPE_VPB;
5548 newvpb->Size = sizeof(VPB);
5549 newvpb->RealDevice = voldev;
5550 newvpb->Flags = VPB_DIRECT_WRITES_ALLOWED;
5551
5552 Vcb->removing = true;
5553
5554 IoAcquireVpbSpinLock(&irql);
5555 voldev->Vpb = newvpb;
5556 IoReleaseVpbSpinLock(irql);
5557
5558 Vcb->vde = NULL;
5559
5560 ExReleaseResourceLite(&Vcb->tree_lock);
5561
5562 if (Vcb->open_files == 0)
5563 uninit(Vcb);
5564 else { // remove from VcbList
5565 ExAcquireResourceExclusiveLite(&global_loading_lock, true);
5566 RemoveEntryList(&Vcb->list_entry);
5567 Vcb->list_entry.Flink = NULL;
5568 ExReleaseResourceLite(&global_loading_lock);
5569 }
5570
5571 return;
5572 }
5573 }
5574
5575 le = le->Flink;
5576 }
5577
5578 ExReleaseResourceLite(&Vcb->tree_lock);
5579 }
5580
5581 _Dispatch_type_(IRP_MJ_POWER)
5582 _Function_class_(DRIVER_DISPATCH)
5583 static NTSTATUS __stdcall drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5584 NTSTATUS Status;
5585 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
5586 device_extension* Vcb = DeviceObject->DeviceExtension;
5587 bool top_level;
5588
5589 // no need for FsRtlEnterFileSystem, as this only ever gets called in a system thread
5590
5591 top_level = is_top_level(Irp);
5592
5593 Irp->IoStatus.Information = 0;
5594
5595 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5596 volume_device_extension* vde = DeviceObject->DeviceExtension;
5597
5598 if (IrpSp->MinorFunction == IRP_MN_QUERY_POWER && IrpSp->Parameters.Power.Type == SystemPowerState &&
5599 IrpSp->Parameters.Power.State.SystemState != PowerSystemWorking && vde->mounted_device) {
5600 device_extension* Vcb2 = vde->mounted_device->DeviceExtension;
5601
5602 /* If power state is about to go to sleep or hibernate, do a flush. We do this on IRP_MJ_QUERY_POWER
5603 * rather than IRP_MJ_SET_POWER because we know that the hard disks are still awake. */
5604
5605 if (Vcb2) {
5606 ExAcquireResourceExclusiveLite(&Vcb2->tree_lock, true);
5607
5608 if (Vcb2->need_write && !Vcb2->readonly) {
5609 TRACE("doing protective flush on power state change\n");
5610 Status = do_write(Vcb2, NULL);
5611 } else
5612 Status = STATUS_SUCCESS;
5613
5614 free_trees(Vcb2);
5615
5616 if (!NT_SUCCESS(Status))
5617 ERR("do_write returned %08lx\n", Status);
5618
5619 ExReleaseResourceLite(&Vcb2->tree_lock);
5620 }
5621 } else if (IrpSp->MinorFunction == IRP_MN_SET_POWER && IrpSp->Parameters.Power.Type == SystemPowerState &&
5622 IrpSp->Parameters.Power.State.SystemState == PowerSystemWorking && vde->mounted_device) {
5623 device_extension* Vcb2 = vde->mounted_device->DeviceExtension;
5624
5625 /* If waking up, make sure that the FS hasn't been changed while we've been out (e.g., by dual-boot Linux) */
5626
5627 if (Vcb2) {
5628 PIO_WORKITEM work_item;
5629
5630 work_item = IoAllocateWorkItem(DeviceObject);
5631 if (!work_item) {
5632 ERR("out of memory\n");
5633 } else
5634 IoQueueWorkItem(work_item, check_after_wakeup, DelayedWorkQueue, Vcb2);
5635 }
5636 }
5637
5638 PoStartNextPowerIrp(Irp);
5639 IoSkipCurrentIrpStackLocation(Irp);
5640 Status = PoCallDriver(vde->attached_device, Irp);
5641
5642 goto exit;
5643 } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
5644 IoSkipCurrentIrpStackLocation(Irp);
5645
5646 Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
5647
5648 goto exit;
5649 } else if (Vcb && Vcb->type == VCB_TYPE_BUS) {
5650 bus_device_extension* bde = DeviceObject->DeviceExtension;
5651
5652 PoStartNextPowerIrp(Irp);
5653 IoSkipCurrentIrpStackLocation(Irp);
5654 Status = PoCallDriver(bde->attached_device, Irp);
5655
5656 goto exit;
5657 }
5658
5659 if (IrpSp->MinorFunction == IRP_MN_SET_POWER || IrpSp->MinorFunction == IRP_MN_QUERY_POWER)
5660 Irp->IoStatus.Status = STATUS_SUCCESS;
5661
5662 Status = Irp->IoStatus.Status;
5663
5664 PoStartNextPowerIrp(Irp);
5665
5666 IoCompleteRequest(Irp, IO_NO_INCREMENT);
5667
5668 exit:
5669 if (top_level)
5670 IoSetTopLevelIrp(NULL);
5671
5672 return Status;
5673 }
5674
5675 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
5676 _Function_class_(DRIVER_DISPATCH)
5677 static NTSTATUS __stdcall drv_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
5678 NTSTATUS Status;
5679 device_extension* Vcb = DeviceObject->DeviceExtension;
5680 bool top_level;
5681
5682 FsRtlEnterFileSystem();
5683
5684 top_level = is_top_level(Irp);
5685
5686 Irp->IoStatus.Information = 0;
5687
5688 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
5689 volume_device_extension* vde = DeviceObject->DeviceExtension;
5690
5691 IoSkipCurrentIrpStackLocation(Irp);
5692
5693 Status = IoCallDriver(vde->attached_device, Irp);
5694
5695 goto exit;
5696 } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
5697 IoSkipCurrentIrpStackLocation(Irp);
5698
5699 Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
5700
5701 goto exit;
5702 } else if (Vcb && Vcb->type == VCB_TYPE_BUS) {
5703 bus_device_extension* bde = DeviceObject->DeviceExtension;
5704
5705 IoSkipCurrentIrpStackLocation(Irp);
5706
5707 Status = IoCallDriver(bde->attached_device, Irp);
5708
5709 goto exit;
5710 }
5711
5712 Status = Irp->IoStatus.Status;
5713 IoCompleteRequest(Irp, IO_NO_INCREMENT);
5714
5715 exit:
5716 if (top_level)
5717 IoSetTopLevelIrp(NULL);
5718
5719 FsRtlExitFileSystem();
5720
5721 return Status;
5722 }
5723
5724 bool is_file_name_valid(_In_ PUNICODE_STRING us, _In_ bool posix, _In_ bool stream) {
5725 ULONG i;
5726
5727 if (us->Length < sizeof(WCHAR))
5728 return false;
5729
5730 if (us->Length > 255 * sizeof(WCHAR))
5731 return false;
5732
5733 for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
5734 if (us->Buffer[i] == '/' || us->Buffer[i] == 0 ||
5735 (!posix && (us->Buffer[i] == '/' || us->Buffer[i] == ':')) ||
5736 (!posix && !stream && (us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == '"' ||
5737 us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))))
5738 return false;
5739 }
5740
5741 if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
5742 return false;
5743
5744 return true;
5745 }
5746
5747 void chunk_lock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ uint64_t start, _In_ uint64_t length) {
5748 LIST_ENTRY* le;
5749 bool locked;
5750 range_lock* rl;
5751
5752 rl = ExAllocateFromNPagedLookasideList(&Vcb->range_lock_lookaside);
5753 if (!rl) {
5754 ERR("out of memory\n");
5755 return;
5756 }
5757
5758 rl->start = start;
5759 rl->length = length;
5760 rl->thread = PsGetCurrentThread();
5761
5762 while (true) {
5763 locked = false;
5764
5765 ExAcquireResourceExclusiveLite(&c->range_locks_lock, true);
5766
5767 le = c->range_locks.Flink;
5768 while (le != &c->range_locks) {
5769 range_lock* rl2 = CONTAINING_RECORD(le, range_lock, list_entry);
5770
5771 if (rl2->start < start + length && rl2->start + rl2->length > start && rl2->thread != PsGetCurrentThread()) {
5772 locked = true;
5773 break;
5774 }
5775
5776 le = le->Flink;
5777 }
5778
5779 if (!locked) {
5780 InsertTailList(&c->range_locks, &rl->list_entry);
5781
5782 ExReleaseResourceLite(&c->range_locks_lock);
5783 return;
5784 }
5785
5786 KeClearEvent(&c->range_locks_event);
5787
5788 ExReleaseResourceLite(&c->range_locks_lock);
5789
5790 KeWaitForSingleObject(&c->range_locks_event, UserRequest, KernelMode, false, NULL);
5791 }
5792 }
5793
5794 void chunk_unlock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ uint64_t start, _In_ uint64_t length) {
5795 LIST_ENTRY* le;
5796
5797 ExAcquireResourceExclusiveLite(&c->range_locks_lock, true);
5798
5799 le = c->range_locks.Flink;
5800 while (le != &c->range_locks) {
5801 range_lock* rl = CONTAINING_RECORD(le, range_lock, list_entry);
5802
5803 if (rl->start == start && rl->length == length) {
5804 RemoveEntryList(&rl->list_entry);
5805 ExFreeToNPagedLookasideList(&Vcb->range_lock_lookaside, rl);
5806 break;
5807 }
5808
5809 le = le->Flink;
5810 }
5811
5812 KeSetEvent(&c->range_locks_event, 0, false);
5813
5814 ExReleaseResourceLite(&c->range_locks_lock);
5815 }
5816
5817 void log_device_error(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ int error) {
5818 dev->stats[error]++;
5819 dev->stats_changed = true;
5820 Vcb->stats_changed = true;
5821 }
5822
5823 #ifdef _DEBUG
5824 _Function_class_(KSTART_ROUTINE)
5825 static void __stdcall serial_thread(void* context) {
5826 LARGE_INTEGER due_time;
5827 KTIMER timer;
5828
5829 UNUSED(context);
5830
5831 KeInitializeTimer(&timer);
5832
5833 due_time.QuadPart = (uint64_t)-10000000;
5834
5835 KeSetTimer(&timer, due_time, NULL);
5836
5837 while (true) {
5838 KeWaitForSingleObject(&timer, Executive, KernelMode, false, NULL);
5839
5840 init_serial(false);
5841
5842 if (comdo)
5843 break;
5844
5845 KeSetTimer(&timer, due_time, NULL);
5846 }
5847
5848 KeCancelTimer(&timer);
5849
5850 PsTerminateSystemThread(STATUS_SUCCESS);
5851
5852 serial_thread_handle = NULL;
5853 }
5854
5855 static void init_serial(bool first_time) {
5856 NTSTATUS Status;
5857
5858 Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
5859 if (!NT_SUCCESS(Status)) {
5860 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
5861
5862 if (first_time) {
5863 OBJECT_ATTRIBUTES oa;
5864
5865 InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
5866
5867 Status = PsCreateSystemThread(&serial_thread_handle, 0, &oa, NULL, NULL, serial_thread, NULL);
5868 if (!NT_SUCCESS(Status)) {
5869 ERR("PsCreateSystemThread returned %08lx\n", Status);
5870 return;
5871 }
5872 }
5873 }
5874 }
5875 #endif
5876
5877 #if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
5878 static void check_cpu() {
5879 unsigned int cpuInfo[4];
5880 bool have_sse42;
5881
5882 #ifndef _MSC_VER
5883 __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
5884 have_sse42 = cpuInfo[2] & bit_SSE4_2;
5885 have_sse2 = cpuInfo[3] & bit_SSE2;
5886 #else
5887 __cpuid(cpuInfo, 1);
5888 have_sse42 = cpuInfo[2] & (1 << 20);
5889 have_sse2 = cpuInfo[3] & (1 << 26);
5890 #endif
5891
5892 if (have_sse42) {
5893 TRACE("SSE4.2 is supported\n");
5894 calc_crc32c = calc_crc32c_hw;
5895 } else
5896 TRACE("SSE4.2 not supported\n");
5897
5898 if (have_sse2)
5899 TRACE("SSE2 is supported\n");
5900 else
5901 TRACE("SSE2 is not supported\n");
5902 }
5903 #endif
5904
5905 #ifdef _DEBUG
5906 static void init_logging() {
5907 ExAcquireResourceExclusiveLite(&log_lock, true);
5908
5909 if (log_device.Length > 0)
5910 init_serial(true);
5911 else if (log_file.Length > 0) {
5912 NTSTATUS Status;
5913 OBJECT_ATTRIBUTES oa;
5914 IO_STATUS_BLOCK iosb;
5915 char* dateline;
5916 LARGE_INTEGER time;
5917 TIME_FIELDS tf;
5918
5919 InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
5920
5921 Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
5922 FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
5923
5924 if (!NT_SUCCESS(Status)) {
5925 ERR("ZwCreateFile returned %08lx\n", Status);
5926 goto end;
5927 }
5928
5929 if (iosb.Information == FILE_OPENED) { // already exists
5930 FILE_STANDARD_INFORMATION fsi;
5931 FILE_POSITION_INFORMATION fpi;
5932
5933 static const char delim[] = "\n---\n";
5934
5935 // move to end of file
5936
5937 Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
5938
5939 if (!NT_SUCCESS(Status)) {
5940 ERR("ZwQueryInformationFile returned %08lx\n", Status);
5941 goto end;
5942 }
5943
5944 fpi.CurrentByteOffset = fsi.EndOfFile;
5945
5946 Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
5947
5948 if (!NT_SUCCESS(Status)) {
5949 ERR("ZwSetInformationFile returned %08lx\n", Status);
5950 goto end;
5951 }
5952
5953 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, (void*)delim, sizeof(delim) - 1, NULL, NULL);
5954
5955 if (!NT_SUCCESS(Status)) {
5956 ERR("ZwWriteFile returned %08lx\n", Status);
5957 goto end;
5958 }
5959 }
5960
5961 dateline = ExAllocatePoolWithTag(PagedPool, 256, ALLOC_TAG);
5962
5963 if (!dateline) {
5964 ERR("out of memory\n");
5965 goto end;
5966 }
5967
5968 KeQuerySystemTime(&time);
5969
5970 RtlTimeToTimeFields(&time, &tf);
5971
5972 sprintf(dateline, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
5973
5974 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, (ULONG)strlen(dateline), NULL, NULL);
5975
5976 ExFreePool(dateline);
5977
5978 if (!NT_SUCCESS(Status)) {
5979 ERR("ZwWriteFile returned %08lx\n", Status);
5980 goto end;
5981 }
5982 }
5983
5984 end:
5985 ExReleaseResourceLite(&log_lock);
5986 }
5987 #endif
5988
5989 _Function_class_(KSTART_ROUTINE)
5990 static void __stdcall degraded_wait_thread(_In_ void* context) {
5991 KTIMER timer;
5992 LARGE_INTEGER delay;
5993
5994 UNUSED(context);
5995
5996 KeInitializeTimer(&timer);
5997
5998 delay.QuadPart = -30000000; // wait three seconds
5999 KeSetTimer(&timer, delay, NULL);
6000 KeWaitForSingleObject(&timer, Executive, KernelMode, false, NULL);
6001
6002 TRACE("timer expired\n");
6003
6004 degraded_wait = false;
6005
6006 ZwClose(degraded_wait_handle);
6007 degraded_wait_handle = NULL;
6008
6009 PsTerminateSystemThread(STATUS_SUCCESS);
6010 }
6011
6012 _Function_class_(DRIVER_ADD_DEVICE)
6013 NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
6014 LIST_ENTRY* le;
6015 NTSTATUS Status;
6016 UNICODE_STRING volname;
6017 ULONG i;
6018 WCHAR* s;
6019 pdo_device_extension* pdode = NULL;
6020 PDEVICE_OBJECT voldev;
6021 volume_device_extension* vde;
6022 UNICODE_STRING arc_name_us;
6023 WCHAR* anp;
6024
6025 static const WCHAR arc_name_prefix[] = L"\\ArcName\\btrfs(";
6026
6027 WCHAR arc_name[(sizeof(arc_name_prefix) / sizeof(WCHAR)) - 1 + 37];
6028
6029 TRACE("(%p, %p)\n", DriverObject, PhysicalDeviceObject);
6030
6031 ExAcquireResourceSharedLite(&pdo_list_lock, true);
6032
6033 le = pdo_list.Flink;
6034 while (le != &pdo_list) {
6035 pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
6036
6037 if (pdode2->pdo == PhysicalDeviceObject) {
6038 pdode = pdode2;
6039 break;
6040 }
6041
6042 le = le->Flink;
6043 }
6044
6045 if (!pdode) {
6046 WARN("unrecognized PDO %p\n", PhysicalDeviceObject);
6047 Status = STATUS_NOT_SUPPORTED;
6048 goto end;
6049 }
6050
6051 ExAcquireResourceExclusiveLite(&pdode->child_lock, true);
6052
6053 if (pdode->vde) { // if already done, return success
6054 Status = STATUS_SUCCESS;
6055 goto end2;
6056 }
6057
6058 volname.Length = volname.MaximumLength = (sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) + ((36 + 1) * sizeof(WCHAR));
6059 volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG); // FIXME - when do we free this?
6060
6061 if (!volname.Buffer) {
6062 ERR("out of memory\n");
6063 Status = STATUS_INSUFFICIENT_RESOURCES;
6064 goto end2;
6065 }
6066
6067 RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
6068 RtlCopyMemory(arc_name, arc_name_prefix, sizeof(arc_name_prefix) - sizeof(WCHAR));
6069
6070 anp = &arc_name[(sizeof(arc_name_prefix) / sizeof(WCHAR)) - 1];
6071 s = &volname.Buffer[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
6072
6073 for (i = 0; i < 16; i++) {
6074 *s = *anp = hex_digit(pdode->uuid.uuid[i] >> 4);
6075 s++;
6076 anp++;
6077
6078 *s = *anp = hex_digit(pdode->uuid.uuid[i] & 0xf);
6079 s++;
6080 anp++;
6081
6082 if (i == 3 || i == 5 || i == 7 || i == 9) {
6083 *s = *anp = '-';
6084 s++;
6085 anp++;
6086 }
6087 }
6088
6089 *s = '}';
6090 *anp = ')';
6091
6092 Status = IoCreateDevice(drvobj, sizeof(volume_device_extension), &volname, FILE_DEVICE_DISK,
6093 WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL : 0, false, &voldev);
6094 if (!NT_SUCCESS(Status)) {
6095 ERR("IoCreateDevice returned %08lx\n", Status);
6096 goto end2;
6097 }
6098
6099 arc_name_us.Buffer = arc_name;
6100 arc_name_us.Length = arc_name_us.MaximumLength = sizeof(arc_name);
6101
6102 Status = IoCreateSymbolicLink(&arc_name_us, &volname);
6103 if (!NT_SUCCESS(Status))
6104 WARN("IoCreateSymbolicLink returned %08lx\n", Status);
6105
6106 voldev->SectorSize = PhysicalDeviceObject->SectorSize;
6107 voldev->Flags |= DO_DIRECT_IO;
6108
6109 vde = voldev->DeviceExtension;
6110 vde->type = VCB_TYPE_VOLUME;
6111 vde->name = volname;
6112 vde->device = voldev;
6113 vde->mounted_device = NULL;
6114 vde->pdo = PhysicalDeviceObject;
6115 vde->pdode = pdode;
6116 vde->removing = false;
6117 vde->dead = false;
6118 vde->open_count = 0;
6119
6120 Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
6121 if (!NT_SUCCESS(Status))
6122 WARN("IoRegisterDeviceInterface returned %08lx\n", Status);
6123
6124 vde->attached_device = IoAttachDeviceToDeviceStack(voldev, PhysicalDeviceObject);
6125
6126 pdode->vde = vde;
6127
6128 if (pdode->removable)
6129 voldev->Characteristics |= FILE_REMOVABLE_MEDIA;
6130
6131 if (RtlCompareMemory(&boot_uuid, &pdode->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
6132 voldev->Flags |= DO_SYSTEM_BOOT_PARTITION;
6133 PhysicalDeviceObject->Flags |= DO_SYSTEM_BOOT_PARTITION;
6134 }
6135
6136 voldev->Flags &= ~DO_DEVICE_INITIALIZING;
6137
6138 Status = IoSetDeviceInterfaceState(&vde->bus_name, true);
6139 if (!NT_SUCCESS(Status))
6140 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
6141
6142 Status = STATUS_SUCCESS;
6143
6144 end2:
6145 ExReleaseResourceLite(&pdode->child_lock);
6146
6147 end:
6148 ExReleaseResourceLite(&pdo_list_lock);
6149
6150 return Status;
6151 }
6152
6153 _Function_class_(DRIVER_INITIALIZE)
6154 NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
6155 NTSTATUS Status;
6156 PDEVICE_OBJECT DeviceObject;
6157 UNICODE_STRING device_nameW;
6158 UNICODE_STRING dosdevice_nameW;
6159 control_device_extension* cde;
6160 bus_device_extension* bde;
6161 HANDLE regh;
6162 OBJECT_ATTRIBUTES oa, system_thread_attributes;
6163 ULONG dispos;
6164
6165 InitializeListHead(&uid_map_list);
6166 InitializeListHead(&gid_map_list);
6167
6168 #ifdef _DEBUG
6169 ExInitializeResourceLite(&log_lock);
6170 #endif
6171 ExInitializeResourceLite(&mapping_lock);
6172
6173 log_device.Buffer = NULL;
6174 log_device.Length = log_device.MaximumLength = 0;
6175 log_file.Buffer = NULL;
6176 log_file.Length = log_file.MaximumLength = 0;
6177
6178 registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
6179 registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
6180
6181 if (!registry_path.Buffer) {
6182 ERR("out of memory\n");
6183 return STATUS_INSUFFICIENT_RESOURCES;
6184 }
6185
6186 RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
6187
6188 read_registry(&registry_path, false);
6189
6190 #ifdef _DEBUG
6191 if (debug_log_level > 0)
6192 init_logging();
6193
6194 log_started = true;
6195 #endif
6196
6197 TRACE("DriverEntry\n");
6198
6199 #if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
6200 check_cpu();
6201 #endif
6202
6203 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8)) {
6204 UNICODE_STRING name;
6205 tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled;
6206
6207 RtlInitUnicodeString(&name, L"PsIsDiskCountersEnabled");
6208 fPsIsDiskCountersEnabled = (tPsIsDiskCountersEnabled)MmGetSystemRoutineAddress(&name);
6209
6210 if (fPsIsDiskCountersEnabled) {
6211 diskacc = fPsIsDiskCountersEnabled();
6212
6213 RtlInitUnicodeString(&name, L"PsUpdateDiskCounters");
6214 fPsUpdateDiskCounters = (tPsUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
6215
6216 if (!fPsUpdateDiskCounters)
6217 diskacc = false;
6218
6219 RtlInitUnicodeString(&name, L"FsRtlUpdateDiskCounters");
6220 fFsRtlUpdateDiskCounters = (tFsRtlUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
6221 }
6222
6223 RtlInitUnicodeString(&name, L"CcCopyReadEx");
6224 fCcCopyReadEx = (tCcCopyReadEx)MmGetSystemRoutineAddress(&name);
6225
6226 RtlInitUnicodeString(&name, L"CcCopyWriteEx");
6227 fCcCopyWriteEx = (tCcCopyWriteEx)MmGetSystemRoutineAddress(&name);
6228
6229 RtlInitUnicodeString(&name, L"CcSetAdditionalCacheAttributesEx");
6230 fCcSetAdditionalCacheAttributesEx = (tCcSetAdditionalCacheAttributesEx)MmGetSystemRoutineAddress(&name);
6231
6232 RtlInitUnicodeString(&name, L"FsRtlCheckLockForOplockRequest");
6233 fFsRtlCheckLockForOplockRequest = (tFsRtlCheckLockForOplockRequest)MmGetSystemRoutineAddress(&name);
6234 } else {
6235 fPsUpdateDiskCounters = NULL;
6236 fCcCopyReadEx = NULL;
6237 fCcCopyWriteEx = NULL;
6238 fCcSetAdditionalCacheAttributesEx = NULL;
6239 fFsRtlUpdateDiskCounters = NULL;
6240 fFsRtlCheckLockForOplockRequest = NULL;
6241 }
6242
6243 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN7)) {
6244 UNICODE_STRING name;
6245
6246 RtlInitUnicodeString(&name, L"IoUnregisterPlugPlayNotificationEx");
6247 fIoUnregisterPlugPlayNotificationEx = (tIoUnregisterPlugPlayNotificationEx)MmGetSystemRoutineAddress(&name);
6248
6249 RtlInitUnicodeString(&name, L"FsRtlAreThereCurrentOrInProgressFileLocks");
6250 fFsRtlAreThereCurrentOrInProgressFileLocks = (tFsRtlAreThereCurrentOrInProgressFileLocks)MmGetSystemRoutineAddress(&name);
6251 } else {
6252 fIoUnregisterPlugPlayNotificationEx = NULL;
6253 fFsRtlAreThereCurrentOrInProgressFileLocks = NULL;
6254 }
6255
6256 if (WdmlibRtlIsNtDdiVersionAvailable(NTDDI_VISTA)) {
6257 UNICODE_STRING name;
6258
6259 RtlInitUnicodeString(&name, L"FsRtlGetEcpListFromIrp");
6260 fFsRtlGetEcpListFromIrp = (tFsRtlGetEcpListFromIrp)MmGetSystemRoutineAddress(&name);
6261
6262 RtlInitUnicodeString(&name, L"FsRtlGetNextExtraCreateParameter");
6263 fFsRtlGetNextExtraCreateParameter = (tFsRtlGetNextExtraCreateParameter)MmGetSystemRoutineAddress(&name);
6264
6265 RtlInitUnicodeString(&name, L"FsRtlValidateReparsePointBuffer");
6266 fFsRtlValidateReparsePointBuffer = (tFsRtlValidateReparsePointBuffer)MmGetSystemRoutineAddress(&name);
6267 } else {
6268 fFsRtlGetEcpListFromIrp = NULL;
6269 fFsRtlGetNextExtraCreateParameter = NULL;
6270 fFsRtlValidateReparsePointBuffer = compat_FsRtlValidateReparsePointBuffer;
6271 }
6272
6273 drvobj = DriverObject;
6274
6275 DriverObject->DriverUnload = DriverUnload;
6276
6277 DriverObject->DriverExtension->AddDevice = AddDevice;
6278
6279 DriverObject->MajorFunction[IRP_MJ_CREATE] = drv_create;
6280 DriverObject->MajorFunction[IRP_MJ_CLOSE] = drv_close;
6281 DriverObject->MajorFunction[IRP_MJ_READ] = drv_read;
6282 DriverObject->MajorFunction[IRP_MJ_WRITE] = drv_write;
6283 DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = drv_query_information;
6284 DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = drv_set_information;
6285 DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = drv_query_ea;
6286 DriverObject->MajorFunction[IRP_MJ_SET_EA] = drv_set_ea;
6287 DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = drv_flush_buffers;
6288 DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = drv_query_volume_information;
6289 DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = drv_set_volume_information;
6290 DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = drv_directory_control;
6291 DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = drv_file_system_control;
6292 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = drv_device_control;
6293 DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = drv_shutdown;
6294 DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = drv_lock_control;
6295 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = drv_cleanup;
6296 DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = drv_query_security;
6297 DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = drv_set_security;
6298 DriverObject->MajorFunction[IRP_MJ_POWER] = drv_power;
6299 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = drv_system_control;
6300 DriverObject->MajorFunction[IRP_MJ_PNP] = drv_pnp;
6301
6302 init_fast_io_dispatch(&DriverObject->FastIoDispatch);
6303
6304 device_nameW.Buffer = (WCHAR*)device_name;
6305 device_nameW.Length = device_nameW.MaximumLength = sizeof(device_name) - sizeof(WCHAR);
6306 dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
6307 dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) - sizeof(WCHAR);
6308
6309 Status = IoCreateDevice(DriverObject, sizeof(control_device_extension), &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM,
6310 FILE_DEVICE_SECURE_OPEN, false, &DeviceObject);
6311 if (!NT_SUCCESS(Status)) {
6312 ERR("IoCreateDevice returned %08lx\n", Status);
6313 return Status;
6314 }
6315
6316 master_devobj = DeviceObject;
6317 cde = (control_device_extension*)master_devobj->DeviceExtension;
6318
6319 RtlZeroMemory(cde, sizeof(control_device_extension));
6320
6321 cde->type = VCB_TYPE_CONTROL;
6322
6323 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
6324
6325 Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
6326 if (!NT_SUCCESS(Status)) {
6327 ERR("IoCreateSymbolicLink returned %08lx\n", Status);
6328 return Status;
6329 }
6330
6331 init_cache();
6332
6333 InitializeListHead(&VcbList);
6334 ExInitializeResourceLite(&global_loading_lock);
6335 ExInitializeResourceLite(&pdo_list_lock);
6336
6337 InitializeListHead(&pdo_list);
6338
6339 InitializeObjectAttributes(&oa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
6340 Status = ZwCreateKey(&regh, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
6341 if (!NT_SUCCESS(Status)) {
6342 ERR("ZwCreateKey returned %08lx\n", Status);
6343 return Status;
6344 }
6345
6346 watch_registry(regh);
6347
6348 Status = IoCreateDevice(DriverObject, sizeof(bus_device_extension), NULL, FILE_DEVICE_UNKNOWN,
6349 FILE_DEVICE_SECURE_OPEN, false, &busobj);
6350 if (!NT_SUCCESS(Status)) {
6351 ERR("IoCreateDevice returned %08lx\n", Status);
6352 return Status;
6353 }
6354
6355 bde = (bus_device_extension*)busobj->DeviceExtension;
6356
6357 RtlZeroMemory(bde, sizeof(bus_device_extension));
6358
6359 bde->type = VCB_TYPE_BUS;
6360
6361 Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF,
6362 NULL, NULL, 0, &bde->buspdo);
6363 if (!NT_SUCCESS(Status)) {
6364 ERR("IoReportDetectedDevice returned %08lx\n", Status);
6365 return Status;
6366 }
6367
6368 Status = IoRegisterDeviceInterface(bde->buspdo, &BtrfsBusInterface, NULL, &bde->bus_name);
6369 if (!NT_SUCCESS(Status))
6370 WARN("IoRegisterDeviceInterface returned %08lx\n", Status);
6371
6372 bde->attached_device = IoAttachDeviceToDeviceStack(busobj, bde->buspdo);
6373
6374 busobj->Flags &= ~DO_DEVICE_INITIALIZING;
6375
6376 Status = IoSetDeviceInterfaceState(&bde->bus_name, true);
6377 if (!NT_SUCCESS(Status))
6378 WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
6379
6380 IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
6381
6382 InitializeObjectAttributes(&system_thread_attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
6383
6384 Status = PsCreateSystemThread(&degraded_wait_handle, 0, &system_thread_attributes, NULL, NULL, degraded_wait_thread, NULL);
6385 if (!NT_SUCCESS(Status))
6386 WARN("PsCreateSystemThread returned %08lx\n", Status);
6387
6388 ExInitializeResourceLite(&boot_lock);
6389
6390 Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
6391 (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry2);
6392 if (!NT_SUCCESS(Status))
6393 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
6394
6395 Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
6396 (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry3);
6397 if (!NT_SUCCESS(Status))
6398 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
6399
6400 Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
6401 (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, pnp_notification, DriverObject, &notification_entry);
6402 if (!NT_SUCCESS(Status))
6403 ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
6404
6405 finished_probing = true;
6406
6407 KeInitializeEvent(&mountmgr_thread_event, NotificationEvent, false);
6408
6409 Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, &system_thread_attributes, NULL, NULL, mountmgr_thread, NULL);
6410 if (!NT_SUCCESS(Status))
6411 WARN("PsCreateSystemThread returned %08lx\n", Status);
6412
6413 IoRegisterFileSystem(DeviceObject);
6414
6415 IoRegisterBootDriverReinitialization(DriverObject, check_system_root, NULL);
6416
6417 return STATUS_SUCCESS;
6418 }