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