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