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