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