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