190db9c149c7023e09b8ea128a59cf9b570f8f52
[reactos.git] / reactos / drivers / filesystems / btrfs / btrfs.c
1 /* Copyright (c) Mark Harmstone 2016
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 "btrfs.h"
31 #ifndef __REACTOS__
32 #include <winioctl.h>
33 #else
34 #include <rtlfuncs.h>
35 #endif
36 #include <mountdev.h>
37
38 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
39 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
40 #define COMPAT_RO_SUPPORTED 0
41
42 static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
43 static WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
44
45 PDRIVER_OBJECT drvobj;
46 PDEVICE_OBJECT devobj;
47 #ifndef __REACTOS__
48 BOOL have_sse42 = FALSE;
49 #endif
50 UINT64 num_reads = 0;
51 LIST_ENTRY uid_map_list;
52 LIST_ENTRY volumes;
53 LIST_ENTRY VcbList;
54 ERESOURCE global_loading_lock;
55 UINT32 debug_log_level = 0;
56 BOOL log_started = FALSE;
57 UNICODE_STRING log_device, log_file, registry_path;
58
59 #ifdef _DEBUG
60 PFILE_OBJECT comfo = NULL;
61 PDEVICE_OBJECT comdo = NULL;
62 HANDLE log_handle = NULL;
63 #endif
64
65 int __security_cookie = __LINE__;
66
67 static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject);
68
69 typedef struct {
70 KEVENT Event;
71 IO_STATUS_BLOCK iosb;
72 } read_context;
73
74 #ifdef _DEBUG
75 static NTSTATUS STDCALL dbg_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
76 read_context* context = conptr;
77
78 // DbgPrint("dbg_completion\n");
79
80 context->iosb = Irp->IoStatus;
81 KeSetEvent(&context->Event, 0, FALSE);
82
83 // return STATUS_SUCCESS;
84 return STATUS_MORE_PROCESSING_REQUIRED;
85 }
86
87 #ifdef DEBUG_LONG_MESSAGES
88 void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...) {
89 #else
90 void STDCALL _debug_message(const char* func, char* s, ...) {
91 #endif
92 LARGE_INTEGER offset;
93 PIO_STACK_LOCATION IrpSp;
94 NTSTATUS Status;
95 PIRP Irp;
96 va_list ap;
97 char *buf2 = NULL, *buf;
98 read_context* context = NULL;
99 UINT32 length;
100
101 buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
102
103 if (!buf2) {
104 DbgPrint("Couldn't allocate buffer in debug_message\n");
105 return;
106 }
107
108 #ifdef DEBUG_LONG_MESSAGES
109 sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
110 #else
111 sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
112 #endif
113 buf = &buf2[strlen(buf2)];
114
115 va_start(ap, s);
116 vsprintf(buf, s, ap);
117
118 if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
119 DbgPrint(buf2);
120 } else if (log_device.Length > 0) {
121 if (!comdo) {
122 DbgPrint("comdo is NULL :-(\n");
123 DbgPrint(buf2);
124 goto exit2;
125 }
126
127 length = (UINT32)strlen(buf2);
128
129 offset.u.LowPart = 0;
130 offset.u.HighPart = 0;
131
132 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
133 if (!context) {
134 DbgPrint("Couldn't allocate context in debug_message\n");
135 return;
136 }
137
138 RtlZeroMemory(context, sizeof(read_context));
139
140 KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
141
142 // status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
143
144 Irp = IoAllocateIrp(comdo->StackSize, FALSE);
145
146 if (!Irp) {
147 DbgPrint("IoAllocateIrp failed\n");
148 goto exit2;
149 }
150
151 IrpSp = IoGetNextIrpStackLocation(Irp);
152 IrpSp->MajorFunction = IRP_MJ_WRITE;
153
154 if (comdo->Flags & DO_BUFFERED_IO) {
155 Irp->AssociatedIrp.SystemBuffer = buf2;
156
157 Irp->Flags = IRP_BUFFERED_IO;
158 } else if (comdo->Flags & DO_DIRECT_IO) {
159 Irp->MdlAddress = IoAllocateMdl(buf2, length, FALSE, FALSE, NULL);
160 if (!Irp->MdlAddress) {
161 DbgPrint("IoAllocateMdl failed\n");
162 goto exit;
163 }
164
165 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
166 } else {
167 Irp->UserBuffer = buf2;
168 }
169
170 IrpSp->Parameters.Write.Length = length;
171 IrpSp->Parameters.Write.ByteOffset = offset;
172
173 Irp->UserIosb = &context->iosb;
174
175 Irp->UserEvent = &context->Event;
176
177 IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE);
178
179 Status = IoCallDriver(comdo, Irp);
180
181 if (Status == STATUS_PENDING) {
182 KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
183 Status = context->iosb.Status;
184 }
185
186 if (comdo->Flags & DO_DIRECT_IO) {
187 MmUnlockPages(Irp->MdlAddress);
188 IoFreeMdl(Irp->MdlAddress);
189 }
190
191 if (!NT_SUCCESS(Status)) {
192 DbgPrint("failed to write to COM1 - error %08x\n", Status);
193 goto exit;
194 }
195
196 exit:
197 IoFreeIrp(Irp);
198 } else if (log_handle != NULL) {
199 IO_STATUS_BLOCK iosb;
200
201 length = (UINT32)strlen(buf2);
202
203 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
204
205 if (!NT_SUCCESS(Status)) {
206 DbgPrint("failed to write to file - error %08x\n", Status);
207 }
208 }
209
210 exit2:
211 va_end(ap);
212
213 if (context)
214 ExFreePool(context);
215
216 if (buf2)
217 ExFreePool(buf2);
218 }
219 #endif
220
221 UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment )
222 {
223 if( Alignment & ( Alignment - 1 ) )
224 {
225 //
226 // Alignment not a power of 2
227 // Just returning
228 //
229 return NumberToBeAligned;
230 }
231 if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
232 {
233 NumberToBeAligned = NumberToBeAligned + Alignment;
234 NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
235 }
236 return NumberToBeAligned;
237 }
238
239 int keycmp(const KEY* key1, const KEY* key2) {
240 if (key1->obj_id < key2->obj_id) {
241 return -1;
242 } else if (key1->obj_id > key2->obj_id) {
243 return 1;
244 }
245
246 if (key1->obj_type < key2->obj_type) {
247 return -1;
248 } else if (key1->obj_type > key2->obj_type) {
249 return 1;
250 }
251
252 if (key1->offset < key2->offset) {
253 return -1;
254 } else if (key1->offset > key2->offset) {
255 return 1;
256 }
257
258 return 0;
259 }
260
261 BOOL is_top_level(PIRP Irp) {
262 if (!IoGetTopLevelIrp()) {
263 IoSetTopLevelIrp(Irp);
264 return TRUE;
265 }
266
267 return FALSE;
268 }
269
270 static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
271 UNICODE_STRING dosdevice_nameW;
272
273 ERR("DriverUnload\n");
274
275 free_cache();
276
277 IoUnregisterFileSystem(DriverObject->DeviceObject);
278
279 dosdevice_nameW.Buffer = dosdevice_name;
280 dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
281
282 IoDeleteSymbolicLink(&dosdevice_nameW);
283 IoDeleteDevice(DriverObject->DeviceObject);
284
285 while (!IsListEmpty(&uid_map_list)) {
286 LIST_ENTRY* le = RemoveHeadList(&uid_map_list);
287 uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
288
289 ExFreePool(um->sid);
290
291 ExFreePool(um);
292 }
293
294 // FIXME - free volumes and their devpaths
295
296 #ifdef _DEBUG
297 if (comfo)
298 ObDereferenceObject(comfo);
299
300 if (log_handle)
301 ZwClose(log_handle);
302 #endif
303
304 ExDeleteResourceLite(&global_loading_lock);
305
306 if (log_device.Buffer)
307 ExFreePool(log_device.Buffer);
308
309 if (log_file.Buffer)
310 ExFreePool(log_file.Buffer);
311
312 if (registry_path.Buffer)
313 ExFreePool(registry_path.Buffer);
314 }
315
316 BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
317 KEY searchkey;
318 traverse_ptr tp, prev_tp;
319 NTSTATUS Status;
320
321 // get last entry
322 searchkey.obj_id = 0xffffffffffffffff;
323 searchkey.obj_type = 0xff;
324 searchkey.offset = 0xffffffffffffffff;
325
326 Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
327 if (!NT_SUCCESS(Status)) {
328 ERR("error - find_item returned %08x\n", Status);
329 return FALSE;
330 }
331
332 while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
333 tp = prev_tp;
334
335 TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
336
337 if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
338 r->lastinode = tp.item->key.obj_id;
339 TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
340 return TRUE;
341 }
342 }
343
344 r->lastinode = SUBVOL_ROOT_INODE;
345
346 WARN("no INODE_ITEMs in tree %llx\n", r->id);
347
348 return TRUE;
349 }
350
351 BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
352 KEY searchkey;
353 traverse_ptr tp;
354 DIR_ITEM* xa;
355 ULONG size, xasize;
356 NTSTATUS Status;
357
358 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
359
360 searchkey.obj_id = inode;
361 searchkey.obj_type = TYPE_XATTR_ITEM;
362 searchkey.offset = crc32;
363
364 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
365 if (!NT_SUCCESS(Status)) {
366 ERR("error - find_item returned %08x\n", Status);
367 return FALSE;
368 }
369
370 if (keycmp(&tp.item->key, &searchkey)) {
371 TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
372 return FALSE;
373 }
374
375 if (tp.item->size < sizeof(DIR_ITEM)) {
376 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));
377 return FALSE;
378 }
379
380 xa = (DIR_ITEM*)tp.item->data;
381 size = tp.item->size;
382
383 while (TRUE) {
384 if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
385 WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
386 return FALSE;
387 }
388
389 if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
390 TRACE("found xattr %s in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
391
392 *datalen = xa->m;
393
394 if (xa->m > 0) {
395 *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
396 if (!*data) {
397 ERR("out of memory\n");
398 return FALSE;
399 }
400
401 RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
402 } else
403 *data = NULL;
404
405 return TRUE;
406 }
407
408 xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
409
410 if (size > xasize) {
411 size -= xasize;
412 xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
413 } else
414 break;
415 }
416
417 TRACE("xattr %s not found in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
418
419 return FALSE;
420 }
421
422 NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) {
423 KEY searchkey;
424 traverse_ptr tp;
425 UINT8* di2;
426 NTSTATUS Status;
427
428 searchkey.obj_id = inode;
429 searchkey.obj_type = TYPE_DIR_ITEM;
430 searchkey.offset = crc32;
431
432 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
433 if (!NT_SUCCESS(Status)) {
434 ERR("error - find_item returned %08x\n", Status);
435 return Status;
436 }
437
438 if (!keycmp(&tp.item->key, &searchkey)) {
439 ULONG maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
440
441 if (tp.item->size + disize > maxlen) {
442 WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp.item->size, disize, maxlen);
443 return STATUS_INTERNAL_ERROR;
444 }
445
446 di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size + disize, ALLOC_TAG);
447 if (!di2) {
448 ERR("out of memory\n");
449 return STATUS_INSUFFICIENT_RESOURCES;
450 }
451
452 if (tp.item->size > 0)
453 RtlCopyMemory(di2, tp.item->data, tp.item->size);
454
455 RtlCopyMemory(di2 + tp.item->size, di, disize);
456
457 delete_tree_item(Vcb, &tp, rollback);
458
459 insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, rollback);
460
461 ExFreePool(di);
462 } else {
463 insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback);
464 }
465
466 return STATUS_SUCCESS;
467 }
468
469 static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
470 NTSTATUS Status;
471 PIO_STACK_LOCATION IrpSp;
472 device_extension* Vcb = DeviceObject->DeviceExtension;
473 BOOL top_level;
474
475 TRACE("close\n");
476
477 FsRtlEnterFileSystem();
478
479 top_level = is_top_level(Irp);
480
481 if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) {
482 TRACE("Closing file system\n");
483 Status = STATUS_SUCCESS;
484 goto exit;
485 }
486
487 IrpSp = IoGetCurrentIrpStackLocation(Irp);
488
489 // FIXME - unmount if called for volume
490 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
491
492 Status = close_file(DeviceObject->DeviceExtension, IrpSp->FileObject);
493
494 exit:
495 Irp->IoStatus.Status = Status;
496 Irp->IoStatus.Information = 0;
497
498 IoCompleteRequest( Irp, IO_DISK_INCREMENT );
499
500 if (top_level)
501 IoSetTopLevelIrp(NULL);
502
503 FsRtlExitFileSystem();
504
505 TRACE("returning %08x\n", Status);
506
507 return Status;
508 }
509
510 static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
511 NTSTATUS Status;
512 BOOL top_level;
513 device_extension* Vcb = DeviceObject->DeviceExtension;
514
515 FsRtlEnterFileSystem();
516
517 top_level = is_top_level(Irp);
518
519 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
520 Status = part0_passthrough(DeviceObject, Irp);
521 goto exit;
522 }
523
524 FIXME("STUB: query ea\n");
525 Status = STATUS_NOT_IMPLEMENTED;
526
527 Irp->IoStatus.Status = Status;
528 Irp->IoStatus.Information = 0;
529
530 IoCompleteRequest( Irp, IO_NO_INCREMENT );
531
532 exit:
533 if (top_level)
534 IoSetTopLevelIrp(NULL);
535
536 FsRtlExitFileSystem();
537
538 return Status;
539 }
540
541 static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
542 NTSTATUS Status;
543 device_extension* Vcb = DeviceObject->DeviceExtension;
544 BOOL top_level;
545
546 FsRtlEnterFileSystem();
547
548 top_level = is_top_level(Irp);
549
550 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
551 Status = part0_passthrough(DeviceObject, Irp);
552 goto exit;
553 }
554
555 FIXME("STUB: set ea\n");
556 Status = STATUS_NOT_IMPLEMENTED;
557
558 if (Vcb->readonly)
559 Status = STATUS_MEDIA_WRITE_PROTECTED;
560
561 // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
562
563 Irp->IoStatus.Status = Status;
564 Irp->IoStatus.Information = 0;
565
566 IoCompleteRequest( Irp, IO_NO_INCREMENT );
567
568 exit:
569 if (top_level)
570 IoSetTopLevelIrp(NULL);
571
572 FsRtlExitFileSystem();
573
574 return Status;
575 }
576
577 static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
578 NTSTATUS Status;
579 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
580 PFILE_OBJECT FileObject = IrpSp->FileObject;
581 fcb* fcb = FileObject->FsContext;
582 device_extension* Vcb = DeviceObject->DeviceExtension;
583 BOOL top_level;
584
585 TRACE("flush buffers\n");
586
587 FsRtlEnterFileSystem();
588
589 top_level = is_top_level(Irp);
590
591 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
592 Status = part0_passthrough(DeviceObject, Irp);
593 goto exit;
594 }
595
596 Status = STATUS_SUCCESS;
597 Irp->IoStatus.Status = Status;
598 Irp->IoStatus.Information = 0;
599
600 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
601 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
602
603 if (fcb->Header.PagingIoResource) {
604 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
605 ExReleaseResourceLite(fcb->Header.PagingIoResource);
606 }
607
608 Status = Irp->IoStatus.Status;
609 }
610
611 IoCompleteRequest(Irp, IO_NO_INCREMENT);
612
613 exit:
614 if (top_level)
615 IoSetTopLevelIrp(NULL);
616
617 FsRtlExitFileSystem();
618
619 return Status;
620 }
621
622 static void calculate_total_space(device_extension* Vcb, LONGLONG* totalsize, LONGLONG* freespace) {
623 UINT8 factor;
624
625 if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10)
626 factor = 2;
627 else
628 factor = 1;
629
630 *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) / factor;
631 *freespace = ((Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size) / factor;
632 }
633
634 static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
635 PIO_STACK_LOCATION IrpSp;
636 NTSTATUS Status;
637 ULONG BytesCopied = 0;
638 device_extension* Vcb = DeviceObject->DeviceExtension;
639 BOOL top_level;
640
641 #ifndef __REACTOS__
642 // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
643 // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
644 // which prevents UAC from working.
645 // FIXME - only lie if we detect that we're being called by mpr.dll
646
647 WCHAR* fs_name = L"NTFS";
648 ULONG fs_name_len = 4 * sizeof(WCHAR);
649 #else
650 WCHAR* fs_name = L"Btrfs";
651 ULONG fs_name_len = 5 * sizeof(WCHAR);
652 #endif
653
654 TRACE("query volume information\n");
655
656 FsRtlEnterFileSystem();
657 top_level = is_top_level(Irp);
658
659 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
660 Status = part0_passthrough(DeviceObject, Irp);
661 goto exit;
662 }
663
664 IrpSp = IoGetCurrentIrpStackLocation(Irp);
665
666 Status = STATUS_NOT_IMPLEMENTED;
667
668 switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
669 case FileFsAttributeInformation:
670 {
671 FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
672 BOOL overflow = FALSE;
673 ULONG orig_fs_name_len = fs_name_len;
674
675 TRACE("FileFsAttributeInformation\n");
676
677 if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
678 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
679 fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
680 else
681 fs_name_len = 0;
682
683 overflow = TRUE;
684 }
685
686 data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
687 FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
688 FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS;
689 if (Vcb->readonly)
690 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
691
692 // should also be FILE_FILE_COMPRESSION when supported
693 data->MaximumComponentNameLength = 255; // FIXME - check
694 data->FileSystemNameLength = orig_fs_name_len;
695 RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
696
697 BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
698 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
699 break;
700 }
701
702 case FileFsControlInformation:
703 FIXME("STUB: FileFsControlInformation\n");
704 break;
705
706 case FileFsDeviceInformation:
707 FIXME("STUB: FileFsDeviceInformation\n");
708 break;
709
710 case FileFsDriverPathInformation:
711 FIXME("STUB: FileFsDriverPathInformation\n");
712 break;
713
714 case FileFsFullSizeInformation:
715 {
716 FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
717
718 TRACE("FileFsFullSizeInformation\n");
719
720 calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->ActualAvailableAllocationUnits.QuadPart);
721 ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
722 ffsi->SectorsPerAllocationUnit = 1;
723 ffsi->BytesPerSector = Vcb->superblock.sector_size;
724
725 BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
726 Status = STATUS_SUCCESS;
727
728 break;
729 }
730
731 case FileFsObjectIdInformation:
732 {
733 FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
734
735 TRACE("FileFsObjectIdInformation\n");
736
737 RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
738 RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
739
740 BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
741 Status = STATUS_SUCCESS;
742
743 break;
744 }
745
746 case FileFsSizeInformation:
747 {
748 FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
749
750 TRACE("FileFsSizeInformation\n");
751
752 calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->AvailableAllocationUnits.QuadPart);
753 ffsi->SectorsPerAllocationUnit = 1;
754 ffsi->BytesPerSector = Vcb->superblock.sector_size;
755
756 BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
757 Status = STATUS_SUCCESS;
758
759 break;
760 }
761
762 case FileFsVolumeInformation:
763 {
764 FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
765 FILE_FS_VOLUME_INFORMATION ffvi;
766 BOOL overflow = FALSE;
767 ULONG label_len, orig_label_len;
768
769 TRACE("FileFsVolumeInformation\n");
770 TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
771
772 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
773
774 // orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
775 RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
776 orig_label_len = label_len;
777
778 if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
779 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
780 label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
781 else
782 label_len = 0;
783
784 overflow = TRUE;
785 }
786
787 TRACE("label_len = %u\n", label_len);
788
789 ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
790 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];
791 ffvi.VolumeLabelLength = orig_label_len;
792 ffvi.SupportsObjects = FALSE;
793
794 RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
795
796 if (label_len > 0) {
797 ULONG bytecount;
798
799 // RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
800 RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
801 TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
802 }
803
804 ExReleaseResourceLite(&Vcb->tree_lock);
805
806 BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
807 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
808 break;
809 }
810
811 default:
812 Status = STATUS_INVALID_PARAMETER;
813 WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
814 break;
815 }
816
817 // if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
818 // WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
819 // BytesCopied = IrpSp->Parameters.QueryVolume.Length;
820 // Status = STATUS_BUFFER_OVERFLOW;
821 // }
822
823 Irp->IoStatus.Status = Status;
824
825 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
826 Irp->IoStatus.Information = 0;
827 else
828 Irp->IoStatus.Information = BytesCopied;
829
830 IoCompleteRequest( Irp, IO_DISK_INCREMENT );
831
832 exit:
833 if (top_level)
834 IoSetTopLevelIrp(NULL);
835
836 FsRtlExitFileSystem();
837
838 TRACE("query volume information returning %08x\n", Status);
839
840 return Status;
841 }
842
843 static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
844 read_context* context = conptr;
845
846 // DbgPrint("read_completion\n");
847
848 context->iosb = Irp->IoStatus;
849 KeSetEvent(&context->Event, 0, FALSE);
850
851 // return STATUS_SUCCESS;
852 return STATUS_MORE_PROCESSING_REQUIRED;
853 }
854
855 // static void test_tree_deletion(device_extension* Vcb) {
856 // KEY searchkey/*, endkey*/;
857 // traverse_ptr tp, next_tp;
858 // root* r;
859 //
860 // searchkey.obj_id = 0x100;
861 // searchkey.obj_type = 0x54;
862 // searchkey.offset = 0xca4ab2f5;
863 //
864 // // endkey.obj_id = 0x100;
865 // // endkey.obj_type = 0x60;
866 // // endkey.offset = 0x15a;
867 //
868 // r = Vcb->roots;
869 // while (r && r->id != 0x102)
870 // r = r->next;
871 //
872 // if (!r) {
873 // ERR("error - could not find root\n");
874 // return;
875 // }
876 //
877 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
878 // ERR("error - could not find key\n");
879 // return;
880 // }
881 //
882 // while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
883 // tp.item->ignore = TRUE;
884 // add_to_tree_cache(tc, tp.tree);
885 //
886 // if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
887 // free_traverse_ptr(&tp);
888 // tp = next_tp;
889 // } else
890 // break;
891 // }
892 //
893 // free_traverse_ptr(&tp);
894 // }
895
896 // static void test_tree_splitting(device_extension* Vcb) {
897 // int i;
898 //
899 // for (i = 0; i < 1000; i++) {
900 // char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
901 //
902 // insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
903 // }
904 // }
905
906 // static void test_dropping_tree(device_extension* Vcb) {
907 // LIST_ENTRY* le = Vcb->roots.Flink;
908 //
909 // while (le != &Vcb->roots) {
910 // root* r = CONTAINING_RECORD(le, root, list_entry);
911 //
912 // if (r->id == 0x101) {
913 // RemoveEntryList(&r->list_entry);
914 // InsertTailList(&Vcb->drop_roots, &r->list_entry);
915 // return;
916 // }
917 //
918 // le = le->Flink;
919 // }
920 // }
921
922 NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, LIST_ENTRY* rollback) {
923 root* r;
924 tree* t;
925 ROOT_ITEM* ri;
926 traverse_ptr tp;
927
928 r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
929 if (!r) {
930 ERR("out of memory\n");
931 return STATUS_INSUFFICIENT_RESOURCES;
932 }
933
934 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
935 if (!r->nonpaged) {
936 ERR("out of memory\n");
937 ExFreePool(r);
938 return STATUS_INSUFFICIENT_RESOURCES;
939 }
940
941 if (!no_tree) {
942 t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
943 if (!t) {
944 ERR("out of memory\n");
945 ExFreePool(r->nonpaged);
946 ExFreePool(r);
947 return STATUS_INSUFFICIENT_RESOURCES;
948 }
949 }
950
951 ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
952 if (!ri) {
953 ERR("out of memory\n");
954
955 if (!no_tree)
956 ExFreePool(t);
957
958 ExFreePool(r->nonpaged);
959 ExFreePool(r);
960 return STATUS_INSUFFICIENT_RESOURCES;
961 }
962
963 r->id = id;
964 r->treeholder.address = 0;
965 r->treeholder.generation = Vcb->superblock.generation;
966 r->treeholder.tree = no_tree ? NULL : t;
967 r->lastinode = 0;
968 r->path.Buffer = NULL;
969 RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
970 r->root_item.num_references = 1;
971 InitializeListHead(&r->fcbs);
972
973 RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
974
975 // We ask here for a traverse_ptr to the item we're inserting, so we can
976 // copy some of the tree's variables
977
978 if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, rollback)) {
979 ERR("insert_tree_item failed\n");
980 ExFreePool(ri);
981
982 if (!no_tree)
983 ExFreePool(t);
984
985 ExFreePool(r->nonpaged);
986 ExFreePool(r);
987 return STATUS_INTERNAL_ERROR;
988 }
989
990 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
991
992 InsertTailList(&Vcb->roots, &r->list_entry);
993
994 if (!no_tree) {
995 t->header.fs_uuid = tp.tree->header.fs_uuid;
996 t->header.address = 0;
997 t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
998 t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
999 t->header.generation = Vcb->superblock.generation;
1000 t->header.tree_id = id;
1001 t->header.num_items = 0;
1002 t->header.level = 0;
1003
1004 t->has_address = FALSE;
1005 t->size = 0;
1006 t->Vcb = Vcb;
1007 t->parent = NULL;
1008 t->paritem = NULL;
1009 t->root = r;
1010
1011 InitializeListHead(&t->itemlist);
1012
1013 t->new_address = 0;
1014 t->has_new_address = FALSE;
1015 t->flags = tp.tree->flags;
1016
1017 InsertTailList(&Vcb->trees, &t->list_entry);
1018
1019 t->write = TRUE;
1020 Vcb->need_write = TRUE;
1021 }
1022
1023 *rootptr = r;
1024
1025 return STATUS_SUCCESS;
1026 }
1027
1028 // static void test_creating_root(device_extension* Vcb) {
1029 // NTSTATUS Status;
1030 // LIST_ENTRY rollback;
1031 // UINT64 id;
1032 // root* r;
1033 //
1034 // InitializeListHead(&rollback);
1035 //
1036 // if (Vcb->root_root->lastinode == 0)
1037 // get_last_inode(Vcb, Vcb->root_root);
1038 //
1039 // id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
1040 // Status = create_root(Vcb, id, &r, &rollback);
1041 //
1042 // if (!NT_SUCCESS(Status)) {
1043 // ERR("create_root returned %08x\n", Status);
1044 // do_rollback(Vcb, &rollback);
1045 // } else {
1046 // Vcb->root_root->lastinode = id;
1047 // clear_rollback(&rollback);
1048 // }
1049 // }
1050
1051 // static void test_alloc_chunk(device_extension* Vcb) {
1052 // LIST_ENTRY rollback;
1053 // chunk* c;
1054 //
1055 // InitializeListHead(&rollback);
1056 //
1057 // c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback);
1058 // if (!c) {
1059 // ERR("alloc_chunk failed\n");
1060 // do_rollback(Vcb, &rollback);
1061 // } else {
1062 // clear_rollback(&rollback);
1063 // }
1064 // }
1065
1066 // static void test_space_list(device_extension* Vcb) {
1067 // chunk* c;
1068 // int i, j;
1069 // LIST_ENTRY* le;
1070 //
1071 // typedef struct {
1072 // UINT64 address;
1073 // UINT64 length;
1074 // BOOL add;
1075 // } space_test;
1076 //
1077 // static const space_test entries[] = {
1078 // { 0x1000, 0x1000 },
1079 // { 0x3000, 0x2000 },
1080 // { 0x6000, 0x1000 },
1081 // { 0, 0 }
1082 // };
1083 //
1084 // static const space_test tests[] = {
1085 // { 0x0, 0x800, TRUE },
1086 // { 0x1800, 0x400, TRUE },
1087 // { 0x800, 0x2000, TRUE },
1088 // { 0x1000, 0x2000, TRUE },
1089 // { 0x2000, 0x3800, TRUE },
1090 // { 0x800, 0x1000, TRUE },
1091 // { 0x1800, 0x1000, TRUE },
1092 // { 0x5000, 0x800, TRUE },
1093 // { 0x5000, 0x1000, TRUE },
1094 // { 0x7000, 0x1000, TRUE },
1095 // { 0x8000, 0x1000, TRUE },
1096 // { 0x800, 0x800, TRUE },
1097 // { 0x0, 0x3800, TRUE },
1098 // { 0x1000, 0x2800, TRUE },
1099 // { 0x1000, 0x1000, FALSE },
1100 // { 0x800, 0x2000, FALSE },
1101 // { 0x0, 0x3800, FALSE },
1102 // { 0x2800, 0x1000, FALSE },
1103 // { 0x1800, 0x2000, FALSE },
1104 // { 0x3800, 0x1000, FALSE },
1105 // { 0, 0, FALSE }
1106 // };
1107 //
1108 // c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry);
1109 //
1110 // i = 0;
1111 // while (tests[i].length > 0) {
1112 // InitializeListHead(&c->space);
1113 // InitializeListHead(&c->space_size);
1114 // ERR("test %u\n", i);
1115 //
1116 // j = 0;
1117 // while (entries[j].length > 0) {
1118 // space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
1119 // s->address = entries[j].address;
1120 // s->size = entries[j].length;
1121 // InsertTailList(&c->space, &s->list_entry);
1122 //
1123 // order_space_entry(s, &c->space_size);
1124 //
1125 // j++;
1126 // }
1127 //
1128 // if (tests[i].add)
1129 // space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1130 // else
1131 // space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1132 //
1133 // le = c->space.Flink;
1134 // while (le != &c->space) {
1135 // space* s = CONTAINING_RECORD(le, space, list_entry);
1136 //
1137 // ERR("(%llx,%llx)\n", s->address, s->size);
1138 //
1139 // le = le->Flink;
1140 // }
1141 //
1142 // ERR("--\n");
1143 //
1144 // le = c->space_size.Flink;
1145 // while (le != &c->space_size) {
1146 // space* s = CONTAINING_RECORD(le, space, list_entry_size);
1147 //
1148 // ERR("(%llx,%llx)\n", s->address, s->size);
1149 //
1150 // le = le->Flink;
1151 // }
1152 //
1153 // i++;
1154 // }
1155 //
1156 // int3;
1157 // }
1158
1159 static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
1160 ULONG utf8len;
1161 NTSTATUS Status;
1162
1163 TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
1164
1165 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
1166 if (!NT_SUCCESS(Status))
1167 goto end;
1168
1169 if (utf8len > MAX_LABEL_SIZE) {
1170 Status = STATUS_INVALID_VOLUME_LABEL;
1171 goto end;
1172 }
1173
1174 // FIXME - check for '/' and '\\' and reject
1175
1176 // utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
1177
1178 Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
1179 if (!NT_SUCCESS(Status))
1180 goto release;
1181
1182 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1183
1184 if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
1185 RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
1186
1187 // test_tree_deletion(Vcb); // TESTING
1188 // test_tree_splitting(Vcb);
1189 // test_dropping_tree(Vcb);
1190 // test_creating_root(Vcb);
1191 // test_alloc_chunk(Vcb);
1192 // test_space_list(Vcb);
1193
1194 Vcb->need_write = TRUE;
1195
1196 release:
1197 ExReleaseResourceLite(&Vcb->tree_lock);
1198
1199 end:
1200 TRACE("returning %08x\n", Status);
1201
1202 return Status;
1203 }
1204
1205 static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
1206 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1207 device_extension* Vcb = DeviceObject->DeviceExtension;
1208 NTSTATUS Status;
1209 BOOL top_level;
1210
1211 TRACE("set volume information\n");
1212
1213 FsRtlEnterFileSystem();
1214
1215 top_level = is_top_level(Irp);
1216
1217 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
1218 Status = part0_passthrough(DeviceObject, Irp);
1219 goto exit;
1220 }
1221
1222 Status = STATUS_NOT_IMPLEMENTED;
1223
1224 if (Vcb->readonly) {
1225 Status = STATUS_MEDIA_WRITE_PROTECTED;
1226 goto end;
1227 }
1228
1229 if (Vcb->removing || Vcb->locked) {
1230 Status = STATUS_ACCESS_DENIED;
1231 goto end;
1232 }
1233
1234 switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
1235 case FileFsControlInformation:
1236 FIXME("STUB: FileFsControlInformation\n");
1237 break;
1238
1239 case FileFsLabelInformation:
1240 TRACE("FileFsLabelInformation\n");
1241
1242 Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
1243 break;
1244
1245 case FileFsObjectIdInformation:
1246 FIXME("STUB: FileFsObjectIdInformation\n");
1247 break;
1248
1249 default:
1250 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
1251 break;
1252 }
1253
1254 end:
1255 Irp->IoStatus.Status = Status;
1256 Irp->IoStatus.Information = 0;
1257
1258 IoCompleteRequest( Irp, IO_NO_INCREMENT );
1259
1260 exit:
1261 if (top_level)
1262 IoSetTopLevelIrp(NULL);
1263
1264 FsRtlExitFileSystem();
1265
1266 return Status;
1267 }
1268
1269 NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback) {
1270 KEY searchkey;
1271 traverse_ptr tp;
1272 NTSTATUS Status;
1273
1274 searchkey.obj_id = parinode;
1275 searchkey.obj_type = TYPE_DIR_ITEM;
1276 searchkey.offset = crc32;
1277
1278 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
1279 if (!NT_SUCCESS(Status)) {
1280 ERR("error - find_item returned %08x\n", Status);
1281 return Status;
1282 }
1283
1284 if (!keycmp(&searchkey, &tp.item->key)) {
1285 if (tp.item->size < sizeof(DIR_ITEM)) {
1286 WARN("(%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));
1287 } else {
1288 DIR_ITEM* di;
1289 LONG len;
1290
1291 di = (DIR_ITEM*)tp.item->data;
1292 len = tp.item->size;
1293
1294 do {
1295 if (di->n == utf8->Length && RtlCompareMemory(di->name, utf8->Buffer, di->n) == di->n) {
1296 ULONG newlen = tp.item->size - (sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m);
1297
1298 delete_tree_item(Vcb, &tp, rollback);
1299
1300 if (newlen == 0) {
1301 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1302 } else {
1303 UINT8 *newdi = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *dioff;
1304
1305 if (!newdi) {
1306 ERR("out of memory\n");
1307 return STATUS_INSUFFICIENT_RESOURCES;
1308 }
1309
1310 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1311
1312 if ((UINT8*)di > tp.item->data) {
1313 RtlCopyMemory(newdi, tp.item->data, (UINT8*)di - tp.item->data);
1314 dioff = newdi + ((UINT8*)di - tp.item->data);
1315 } else {
1316 dioff = newdi;
1317 }
1318
1319 if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
1320 RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
1321
1322 insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, rollback);
1323 }
1324
1325 break;
1326 }
1327
1328 len -= sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m;
1329 di = (DIR_ITEM*)&di->name[di->n + di->m];
1330 } while (len > 0);
1331 }
1332 } else {
1333 WARN("could not find DIR_ITEM for crc32 %08x\n", crc32);
1334 }
1335
1336 return STATUS_SUCCESS;
1337 }
1338
1339 NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, LIST_ENTRY* rollback) {
1340 KEY searchkey;
1341 traverse_ptr tp;
1342 BOOL changed = FALSE;
1343 NTSTATUS Status;
1344
1345 searchkey.obj_id = inode;
1346 searchkey.obj_type = TYPE_INODE_REF;
1347 searchkey.offset = parinode;
1348
1349 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
1350 if (!NT_SUCCESS(Status)) {
1351 ERR("error - find_item returned %08x\n", Status);
1352 return Status;
1353 }
1354
1355 if (!keycmp(&searchkey, &tp.item->key)) {
1356 if (tp.item->size < sizeof(INODE_REF)) {
1357 WARN("(%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(INODE_REF));
1358 } else {
1359 INODE_REF* ir;
1360 ULONG len;
1361
1362 ir = (INODE_REF*)tp.item->data;
1363 len = tp.item->size;
1364
1365 do {
1366 ULONG itemlen;
1367
1368 if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
1369 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1370 break;
1371 }
1372
1373 itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
1374
1375 if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
1376 ULONG newlen = tp.item->size - itemlen;
1377
1378 delete_tree_item(Vcb, &tp, rollback);
1379 changed = TRUE;
1380
1381 if (newlen == 0) {
1382 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1383 } else {
1384 UINT8 *newir = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *iroff;
1385
1386 if (!newir) {
1387 ERR("out of memory\n");
1388 return STATUS_INSUFFICIENT_RESOURCES;
1389 }
1390
1391 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1392
1393 if ((UINT8*)ir > tp.item->data) {
1394 RtlCopyMemory(newir, tp.item->data, (UINT8*)ir - tp.item->data);
1395 iroff = newir + ((UINT8*)ir - tp.item->data);
1396 } else {
1397 iroff = newir;
1398 }
1399
1400 if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
1401 RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
1402
1403 insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback);
1404 }
1405
1406 break;
1407 }
1408
1409 if (len > itemlen) {
1410 len -= itemlen;
1411 ir = (INODE_REF*)&ir->name[ir->n];
1412 } else
1413 break;
1414 } while (len > 0);
1415
1416 if (!changed) {
1417 WARN("found INODE_REF entry, but couldn't find filename\n");
1418 }
1419 }
1420 } else {
1421 WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey.obj_id, searchkey.offset);
1422 }
1423
1424 if (changed)
1425 return STATUS_SUCCESS;
1426
1427 if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF))
1428 return STATUS_INTERNAL_ERROR;
1429
1430 searchkey.obj_id = inode;
1431 searchkey.obj_type = TYPE_INODE_EXTREF;
1432 searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
1433
1434 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
1435 if (!NT_SUCCESS(Status)) {
1436 ERR("error - find_item returned %08x\n", Status);
1437 return Status;
1438 }
1439
1440 if (!keycmp(&searchkey, &tp.item->key)) {
1441 if (tp.item->size < sizeof(INODE_EXTREF)) {
1442 WARN("(%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(INODE_EXTREF));
1443 } else {
1444 INODE_EXTREF* ier;
1445 ULONG len;
1446
1447 ier = (INODE_EXTREF*)tp.item->data;
1448 len = tp.item->size;
1449
1450 do {
1451 ULONG itemlen;
1452
1453 if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
1454 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1455 break;
1456 }
1457
1458 itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
1459
1460 if (ier->dir == parinode && ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
1461 ULONG newlen = tp.item->size - itemlen;
1462
1463 delete_tree_item(Vcb, &tp, rollback);
1464 changed = TRUE;
1465
1466 if (newlen == 0) {
1467 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1468 } else {
1469 UINT8 *newier = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *ieroff;
1470
1471 if (!newier) {
1472 ERR("out of memory\n");
1473 return STATUS_INSUFFICIENT_RESOURCES;
1474 }
1475
1476 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1477
1478 if ((UINT8*)ier > tp.item->data) {
1479 RtlCopyMemory(newier, tp.item->data, (UINT8*)ier - tp.item->data);
1480 ieroff = newier + ((UINT8*)ier - tp.item->data);
1481 } else {
1482 ieroff = newier;
1483 }
1484
1485 if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
1486 RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
1487
1488 insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback);
1489 }
1490
1491 break;
1492 }
1493
1494 if (len > itemlen) {
1495 len -= itemlen;
1496 ier = (INODE_EXTREF*)&ier->name[ier->n];
1497 } else
1498 break;
1499 } while (len > 0);
1500 }
1501 } else {
1502 WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32)searchkey.offset);
1503 }
1504
1505 return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
1506 }
1507
1508 static WCHAR* file_desc_fcb(fcb* fcb) {
1509 char s[60];
1510 UNICODE_STRING us;
1511 ANSI_STRING as;
1512
1513 if (fcb->debug_desc)
1514 return fcb->debug_desc;
1515
1516 if (fcb == fcb->Vcb->volume_fcb)
1517 return L"volume FCB";
1518
1519 fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
1520 if (!fcb->debug_desc)
1521 return L"(memory error)";
1522
1523 // I know this is pretty hackish...
1524 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1525 // without the CRT, which breaks drivers.
1526
1527 sprintf(s, "subvol %x, inode %x", (UINT32)fcb->subvol->id, (UINT32)fcb->inode);
1528
1529 as.Buffer = s;
1530 as.Length = as.MaximumLength = strlen(s);
1531
1532 us.Buffer = fcb->debug_desc;
1533 us.MaximumLength = 60 * sizeof(WCHAR);
1534 us.Length = 0;
1535
1536 RtlAnsiStringToUnicodeString(&us, &as, FALSE);
1537
1538 us.Buffer[us.Length / sizeof(WCHAR)] = 0;
1539
1540 return fcb->debug_desc;
1541 }
1542
1543 WCHAR* file_desc_fileref(file_ref* fileref) {
1544 NTSTATUS Status;
1545 UNICODE_STRING fn;
1546
1547 if (fileref->debug_desc)
1548 return fileref->debug_desc;
1549
1550 Status = fileref_get_filename(fileref, &fn, NULL);
1551 if (!NT_SUCCESS(Status)) {
1552 return L"ERROR";
1553 }
1554
1555 fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fn.Length + sizeof(WCHAR), ALLOC_TAG);
1556 if (!fileref->debug_desc) {
1557 ExFreePool(fn.Buffer);
1558 return L"(memory error)";
1559 }
1560
1561 RtlCopyMemory(fileref->debug_desc, fn.Buffer, fn.Length);
1562 fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
1563
1564 ExFreePool(fn.Buffer);
1565
1566 return fileref->debug_desc;
1567 }
1568
1569 WCHAR* file_desc(PFILE_OBJECT FileObject) {
1570 fcb* fcb = FileObject->FsContext;
1571 ccb* ccb = FileObject->FsContext2;
1572 file_ref* fileref = ccb ? ccb->fileref : NULL;
1573
1574 if (fileref)
1575 return file_desc_fileref(fileref);
1576 else
1577 return file_desc_fcb(fcb);
1578 }
1579
1580 void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action) {
1581 UNICODE_STRING fn;
1582 NTSTATUS Status;
1583 USHORT name_offset;
1584 fcb* fcb = fileref->fcb;
1585
1586 Status = fileref_get_filename(fileref, &fn, &name_offset);
1587 if (!NT_SUCCESS(Status)) {
1588 ERR("fileref_get_filename returned %08x\n", Status);
1589 return;
1590 }
1591
1592 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
1593 NULL, NULL, filter_match, action, NULL, NULL);
1594 ExFreePool(fn.Buffer);
1595 }
1596
1597 void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action) {
1598 fcb* fcb = fileref->fcb;
1599 LIST_ENTRY* le;
1600 NTSTATUS Status;
1601
1602 // no point looking for hardlinks if st_nlink == 1
1603 if (fileref->fcb->inode_item.st_nlink == 1) {
1604 send_notification_fileref(fileref, filter_match, action);
1605 return;
1606 }
1607
1608 ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
1609
1610 le = fcb->hardlinks.Flink;
1611 while (le != &fcb->hardlinks) {
1612 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1613 file_ref* parfr;
1614
1615 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr);
1616
1617 if (!NT_SUCCESS(Status)) {
1618 ERR("open_fileref_by_inode returned %08x\n", Status);
1619 } else if (!parfr->deleted) {
1620 LIST_ENTRY* le2;
1621 BOOL found = FALSE, deleted = FALSE;
1622 UNICODE_STRING* fn;
1623
1624 le2 = parfr->children.Flink;
1625 while (le2 != &parfr->children) {
1626 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
1627
1628 if (fr2->index == hl->index) {
1629 found = TRUE;
1630 deleted = fr2->deleted;
1631
1632 if (!deleted)
1633 fn = &fr2->filepart;
1634
1635 break;
1636 }
1637
1638 le2 = le2->Flink;
1639 }
1640
1641 if (!found)
1642 fn = &hl->name;
1643
1644 if (!deleted) {
1645 UNICODE_STRING path;
1646
1647 Status = fileref_get_filename(parfr, &path, NULL);
1648 if (!NT_SUCCESS(Status)) {
1649 ERR("fileref_get_filename returned %08x\n", Status);
1650 } else {
1651 UNICODE_STRING fn2;
1652 ULONG name_offset;
1653
1654 name_offset = path.Length;
1655 if (parfr != fileref->fcb->Vcb->root_fileref) name_offset += sizeof(WCHAR);
1656
1657 fn2.Length = fn2.MaximumLength = fn->Length + name_offset;
1658 fn2.Buffer = ExAllocatePoolWithTag(PagedPool, fn2.MaximumLength, ALLOC_TAG);
1659
1660 RtlCopyMemory(fn2.Buffer, path.Buffer, path.Length);
1661 if (parfr != fileref->fcb->Vcb->root_fileref) fn2.Buffer[path.Length / sizeof(WCHAR)] = '\\';
1662 RtlCopyMemory(&fn2.Buffer[name_offset / sizeof(WCHAR)], fn->Buffer, fn->Length);
1663
1664 TRACE("%.*S\n", fn2.Length / sizeof(WCHAR), fn2.Buffer);
1665
1666 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn2, name_offset,
1667 NULL, NULL, filter_match, action, NULL, NULL);
1668
1669 ExFreePool(fn2.Buffer);
1670 ExFreePool(path.Buffer);
1671 }
1672 }
1673
1674 free_fileref(parfr);
1675 }
1676
1677 le = le->Flink;
1678 }
1679
1680 ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
1681 }
1682
1683 void mark_fcb_dirty(fcb* fcb) {
1684 if (!fcb->dirty) {
1685 #ifdef DEBUG_FCB_REFCOUNTS
1686 LONG rc;
1687 #endif
1688 dirty_fcb* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fcb), ALLOC_TAG);
1689
1690 if (!dirt) {
1691 ExFreePool("out of memory\n");
1692 return;
1693 }
1694
1695 fcb->dirty = TRUE;
1696
1697 #ifdef DEBUG_FCB_REFCOUNTS
1698 rc = InterlockedIncrement(&fcb->refcount);
1699 WARN("fcb %p: refcount now %i\n", fcb, rc);
1700 #else
1701 InterlockedIncrement(&fcb->refcount);
1702 #endif
1703
1704 dirt->fcb = fcb;
1705
1706 ExInterlockedInsertTailList(&fcb->Vcb->dirty_fcbs, &dirt->list_entry, &fcb->Vcb->dirty_fcbs_lock);
1707 }
1708
1709 fcb->Vcb->need_write = TRUE;
1710 }
1711
1712 void mark_fileref_dirty(file_ref* fileref) {
1713 if (!fileref->dirty) {
1714 dirty_fileref* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fileref), ALLOC_TAG);
1715
1716 if (!dirt) {
1717 ExFreePool("out of memory\n");
1718 return;
1719 }
1720
1721 fileref->dirty = TRUE;
1722 increase_fileref_refcount(fileref);
1723
1724 dirt->fileref = fileref;
1725
1726 ExInterlockedInsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &dirt->list_entry, &fileref->fcb->Vcb->dirty_filerefs_lock);
1727 }
1728
1729 fileref->fcb->Vcb->need_write = TRUE;
1730 }
1731
1732 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
1733 LONG rc;
1734
1735 rc = InterlockedDecrement(&fcb->refcount);
1736
1737 #ifdef DEBUG_FCB_REFCOUNTS
1738 // WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1739 #ifdef DEBUG_LONG_MESSAGES
1740 _debug_message(func, file, line, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1741 #else
1742 _debug_message(func, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1743 #endif
1744 #endif
1745
1746 if (rc > 0)
1747 return;
1748
1749 ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
1750
1751 if (fcb->list_entry.Flink)
1752 RemoveEntryList(&fcb->list_entry);
1753
1754 if (fcb->list_entry_all.Flink)
1755 RemoveEntryList(&fcb->list_entry_all);
1756
1757 ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
1758
1759 ExDeleteResourceLite(&fcb->nonpaged->resource);
1760 ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
1761 ExDeleteResourceLite(&fcb->nonpaged->index_lock);
1762 ExFreePool(fcb->nonpaged);
1763
1764 if (fcb->sd)
1765 ExFreePool(fcb->sd);
1766
1767 if (fcb->adsxattr.Buffer)
1768 ExFreePool(fcb->adsxattr.Buffer);
1769
1770 if (fcb->reparse_xattr.Buffer)
1771 ExFreePool(fcb->reparse_xattr.Buffer);
1772
1773 if (fcb->adsdata.Buffer)
1774 ExFreePool(fcb->adsdata.Buffer);
1775
1776 if (fcb->debug_desc)
1777 ExFreePool(fcb->debug_desc);
1778
1779 while (!IsListEmpty(&fcb->extents)) {
1780 LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
1781 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1782
1783 ExFreePool(ext->data);
1784 ExFreePool(ext);
1785 }
1786
1787 while (!IsListEmpty(&fcb->index_list)) {
1788 LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
1789 index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
1790
1791 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
1792 if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
1793 ExFreePool(ie);
1794 }
1795
1796 while (!IsListEmpty(&fcb->hardlinks)) {
1797 LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
1798 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1799
1800 if (hl->name.Buffer)
1801 ExFreePool(hl->name.Buffer);
1802
1803 if (hl->utf8.Buffer)
1804 ExFreePool(hl->utf8.Buffer);
1805
1806 ExFreePool(hl);
1807 }
1808
1809 FsRtlUninitializeFileLock(&fcb->lock);
1810
1811 ExFreePool(fcb);
1812 #ifdef DEBUG_FCB_REFCOUNTS
1813 #ifdef DEBUG_LONG_MESSAGES
1814 _debug_message(func, file, line, "freeing fcb %p\n", fcb);
1815 #else
1816 _debug_message(func, "freeing fcb %p\n", fcb);
1817 #endif
1818 #endif
1819 }
1820
1821 void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
1822 LONG rc;
1823
1824 rc = InterlockedDecrement(&fr->refcount);
1825
1826 #ifdef DEBUG_FCB_REFCOUNTS
1827 #ifdef DEBUG_LONG_MESSAGES
1828 _debug_message(func, file, line, "fileref %p: refcount now %i\n", fr, rc);
1829 #else
1830 _debug_message(func, "fileref %p: refcount now %i\n", fr, rc);
1831 #endif
1832 #endif
1833
1834 #ifdef _DEBUG
1835 if (rc < 0) {
1836 ERR("fileref %p: refcount now %i\n", fr, rc);
1837 int3;
1838 }
1839 #endif
1840
1841 if (rc > 0)
1842 return;
1843
1844 if (fr->parent)
1845 ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE);
1846
1847 // FIXME - do we need a file_ref lock?
1848
1849 // FIXME - do delete if needed
1850
1851 if (fr->filepart.Buffer)
1852 ExFreePool(fr->filepart.Buffer);
1853
1854 if (fr->filepart_uc.Buffer)
1855 ExFreePool(fr->filepart_uc.Buffer);
1856
1857 if (fr->utf8.Buffer)
1858 ExFreePool(fr->utf8.Buffer);
1859
1860 if (fr->debug_desc)
1861 ExFreePool(fr->debug_desc);
1862
1863 ExDeleteResourceLite(&fr->nonpaged->children_lock);
1864
1865 ExFreePool(fr->nonpaged);
1866
1867 // FIXME - throw error if children not empty
1868
1869 if (fr->fcb->fileref == fr)
1870 fr->fcb->fileref = NULL;
1871
1872 if (fr->list_entry.Flink)
1873 RemoveEntryList(&fr->list_entry);
1874
1875 if (fr->parent) {
1876 ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
1877 free_fileref(fr->parent);
1878 }
1879
1880 free_fcb(fr->fcb);
1881 ExFreePool(fr);
1882 }
1883
1884 static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
1885 fcb* fcb;
1886 ccb* ccb;
1887 file_ref* fileref = NULL;
1888
1889 TRACE("FileObject = %p\n", FileObject);
1890
1891 fcb = FileObject->FsContext;
1892 if (!fcb) {
1893 TRACE("FCB was NULL, returning success\n");
1894 return STATUS_SUCCESS;
1895 }
1896
1897 ccb = FileObject->FsContext2;
1898
1899 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
1900
1901 // FIXME - make sure notification gets sent if file is being deleted
1902
1903 if (ccb) {
1904 if (ccb->query_string.Buffer)
1905 RtlFreeUnicodeString(&ccb->query_string);
1906
1907 if (ccb->filename.Buffer)
1908 ExFreePool(ccb->filename.Buffer);
1909
1910 // FIXME - use refcounts for fileref
1911 fileref = ccb->fileref;
1912
1913 ExFreePool(ccb);
1914 }
1915
1916 CcUninitializeCacheMap(FileObject, NULL, NULL);
1917
1918 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1919
1920 if (fileref)
1921 free_fileref(fileref);
1922 else
1923 free_fcb(fcb);
1924
1925 ExReleaseResourceLite(&Vcb->fcb_lock);
1926
1927 return STATUS_SUCCESS;
1928 }
1929
1930 void STDCALL uninit(device_extension* Vcb, BOOL flush) {
1931 space* s;
1932 UINT64 i;
1933 LIST_ENTRY rollback;
1934 NTSTATUS Status;
1935 LIST_ENTRY* le;
1936 LARGE_INTEGER time;
1937
1938 #ifndef __REACTOS__
1939 RemoveEntryList(&Vcb->list_entry);
1940 #endif
1941
1942 Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
1943 if (!NT_SUCCESS(Status))
1944 WARN("registry_mark_volume_unmounted returned %08x\n", Status);
1945
1946 if (flush) {
1947 InitializeListHead(&rollback);
1948
1949 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1950
1951 if (Vcb->need_write)
1952 do_write(Vcb, &rollback);
1953
1954 free_trees(Vcb);
1955
1956 clear_rollback(&rollback);
1957
1958 ExReleaseResourceLite(&Vcb->tree_lock);
1959 }
1960
1961 for (i = 0; i < Vcb->threads.num_threads; i++) {
1962 Vcb->threads.threads[i].quit = TRUE;
1963 KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
1964
1965 KeWaitForSingleObject(&Vcb->threads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
1966
1967 ZwClose(Vcb->threads.threads[i].handle);
1968 }
1969
1970 ExFreePool(Vcb->threads.threads);
1971
1972 Vcb->removing = TRUE;
1973
1974 time.QuadPart = 0;
1975 KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
1976 KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
1977
1978 free_fcb(Vcb->volume_fcb);
1979
1980 if (Vcb->root_file)
1981 ObDereferenceObject(Vcb->root_file);
1982
1983 le = Vcb->chunks.Flink;
1984 while (le != &Vcb->chunks) {
1985 chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
1986
1987 if (c->cache) {
1988 free_fcb(c->cache);
1989 c->cache = NULL;
1990 }
1991
1992 le = le->Flink;
1993 }
1994
1995 while (!IsListEmpty(&Vcb->roots)) {
1996 LIST_ENTRY* le = RemoveHeadList(&Vcb->roots);
1997 root* r = CONTAINING_RECORD(le, root, list_entry);
1998
1999 ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
2000 ExFreePool(r->nonpaged);
2001 ExFreePool(r);
2002 }
2003
2004 while (!IsListEmpty(&Vcb->chunks)) {
2005 chunk* c;
2006
2007 le = RemoveHeadList(&Vcb->chunks);
2008 c = CONTAINING_RECORD(le, chunk, list_entry);
2009
2010 while (!IsListEmpty(&c->space)) {
2011 LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2012 s = CONTAINING_RECORD(le2, space, list_entry);
2013
2014 ExFreePool(s);
2015 }
2016
2017 while (!IsListEmpty(&c->deleting)) {
2018 LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
2019 s = CONTAINING_RECORD(le2, space, list_entry);
2020
2021 ExFreePool(s);
2022 }
2023
2024 if (c->devices)
2025 ExFreePool(c->devices);
2026
2027 if (c->cache)
2028 free_fcb(c->cache);
2029
2030 ExDeleteResourceLite(&c->nonpaged->lock);
2031 ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
2032
2033 ExFreePool(c->nonpaged);
2034 ExFreePool(c->chunk_item);
2035 ExFreePool(c);
2036 }
2037
2038 // FIXME - free any open fcbs?
2039
2040 while (!IsListEmpty(&Vcb->sector_checksums)) {
2041 LIST_ENTRY* le = RemoveHeadList(&Vcb->sector_checksums);
2042 changed_sector* cs = (changed_sector*)le;
2043
2044 ExFreePool(cs);
2045 }
2046
2047 for (i = 0; i < Vcb->superblock.num_devices; i++) {
2048 while (!IsListEmpty(&Vcb->devices[i].space)) {
2049 LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].space);
2050 space* s = CONTAINING_RECORD(le, space, list_entry);
2051
2052 ExFreePool(s);
2053 }
2054 }
2055
2056 ExFreePool(Vcb->devices);
2057
2058 ExDeleteResourceLite(&Vcb->fcb_lock);
2059 ExDeleteResourceLite(&Vcb->load_lock);
2060 ExDeleteResourceLite(&Vcb->tree_lock);
2061 ExDeleteResourceLite(&Vcb->checksum_lock);
2062 ExDeleteResourceLite(&Vcb->chunk_lock);
2063
2064 ZwClose(Vcb->flush_thread_handle);
2065 }
2066
2067 NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
2068 LARGE_INTEGER newlength, time;
2069 BTRFS_TIME now;
2070 NTSTATUS Status;
2071
2072 KeQuerySystemTime(&time);
2073 win_time_to_unix(time, &now);
2074
2075 ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
2076
2077 if (fileref->deleted) {
2078 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2079 return STATUS_SUCCESS;
2080 }
2081
2082 fileref->deleted = TRUE;
2083 mark_fileref_dirty(fileref);
2084
2085 // delete INODE_ITEM (0x1)
2086
2087 TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
2088
2089 if (!fileref->fcb->ads) {
2090 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
2091 LIST_ENTRY* le;
2092
2093 mark_fcb_dirty(fileref->fcb);
2094
2095 if (fileref->fcb->inode_item.st_nlink > 1) {
2096 fileref->fcb->inode_item.st_nlink--;
2097 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2098 fileref->fcb->inode_item.sequence++;
2099 fileref->fcb->inode_item.st_ctime = now;
2100 } else {
2101 fileref->fcb->deleted = TRUE;
2102
2103 // excise extents
2104
2105 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
2106 Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback);
2107 if (!NT_SUCCESS(Status)) {
2108 ERR("excise_extents returned %08x\n", Status);
2109 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2110 return Status;
2111 }
2112 }
2113
2114 fileref->fcb->Header.AllocationSize.QuadPart = 0;
2115 fileref->fcb->Header.FileSize.QuadPart = 0;
2116 fileref->fcb->Header.ValidDataLength.QuadPart = 0;
2117
2118 if (FileObject) {
2119 CC_FILE_SIZES ccfs;
2120
2121 ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
2122 ccfs.FileSize = fileref->fcb->Header.FileSize;
2123 ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
2124
2125 CcSetFileSizes(FileObject, &ccfs);
2126 }
2127 }
2128
2129 le = fileref->fcb->hardlinks.Flink;
2130 while (le != &fileref->fcb->hardlinks) {
2131 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
2132
2133 if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->index) {
2134 RemoveEntryList(&hl->list_entry);
2135
2136 if (hl->name.Buffer)
2137 ExFreePool(hl->name.Buffer);
2138
2139 if (hl->utf8.Buffer)
2140 ExFreePool(hl->utf8.Buffer);
2141
2142 ExFreePool(hl);
2143 break;
2144 }
2145
2146 le = le->Flink;
2147 }
2148 } else { // subvolume
2149 if (fileref->fcb->subvol->root_item.num_references > 1) {
2150 fileref->fcb->subvol->root_item.num_references--;
2151
2152 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2153 } else {
2154 // FIXME - we need a lock here
2155
2156 RemoveEntryList(&fileref->fcb->subvol->list_entry);
2157
2158 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2159 }
2160 }
2161 } else {
2162 fileref->fcb->deleted = TRUE;
2163 mark_fcb_dirty(fileref->fcb);
2164 }
2165
2166 // update INODE_ITEM of parent
2167
2168 TRACE("delete file %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer);
2169 ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE);
2170 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2171 fileref->parent->fcb->inode_item.st_size -= fileref->utf8.Length * 2;
2172 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2173 fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2174 fileref->parent->fcb->inode_item.sequence++;
2175 fileref->parent->fcb->inode_item.st_ctime = now;
2176 fileref->parent->fcb->inode_item.st_mtime = now;
2177 ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2178
2179 mark_fcb_dirty(fileref->parent->fcb);
2180
2181 send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
2182
2183 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2184 fileref->fcb->subvol->root_item.ctime = now;
2185
2186 if (FileObject && FileObject->Flags & FO_CACHE_SUPPORTED && fileref->fcb->nonpaged->segment_object.DataSectionObject)
2187 CcPurgeCacheSection(&fileref->fcb->nonpaged->segment_object, NULL, 0, FALSE);
2188
2189 newlength.QuadPart = 0;
2190
2191 if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2192 TRACE("CcUninitializeCacheMap failed\n");
2193
2194 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2195
2196 return STATUS_SUCCESS;
2197 }
2198
2199 static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2200 NTSTATUS Status;
2201 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2202 PFILE_OBJECT FileObject = IrpSp->FileObject;
2203 device_extension* Vcb = DeviceObject->DeviceExtension;
2204 fcb* fcb;
2205 BOOL top_level;
2206
2207 TRACE("cleanup\n");
2208
2209 FsRtlEnterFileSystem();
2210
2211 top_level = is_top_level(Irp);
2212
2213 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
2214 Status = part0_passthrough(DeviceObject, Irp);
2215 goto exit2;
2216 }
2217
2218 if (DeviceObject == devobj) {
2219 TRACE("closing file system\n");
2220 Status = STATUS_SUCCESS;
2221 goto exit;
2222 }
2223
2224 if (FileObject && FileObject->FsContext) {
2225 LONG oc;
2226 ccb* ccb;
2227 file_ref* fileref;
2228
2229 fcb = FileObject->FsContext;
2230 ccb = FileObject->FsContext2;
2231 fileref = ccb ? ccb->fileref : NULL;
2232
2233 TRACE("cleanup called for FileObject %p\n", FileObject);
2234 TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb, file_desc(FileObject), fcb->refcount, fcb->open_count);
2235
2236 IoRemoveShareAccess(FileObject, &fcb->share_access);
2237
2238 FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
2239
2240 oc = InterlockedDecrement(&fcb->open_count);
2241 #ifdef DEBUG_FCB_REFCOUNTS
2242 ERR("fcb %p: open_count now %i\n", fcb, oc);
2243 #endif
2244
2245 if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2246 fileref->delete_on_close = TRUE;
2247
2248 if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
2249 fileref->delete_on_close = FALSE;
2250
2251 if (Vcb->locked && Vcb->locked_fileobj == FileObject) {
2252 TRACE("unlocking volume\n");
2253 do_unlock_volume(Vcb);
2254 FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
2255 }
2256
2257 if (oc == 0) {
2258 if (!Vcb->removing) {
2259 LIST_ENTRY rollback;
2260
2261 InitializeListHead(&rollback);
2262
2263 if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2264 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
2265
2266 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
2267
2268 Status = delete_fileref(fileref, FileObject, &rollback);
2269 if (!NT_SUCCESS(Status)) {
2270 ERR("delete_fileref returned %08x\n", Status);
2271 do_rollback(Vcb, &rollback);
2272 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2273 goto exit;
2274 }
2275
2276 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2277 clear_rollback(&rollback);
2278 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
2279 IO_STATUS_BLOCK iosb;
2280 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2281
2282 if (!NT_SUCCESS(iosb.Status)) {
2283 ERR("CcFlushCache returned %08x\n", iosb.Status);
2284 }
2285
2286 if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2287 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
2288 ExReleaseResourceLite(fcb->Header.PagingIoResource);
2289 }
2290
2291 CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
2292
2293 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2294 FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2295 }
2296 }
2297
2298 if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2299 CcUninitializeCacheMap(FileObject, NULL, NULL);
2300 }
2301
2302 FileObject->Flags |= FO_CLEANUP_COMPLETE;
2303 }
2304
2305 Status = STATUS_SUCCESS;
2306
2307 exit:
2308 Irp->IoStatus.Status = Status;
2309 Irp->IoStatus.Information = 0;
2310
2311 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2312
2313 exit2:
2314 if (top_level)
2315 IoSetTopLevelIrp(NULL);
2316
2317 FsRtlExitFileSystem();
2318
2319 return Status;
2320 }
2321
2322 ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
2323 ULONG att;
2324 char* eaval;
2325 UINT16 ealen;
2326
2327 // ii can be NULL
2328
2329 if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
2330 if (ealen > 2) {
2331 if (eaval[0] == '0' && eaval[1] == 'x') {
2332 int i;
2333 ULONG dosnum = 0;
2334
2335 for (i = 2; i < ealen; i++) {
2336 dosnum *= 0x10;
2337
2338 if (eaval[i] >= '0' && eaval[i] <= '9')
2339 dosnum |= eaval[i] - '0';
2340 else if (eaval[i] >= 'a' && eaval[i] <= 'f')
2341 dosnum |= eaval[i] + 10 - 'a';
2342 else if (eaval[i] >= 'A' && eaval[i] <= 'F')
2343 dosnum |= eaval[i] + 10 - 'a';
2344 }
2345
2346 TRACE("DOSATTRIB: %08x\n", dosnum);
2347
2348 ExFreePool(eaval);
2349
2350 if (type == BTRFS_TYPE_DIRECTORY)
2351 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2352 else if (type == BTRFS_TYPE_SYMLINK)
2353 dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2354
2355 return dosnum;
2356 }
2357 }
2358
2359 ExFreePool(eaval);
2360 }
2361
2362 switch (type) {
2363 case BTRFS_TYPE_DIRECTORY:
2364 att = FILE_ATTRIBUTE_DIRECTORY;
2365 break;
2366
2367 case BTRFS_TYPE_SYMLINK:
2368 att = FILE_ATTRIBUTE_REPARSE_POINT;
2369 break;
2370
2371 default:
2372 att = 0;
2373 break;
2374 }
2375
2376 if (dotfile) {
2377 att |= FILE_ATTRIBUTE_HIDDEN;
2378 }
2379
2380 att |= FILE_ATTRIBUTE_ARCHIVE;
2381
2382 // FIXME - get READONLY from ii->st_mode
2383 // FIXME - return SYSTEM for block/char devices?
2384
2385 if (att == 0)
2386 att = FILE_ATTRIBUTE_NORMAL;
2387
2388 return att;
2389 }
2390
2391 NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
2392 IO_STATUS_BLOCK* IoStatus;
2393 LARGE_INTEGER Offset;
2394 PIRP Irp;
2395 PIO_STACK_LOCATION IrpSp;
2396 NTSTATUS Status;
2397 read_context* context;
2398
2399 num_reads++;
2400
2401 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
2402 if (!context) {
2403 ERR("out of memory\n");
2404 return STATUS_INSUFFICIENT_RESOURCES;
2405 }
2406
2407 RtlZeroMemory(context, sizeof(read_context));
2408 KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
2409
2410 IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
2411 if (!IoStatus) {
2412 ERR("out of memory\n");
2413 ExFreePool(context);
2414 return STATUS_INSUFFICIENT_RESOURCES;
2415 }
2416
2417 Offset.QuadPart = StartingOffset;
2418
2419 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2420 Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
2421
2422 if (!Irp) {
2423 ERR("IoAllocateIrp failed\n");
2424 Status = STATUS_INSUFFICIENT_RESOURCES;
2425 goto exit;
2426 }
2427
2428 IrpSp = IoGetNextIrpStackLocation(Irp);
2429 IrpSp->MajorFunction = IRP_MJ_READ;
2430
2431 if (DeviceObject->Flags & DO_BUFFERED_IO) {
2432 FIXME("FIXME - buffered IO\n");
2433 } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2434 // TRACE("direct IO\n");
2435
2436 Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
2437 if (!Irp->MdlAddress) {
2438 ERR("IoAllocateMdl failed\n");
2439 Status = STATUS_INSUFFICIENT_RESOURCES;
2440 // IoFreeIrp(Irp);
2441 goto exit;
2442 // } else {
2443 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2444 }
2445
2446 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
2447 } else {
2448 // TRACE("neither buffered nor direct IO\n");
2449 Irp->UserBuffer = Buffer;
2450 }
2451
2452 IrpSp->Parameters.Read.Length = Length;
2453 IrpSp->Parameters.Read.ByteOffset = Offset;
2454
2455 Irp->UserIosb = IoStatus;
2456 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2457
2458 Irp->UserEvent = &context->Event;
2459
2460 // IoQueueThreadIrp(Irp);
2461
2462 IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
2463
2464 // if (Override)
2465 // {
2466 // Stack = IoGetNextIrpStackLocation(Irp);
2467 // Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2468 // }
2469
2470 // TRACE("Calling IO Driver... with irp %p\n", Irp);
2471 Status = IoCallDriver(DeviceObject, Irp);
2472
2473 // TRACE("Waiting for IO Operation for %p\n", Irp);
2474 if (Status == STATUS_PENDING) {
2475 // TRACE("Operation pending\n");
2476 KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
2477 // TRACE("Getting IO Status... for %p\n", Irp);
2478 Status = context->iosb.Status;
2479 }
2480
2481 if (DeviceObject->Flags & DO_DIRECT_IO) {
2482 MmUnlockPages(Irp->MdlAddress);
2483 IoFreeMdl(Irp->MdlAddress);
2484 }
2485
2486 exit:
2487 IoFreeIrp(Irp);
2488
2489 ExFreePool(IoStatus);
2490 ExFreePool(context);
2491
2492 return Status;
2493 }
2494
2495 static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device, UINT64 length) {
2496 NTSTATUS Status;
2497 superblock* sb;
2498 unsigned int i, to_read;
2499 UINT32 crc32;
2500
2501 to_read = sector_align(sizeof(superblock), device->SectorSize);
2502
2503 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
2504 if (!sb) {
2505 ERR("out of memory\n");
2506 return STATUS_INSUFFICIENT_RESOURCES;
2507 }
2508
2509 i = 0;
2510
2511 while (superblock_addrs[i] > 0) {
2512 if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
2513 break;
2514
2515 Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
2516 if (!NT_SUCCESS(Status)) {
2517 ERR("Failed to read superblock %u: %08x\n", i, Status);
2518 ExFreePool(sb);
2519 return Status;
2520 }
2521
2522 // FIXME - check checksum before accepting?
2523
2524 TRACE("got superblock %u!\n", i);
2525
2526 if (i == 0 || sb->generation > Vcb->superblock.generation)
2527 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2528
2529 i++;
2530 }
2531
2532 ExFreePool(sb);
2533
2534 crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
2535 crc32 = ~crc32;
2536 TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
2537
2538 if (crc32 != *((UINT32*)Vcb->superblock.checksum))
2539 return STATUS_INTERNAL_ERROR; // FIXME - correct error?
2540
2541 TRACE("label is %s\n", Vcb->superblock.label);
2542 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2543
2544 return STATUS_SUCCESS;
2545 }
2546
2547 NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer, ULONG InputBufferSize,
2548 PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb)
2549 {
2550 PIRP Irp;
2551 KEVENT Event;
2552 NTSTATUS Status;
2553 PIO_STACK_LOCATION Stack;
2554 IO_STATUS_BLOCK IoStatus;
2555
2556 KeInitializeEvent(&Event, NotificationEvent, FALSE);
2557
2558 Irp = IoBuildDeviceIoControlRequest(ControlCode,
2559 DeviceObject,
2560 InputBuffer,
2561 InputBufferSize,
2562 OutputBuffer,
2563 OutputBufferSize,
2564 FALSE,
2565 &Event,
2566 &IoStatus);
2567
2568 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2569
2570 if (Override) {
2571 Stack = IoGetNextIrpStackLocation(Irp);
2572 Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2573 }
2574
2575 Status = IoCallDriver(DeviceObject, Irp);
2576
2577 if (Status == STATUS_PENDING) {
2578 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2579 Status = IoStatus.Status;
2580 }
2581
2582 if (iosb)
2583 *iosb = IoStatus;
2584
2585 return Status;
2586 }
2587
2588 static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
2589 root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
2590 if (!r) {
2591 ERR("out of memory\n");
2592 return STATUS_INSUFFICIENT_RESOURCES;
2593 }
2594
2595 r->id = id;
2596 r->path.Buffer = NULL;
2597 r->treeholder.address = addr;
2598 r->treeholder.tree = NULL;
2599 InitializeListHead(&r->fcbs);
2600
2601 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
2602 if (!r->nonpaged) {
2603 ERR("out of memory\n");
2604 ExFreePool(r);
2605 return STATUS_INSUFFICIENT_RESOURCES;
2606 }
2607
2608 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
2609
2610 r->lastinode = 0;
2611
2612 if (tp) {
2613 RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
2614 if (tp->item->size < sizeof(ROOT_ITEM))
2615 RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
2616 }
2617
2618 InsertTailList(&Vcb->roots, &r->list_entry);
2619
2620 switch (r->id) {
2621 case BTRFS_ROOT_ROOT:
2622 Vcb->root_root = r;
2623 break;
2624
2625 case BTRFS_ROOT_EXTENT:
2626 Vcb->extent_root = r;
2627 break;
2628
2629 case BTRFS_ROOT_CHUNK:
2630 Vcb->chunk_root = r;
2631 break;
2632
2633 case BTRFS_ROOT_DEVTREE:
2634 Vcb->dev_root = r;
2635 break;
2636
2637 case BTRFS_ROOT_CHECKSUM:
2638 Vcb->checksum_root = r;
2639 break;
2640
2641 case BTRFS_ROOT_UUID:
2642 Vcb->uuid_root = r;
2643 break;
2644 }
2645
2646 return STATUS_SUCCESS;
2647 }
2648
2649 static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
2650 traverse_ptr tp, next_tp;
2651 KEY searchkey;
2652 BOOL b;
2653 NTSTATUS Status;
2654
2655 searchkey.obj_id = 0;
2656 searchkey.obj_type = 0;
2657 searchkey.offset = 0;
2658
2659 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
2660 if (!NT_SUCCESS(Status)) {
2661 ERR("error - find_tree returned %08x\n", Status);
2662 return Status;
2663 }
2664
2665 do {
2666 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2667
2668 if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
2669 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2670
2671 if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
2672 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));
2673 } else {
2674 TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
2675
2676 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
2677 if (!NT_SUCCESS(Status)) {
2678 ERR("add_root returned %08x\n", Status);
2679 return Status;
2680 }
2681 }
2682 }
2683
2684 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
2685
2686 if (b)
2687 tp = next_tp;
2688 } while (b);
2689
2690 return STATUS_SUCCESS;
2691 }
2692
2693 static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
2694 KEY searchkey;
2695 traverse_ptr tp, next_tp;
2696 BOOL b;
2697 UINT64 lastaddr;
2698 NTSTATUS Status;
2699
2700 InitializeListHead(&dev->space);
2701
2702 searchkey.obj_id = dev->devitem.dev_id;
2703 searchkey.obj_type = TYPE_DEV_EXTENT;
2704 searchkey.offset = 0;
2705
2706 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
2707 if (!NT_SUCCESS(Status)) {
2708 ERR("error - find_tree returned %08x\n", Status);
2709 return Status;
2710 }
2711
2712 lastaddr = 0;
2713
2714 do {
2715 if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
2716 if (tp.item->size >= sizeof(DEV_EXTENT)) {
2717 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
2718
2719 if (tp.item->key.offset > lastaddr) {
2720 Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
2721 if (!NT_SUCCESS(Status)) {
2722 ERR("add_space_entry returned %08x\n", Status);
2723 return Status;
2724 }
2725 }
2726
2727 lastaddr = tp.item->key.offset + de->length;
2728 } else {
2729 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));
2730 }
2731 }
2732
2733 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
2734
2735 if (b) {
2736 tp = next_tp;
2737 if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
2738 break;
2739 }
2740 } while (b);
2741
2742 if (lastaddr < dev->devitem.num_bytes) {
2743 Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
2744 if (!NT_SUCCESS(Status)) {
2745 ERR("add_space_entry returned %08x\n", Status);
2746 return Status;
2747 }
2748 }
2749
2750 return STATUS_SUCCESS;
2751 }
2752
2753 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
2754 UINT64 i;
2755
2756 for (i = 0; i < Vcb->devices_loaded; i++) {
2757 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
2758 Vcb->devices[i].devitem.device_uuid.uuid[0], Vcb->devices[i].devitem.device_uuid.uuid[1], Vcb->devices[i].devitem.device_uuid.uuid[2], Vcb->devices[i].devitem.device_uuid.uuid[3], Vcb->devices[i].devitem.device_uuid.uuid[4], Vcb->devices[i].devitem.device_uuid.uuid[5], Vcb->devices[i].devitem.device_uuid.uuid[6], Vcb->devices[i].devitem.device_uuid.uuid[7],
2759 Vcb->devices[i].devitem.device_uuid.uuid[8], Vcb->devices[i].devitem.device_uuid.uuid[9], Vcb->devices[i].devitem.device_uuid.uuid[10], Vcb->devices[i].devitem.device_uuid.uuid[11], Vcb->devices[i].devitem.device_uuid.uuid[12], Vcb->devices[i].devitem.device_uuid.uuid[13], Vcb->devices[i].devitem.device_uuid.uuid[14], Vcb->devices[i].devitem.device_uuid.uuid[15]);
2760
2761 if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2762 TRACE("returning device %llx\n", i);
2763 return &Vcb->devices[i];
2764 }
2765 }
2766
2767 if (Vcb->devices_loaded < Vcb->superblock.num_devices && !IsListEmpty(&volumes)) {
2768 LIST_ENTRY* le = volumes.Flink;
2769
2770 while (le != &volumes) {
2771 volume* v = CONTAINING_RECORD(le, volume, list_entry);
2772
2773 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
2774 RtlCompareMemory(uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
2775 ) {
2776 NTSTATUS Status;
2777 PFILE_OBJECT FileObject;
2778 PDEVICE_OBJECT DeviceObject;
2779
2780 Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
2781 if (!NT_SUCCESS(Status)) {
2782 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
2783 return NULL;
2784 }
2785
2786 DeviceObject = FileObject->DeviceObject;
2787
2788 ObReferenceObject(DeviceObject);
2789 ObDereferenceObject(FileObject);
2790
2791 Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
2792 Vcb->devices[Vcb->devices_loaded].devitem.device_uuid = *uuid;
2793 Vcb->devices_loaded++;
2794
2795 return &Vcb->devices[Vcb->devices_loaded - 1];
2796 }
2797
2798 le = le->Flink;
2799 }
2800 }
2801
2802 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2803 uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
2804 uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
2805
2806 return NULL;
2807 }
2808
2809 static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
2810 NTSTATUS Status;
2811 STORAGE_HOTPLUG_INFO shi;
2812
2813 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
2814
2815 if (!NT_SUCCESS(Status)) {
2816 ERR("dev_ioctl returned %08x\n", Status);
2817 return FALSE;
2818 }
2819
2820 return shi.MediaRemovable != 0 ? TRUE : FALSE;
2821 }
2822
2823 static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
2824 NTSTATUS Status;
2825 ULONG cc;
2826 IO_STATUS_BLOCK iosb;
2827
2828 Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
2829
2830 if (!NT_SUCCESS(Status)) {
2831 ERR("dev_ioctl returned %08x\n", Status);
2832 return 0;
2833 }
2834
2835 if (iosb.Information < sizeof(ULONG)) {
2836 ERR("iosb.Information was too short\n");
2837 return 0;
2838 }
2839
2840 return cc;
2841 }
2842
2843 static void init_device(device_extension* Vcb, device* dev, BOOL get_length) {
2844 NTSTATUS Status;
2845 GET_LENGTH_INFORMATION gli;
2846
2847 dev->removable = is_device_removable(dev->devobj);
2848 dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
2849
2850 if (get_length) {
2851 Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
2852 &gli, sizeof(gli), TRUE, NULL);
2853 if (!NT_SUCCESS(Status)) {
2854 ERR("error reading length information: %08x\n", Status);
2855 }
2856
2857 dev->length = gli.Length.QuadPart;
2858 }
2859 }
2860
2861 static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
2862 traverse_ptr tp, next_tp;
2863 KEY searchkey;
2864 BOOL b;
2865 chunk* c;
2866 UINT64 i;
2867 NTSTATUS Status;
2868
2869 searchkey.obj_id = 0;
2870 searchkey.obj_type = 0;
2871 searchkey.offset = 0;
2872
2873 Vcb->data_flags = 0;
2874
2875 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
2876 if (!NT_SUCCESS(Status)) {
2877 ERR("error - find_item returned %08x\n", Status);
2878 return Status;
2879 }
2880
2881 do {
2882 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2883
2884 if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
2885 if (tp.item->size < sizeof(DEV_ITEM)) {
2886 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));
2887 } else {
2888 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
2889 BOOL done = FALSE;
2890
2891 for (i = 0; i < Vcb->devices_loaded; i++) {
2892 if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2893 RtlCopyMemory(&Vcb->devices[i].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
2894
2895 if (i > 0)
2896 init_device(Vcb, &Vcb->devices[i], TRUE);
2897
2898 done = TRUE;
2899 break;
2900 }
2901 }
2902
2903 if (!done) {
2904 if (!IsListEmpty(&volumes) && Vcb->devices_loaded < Vcb->superblock.num_devices) {
2905 LIST_ENTRY* le = volumes.Flink;
2906
2907 while (le != &volumes) {
2908 volume* v = CONTAINING_RECORD(le, volume, list_entry);
2909
2910 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
2911 RtlCompareMemory(&di->device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
2912 ) {
2913 PFILE_OBJECT FileObject;
2914 PDEVICE_OBJECT DeviceObject;
2915
2916 Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_DATA | FILE_WRITE_DATA, &FileObject, &DeviceObject);
2917 if (!NT_SUCCESS(Status)) {
2918 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
2919 return Status;
2920 }
2921
2922 DeviceObject = FileObject->DeviceObject;
2923
2924 ObReferenceObject(DeviceObject);
2925 ObDereferenceObject(FileObject);
2926
2927 Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
2928 RtlCopyMemory(&Vcb->devices[Vcb->devices_loaded].devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
2929 init_device(Vcb, &Vcb->devices[i], FALSE);
2930 Vcb->devices[i].length = v->length;
2931 Vcb->devices_loaded++;
2932
2933 done = TRUE;
2934 break;
2935 }
2936
2937 le = le->Flink;
2938 }
2939
2940 if (!done) {
2941 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,
2942 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],
2943 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]);
2944 }
2945 } else
2946 ERR("unexpected device %llx found\n", tp.item->key.offset);
2947 }
2948 }
2949 } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
2950 if (tp.item->size < sizeof(CHUNK_ITEM)) {
2951 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));
2952 } else {
2953 c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
2954
2955 if (!c) {
2956 ERR("out of memory\n");
2957 return STATUS_INSUFFICIENT_RESOURCES;
2958 }
2959
2960 c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
2961
2962 if (!c->nonpaged) {
2963 ERR("out of memory\n");
2964 ExFreePool(c);
2965 return STATUS_INSUFFICIENT_RESOURCES;
2966 }
2967
2968 c->size = tp.item->size;
2969 c->offset = tp.item->key.offset;
2970 c->used = c->oldused = 0;
2971 c->cache = NULL;
2972 c->created = FALSE;
2973
2974 c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
2975
2976 if (!c->chunk_item) {
2977 ERR("out of memory\n");
2978 ExFreePool(c);
2979 ExFreePool(c->nonpaged);
2980 return STATUS_INSUFFICIENT_RESOURCES;
2981 }
2982
2983 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
2984
2985 if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
2986 Vcb->data_flags = c->chunk_item->type;
2987
2988 if (c->chunk_item->num_stripes > 0) {
2989 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
2990
2991 c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
2992
2993 if (!c->devices) {
2994 ERR("out of memory\n");
2995 ExFreePool(c);
2996 ExFreePool(c->nonpaged);
2997 ExFreePool(c->chunk_item);
2998 return STATUS_INSUFFICIENT_RESOURCES;
2999 }
3000
3001 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3002 c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3003 TRACE("device %llu = %p\n", i, c->devices[i]);
3004 }
3005 } else
3006 c->devices = NULL;
3007
3008 ExInitializeResourceLite(&c->nonpaged->lock);
3009 ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
3010
3011 InitializeListHead(&c->space);
3012 InitializeListHead(&c->space_size);
3013 InitializeListHead(&c->deleting);
3014 InitializeListHead(&c->changed_extents);
3015
3016 InsertTailList(&Vcb->chunks, &c->list_entry);
3017
3018 c->list_entry_changed.Flink = NULL;
3019 }
3020 }
3021
3022 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
3023
3024 if (b)
3025 tp = next_tp;
3026 } while (b);
3027
3028 Vcb->log_to_phys_loaded = TRUE;
3029
3030 if (Vcb->data_flags == 0)
3031 Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
3032
3033 return STATUS_SUCCESS;
3034 }
3035
3036 void protect_superblocks(device_extension* Vcb, chunk* c) {
3037 UINT16 i = 0, j;
3038 UINT64 off_start, off_end;
3039
3040 // The Linux driver also protects all the space before the first superblock.
3041 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3042 // evidently Linux assumes the chunk at 0 is always SINGLE.
3043 if (c->offset < superblock_addrs[0])
3044 space_list_subtract(Vcb, c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
3045
3046 while (superblock_addrs[i] != 0) {
3047 CHUNK_ITEM* ci = c->chunk_item;
3048 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3049
3050 if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
3051 for (j = 0; j < ci->num_stripes; j++) {
3052 ULONG sub_stripes = max(ci->sub_stripes, 1);
3053
3054 if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3055 #ifdef _DEBUG
3056 UINT64 startoff;
3057 UINT16 startoffstripe;
3058 #endif
3059
3060 TRACE("cut out superblock in chunk %llx\n", c->offset);
3061
3062 off_start = superblock_addrs[i] - cis[j].offset;
3063 off_start -= off_start % ci->stripe_length;
3064 off_start *= ci->num_stripes / sub_stripes;
3065 off_start += (j / sub_stripes) * ci->stripe_length;
3066
3067 off_end = off_start + ci->stripe_length;
3068
3069 #ifdef _DEBUG
3070 get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
3071 TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
3072 TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]);
3073 #endif
3074
3075 space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3076 }
3077 }
3078 } else { // SINGLE, DUPLICATE, RAID1
3079 for (j = 0; j < ci->num_stripes; j++) {
3080 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3081 TRACE("cut out superblock in chunk %llx\n", c->offset);
3082
3083 // The Linux driver protects the whole stripe in which the superblock lives
3084
3085 off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3086 off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3087
3088 space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3089 }
3090 }
3091 }
3092
3093 i++;
3094 }
3095 }
3096
3097 static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
3098 LIST_ENTRY* le = Vcb->chunks.Flink;
3099 chunk* c;
3100 KEY searchkey;
3101 traverse_ptr tp;
3102 BLOCK_GROUP_ITEM* bgi;
3103 NTSTATUS Status;
3104
3105 // c00000,c0,800000
3106 // block_group_item size=7f0000 chunktreeid=100 flags=1
3107
3108 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3109
3110 while (le != &Vcb->chunks) {
3111 c = CONTAINING_RECORD(le, chunk, list_entry);
3112
3113 searchkey.obj_id = c->offset;
3114 searchkey.offset = c->chunk_item->size;
3115
3116 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3117 if (!NT_SUCCESS(Status)) {
3118 ERR("error - find_item returned %08x\n", Status);
3119 return Status;
3120 }
3121
3122 if (!keycmp(&searchkey, &tp.item->key)) {
3123 if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
3124 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
3125
3126 c->used = c->oldused = bgi->used;
3127
3128 TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
3129 } else {
3130 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3131 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));
3132 }
3133 }
3134
3135 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3136 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3137 //
3138 // return (addr - c->offset) + cis->offset;
3139 // }
3140
3141 // FIXME - make sure we free occasionally after doing one of these, or we
3142 // might use up a lot of memory with a big disk.
3143
3144 Status = load_free_space_cache(Vcb, c);
3145 if (!NT_SUCCESS(Status)) {
3146 ERR("load_free_space_cache returned %08x\n", Status);
3147 return Status;
3148 }
3149
3150 protect_superblocks(Vcb, c);
3151
3152 le = le->Flink;
3153 }
3154
3155 return STATUS_SUCCESS;
3156 }
3157
3158 // static void STDCALL root_test(device_extension* Vcb) {
3159 // root* r;
3160 // KEY searchkey;
3161 // traverse_ptr tp, next_tp;
3162 // BOOL b;
3163 //
3164 // r = Vcb->roots;
3165 // while (r) {
3166 // if (r->id == 0x102)
3167 // break;
3168 // r = r->next;
3169 // }
3170 //
3171 // if (!r) {
3172 // ERR("Could not find root tree.\n");
3173 // return;
3174 // }
3175 //
3176 // searchkey.obj_id = 0x1b6;
3177 // searchkey.obj_type = 0xb;
3178 // searchkey.offset = 0;
3179 //
3180 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3181 // ERR("Could not find first item.\n");
3182 // return;
3183 // }
3184 //
3185 // b = TRUE;
3186 // do {
3187 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3188 //
3189 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3190 //
3191 // if (b) {
3192 // free_traverse_ptr(&tp);
3193 // tp = next_tp;
3194 // }
3195 // } while (b);
3196 //
3197 // free_traverse_ptr(&tp);
3198 // }
3199
3200 static NTSTATUS load_sys_chunks(device_extension* Vcb) {
3201 KEY key;
3202 ULONG n = Vcb->superblock.n;
3203
3204 while (n > 0) {
3205 if (n > sizeof(KEY)) {
3206 RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
3207 n -= sizeof(KEY);
3208 } else
3209 return STATUS_SUCCESS;
3210
3211 TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
3212
3213 if (key.obj_type == TYPE_CHUNK_ITEM) {
3214 CHUNK_ITEM* ci;
3215 ULONG cisize;
3216 sys_chunk* sc;
3217
3218 if (n < sizeof(CHUNK_ITEM))
3219 return STATUS_SUCCESS;
3220
3221 ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
3222 cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
3223
3224 if (n < cisize)
3225 return STATUS_SUCCESS;
3226
3227 sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
3228
3229 if (!sc) {
3230 ERR("out of memory\n");
3231 return STATUS_INSUFFICIENT_RESOURCES;
3232 }
3233
3234 sc->key = key;
3235 sc->size = cisize;
3236 sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
3237
3238 if (!sc->data) {
3239 ERR("out of memory\n");
3240 return STATUS_INSUFFICIENT_RESOURCES;
3241 }
3242
3243 RtlCopyMemory(sc->data, ci, sc->size);
3244 InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
3245
3246 n -= cisize;
3247 } else {
3248 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
3249 return STATUS_INTERNAL_ERROR;
3250 }
3251 }
3252
3253 return STATUS_SUCCESS;
3254 }
3255
3256 static root* find_default_subvol(device_extension* Vcb) {
3257 LIST_ENTRY* le;
3258
3259 static char fn[] = "default";
3260 static UINT32 crc32 = 0x8dbfc2d2;
3261
3262 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
3263 NTSTATUS Status;
3264 KEY searchkey;
3265 traverse_ptr tp;
3266 DIR_ITEM* di;
3267
3268 searchkey.obj_id = Vcb->superblock.root_dir_objectid;
3269 searchkey.obj_type = TYPE_DIR_ITEM;
3270 searchkey.offset = crc32;
3271
3272 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
3273 if (!NT_SUCCESS(Status)) {
3274 ERR("error - find_item returned %08x\n", Status);
3275 goto end;
3276 }
3277
3278 if (keycmp(&tp.item->key, &searchkey)) {
3279 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3280 goto end;
3281 }
3282
3283 if (tp.item->size < sizeof(DIR_ITEM)) {
3284 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));
3285 goto end;
3286 }
3287
3288 di = (DIR_ITEM*)tp.item->data;
3289
3290 if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
3291 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);
3292 goto end;
3293 }
3294
3295 if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
3296 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3297 goto end;
3298 }
3299
3300 if (di->key.obj_type != TYPE_ROOT_ITEM) {
3301 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
3302 goto end;
3303 }
3304
3305 le = Vcb->roots.Flink;
3306 while (le != &Vcb->roots) {
3307 root* r = CONTAINING_RECORD(le, root, list_entry);
3308
3309 if (r->id == di->key.obj_id)
3310 return r;
3311
3312 le = le->Flink;
3313 }
3314
3315 ERR("could not find root %llx, using default instead\n", di->key.obj_id);
3316 }
3317
3318 end:
3319 le = Vcb->roots.Flink;
3320 while (le != &Vcb->roots) {
3321 root* r = CONTAINING_RECORD(le, root, list_entry);
3322
3323 if (r->id == BTRFS_ROOT_FSTREE)
3324 return r;
3325
3326 le = le->Flink;
3327 }
3328
3329 return NULL;
3330 }
3331
3332 static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
3333 device_extension* Vcb = DeviceObject->DeviceExtension;
3334 ULONG i;
3335 NTSTATUS Status;
3336
3337 Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors?
3338
3339 Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG);
3340 if (!Vcb->threads.threads) {
3341 ERR("out of memory\n");
3342 return STATUS_INSUFFICIENT_RESOURCES;
3343 }
3344
3345 RtlZeroMemory(Vcb->threads.threads, sizeof(drv_thread) * Vcb->threads.num_threads);
3346
3347 for (i = 0; i < Vcb->threads.num_threads; i++) {
3348 Vcb->threads.threads[i].DeviceObject = DeviceObject;
3349 KeInitializeEvent(&Vcb->threads.threads[i].event, SynchronizationEvent, FALSE);
3350 KeInitializeEvent(&Vcb->threads.threads[i].finished, NotificationEvent, FALSE);
3351 InitializeListHead(&Vcb->threads.threads[i].jobs);
3352 KeInitializeSpinLock(&Vcb->threads.threads[i].spin_lock);
3353
3354 Status = PsCreateSystemThread(&Vcb->threads.threads[i].handle, 0, NULL, NULL, NULL, worker_thread, &Vcb->threads.threads[i]);
3355 if (!NT_SUCCESS(Status)) {
3356 ULONG j;
3357
3358 ERR("PsCreateSystemThread returned %08x\n", Status);
3359
3360 for (j = 0; j < i; j++) {
3361 Vcb->threads.threads[i].quit = TRUE;
3362 KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
3363 }
3364
3365 return Status;
3366 }
3367 }
3368
3369 return STATUS_SUCCESS;
3370 }
3371
3372 BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
3373 ULONG threadnum;
3374 thread_job* tj;
3375
3376 threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads;
3377
3378 if (Vcb->threads.threads[threadnum].quit)
3379 return FALSE;
3380
3381 tj = ExAllocatePoolWithTag(NonPagedPool, sizeof(thread_job), ALLOC_TAG);
3382 if (!tj) {
3383 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
3384 Irp->IoStatus.Information = 0;
3385 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3386 return FALSE;
3387 }
3388
3389 tj->Irp = Irp;
3390
3391 ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock);
3392 KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE);
3393
3394 return TRUE;
3395 }
3396
3397 static BOOL raid_generations_okay(device_extension* Vcb) {
3398 UINT64 i;
3399
3400 // FIXME - if the difference between superblocks is small, we should try to recover
3401
3402 for (i = 0; i < Vcb->superblock.num_devices; i++) {
3403 LIST_ENTRY* le = volumes.Flink;
3404 while (le != &volumes) {
3405 volume* v = CONTAINING_RECORD(le, volume, list_entry);
3406
3407 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
3408 RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
3409 ) {
3410 if (v->gen1 != Vcb->superblock.generation - 1) {
3411 WARN("device %llu had generation %llx, expected %llx\n", i, v->gen1, Vcb->superblock.generation - 1);
3412 return FALSE;
3413 } else
3414 break;
3415 }
3416 le = le->Flink;
3417 }
3418 }
3419
3420 return TRUE;
3421 }
3422
3423 static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
3424 PIO_STACK_LOCATION Stack;
3425 PDEVICE_OBJECT NewDeviceObject = NULL;
3426 PDEVICE_OBJECT DeviceToMount;
3427 NTSTATUS Status;
3428 device_extension* Vcb = NULL;
3429 GET_LENGTH_INFORMATION gli;
3430 UINT64 i;
3431 LIST_ENTRY* le;
3432 KEY searchkey;
3433 traverse_ptr tp;
3434 fcb* root_fcb = NULL;
3435 ccb* root_ccb = NULL;
3436
3437 TRACE("mount_vol called\n");
3438
3439 if (DeviceObject != devobj)
3440 {
3441 Status = STATUS_INVALID_DEVICE_REQUEST;
3442 goto exit;
3443 }
3444
3445 Stack = IoGetCurrentIrpStackLocation(Irp);
3446 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
3447
3448 Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
3449 &gli, sizeof(gli), TRUE, NULL);
3450 if (!NT_SUCCESS(Status)) {
3451 ERR("error reading length information: %08x\n", Status);
3452 Status = STATUS_UNRECOGNIZED_VOLUME;
3453 goto exit;
3454 }
3455
3456 Status = IoCreateDevice(drvobj,
3457 sizeof(device_extension),
3458 NULL,
3459 FILE_DEVICE_DISK_FILE_SYSTEM,
3460 0,
3461 FALSE,
3462 &NewDeviceObject);
3463 if (!NT_SUCCESS(Status)) {
3464 ERR("IoCreateDevice returned %08x\n", Status);
3465 Status = STATUS_UNRECOGNIZED_VOLUME;
3466 goto exit;
3467 }
3468
3469 // TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
3470
3471 NewDeviceObject->Flags |= DO_DIRECT_IO;
3472 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
3473 RtlZeroMemory(Vcb, sizeof(device_extension));
3474 Vcb->type = VCB_TYPE_VOLUME;
3475
3476 ExInitializeResourceLite(&Vcb->tree_lock);
3477 Vcb->open_trees = 0;
3478 Vcb->need_write = FALSE;
3479
3480 ExInitializeResourceLite(&Vcb->fcb_lock);
3481 ExInitializeResourceLite(&Vcb->DirResource);
3482 ExInitializeResourceLite(&Vcb->checksum_lock);
3483 ExInitializeResourceLite(&Vcb->chunk_lock);
3484
3485 ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
3486 InsertTailList(&VcbList, &Vcb->list_entry);
3487 ExReleaseResourceLite(&global_loading_lock);
3488
3489 ExInitializeResourceLite(&Vcb->load_lock);
3490 ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
3491
3492 // Vcb->Identifier.Type = NTFS_TYPE_VCB;
3493 // Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
3494 //
3495 // Status = NtfsGetVolumeData(DeviceToMount,
3496 // Vcb);
3497 // if (!NT_SUCCESS(Status))
3498 // goto ByeBye;
3499
3500 // Vcb->device = DeviceToMount;
3501 DeviceToMount->Flags |= DO_DIRECT_IO;
3502
3503 // Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
3504 // &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
3505 // if (!NT_SUCCESS(Status)) {
3506 // ERR("error reading disk geometry: %08x\n", Status);
3507 // goto exit;
3508 // } else {
3509 // TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
3510 // Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
3511 // Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
3512 // }
3513
3514 TRACE("partition length = %llx\n", gli.Length.QuadPart);
3515
3516 Status = read_superblock(Vcb, DeviceToMount, gli.Length.QuadPart);
3517 if (!NT_SUCCESS(Status)) {
3518 Status = STATUS_UNRECOGNIZED_VOLUME;
3519 goto exit;
3520 }
3521
3522 if (Vcb->superblock.magic != BTRFS_MAGIC) {
3523 ERR("not a BTRFS volume\n");
3524 Status = STATUS_UNRECOGNIZED_VOLUME;
3525 goto exit;
3526 } else {
3527 TRACE("btrfs magic found\n");
3528 }
3529
3530 Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options);
3531 if (!NT_SUCCESS(Status)) {
3532 ERR("registry_load_volume_options returned %08x\n", Status);
3533 goto exit;
3534 }
3535
3536 if (Vcb->options.ignore) {
3537 TRACE("ignoring volume\n");
3538 Status = STATUS_UNRECOGNIZED_VOLUME;
3539 goto exit;
3540 }
3541
3542 if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
3543 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
3544 Status = STATUS_UNRECOGNIZED_VOLUME;
3545 goto exit;
3546 }
3547
3548 le = volumes.Flink;
3549 while (le != &volumes) {
3550 volume* v = CONTAINING_RECORD(le, volume, list_entry);
3551
3552 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
3553 // skipping over device in RAID which isn't the first one
3554 Status = STATUS_UNRECOGNIZED_VOLUME;
3555 goto exit;
3556 }
3557
3558 le = le->Flink;
3559 }
3560
3561 Vcb->readonly = FALSE;
3562 if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
3563 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
3564 Vcb->readonly = TRUE;
3565 }
3566
3567 Vcb->superblock.generation++;
3568 Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
3569
3570 Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
3571 if (!Vcb->devices) {
3572 ERR("out of memory\n");
3573 Status = STATUS_INSUFFICIENT_RESOURCES;
3574 goto exit;
3575 }
3576
3577 Vcb->devices[0].devobj = DeviceToMount;
3578 RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
3579 init_device(Vcb, &Vcb->devices[0], FALSE);
3580 Vcb->devices[0].length = gli.Length.QuadPart;
3581
3582 if (Vcb->superblock.num_devices > 1)
3583 RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
3584
3585 Vcb->devices_loaded = 1;
3586
3587 TRACE("DeviceToMount = %p\n", DeviceToMount);
3588 TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb);
3589
3590 NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
3591 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
3592
3593 InitializeListHead(&Vcb->roots);
3594 InitializeListHead(&Vcb->drop_roots);
3595
3596 Vcb->log_to_phys_loaded = FALSE;
3597
3598 Vcb->max_inline = Vcb->superblock.node_size / 2;
3599
3600 add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
3601
3602 if (!Vcb->chunk_root) {
3603 ERR("Could not load chunk root.\n");
3604 Status = STATUS_INTERNAL_ERROR;
3605 goto exit;
3606 }
3607
3608 InitializeListHead(&Vcb->sys_chunks);
3609 Status = load_sys_chunks(Vcb);
3610 if (!NT_SUCCESS(Status)) {
3611 ERR("load_sys_chunks returned %08x\n", Status);
3612 goto exit;
3613 }
3614
3615 InitializeListHead(&Vcb->chunks);
3616 InitializeListHead(&Vcb->chunks_changed);
3617 InitializeListHead(&Vcb->trees);
3618 InitializeListHead(&Vcb->all_fcbs);
3619 InitializeListHead(&Vcb->dirty_fcbs);
3620 InitializeListHead(&Vcb->dirty_filerefs);
3621 InitializeListHead(&Vcb->shared_extents);
3622 InitializeListHead(&Vcb->sector_checksums);
3623
3624 KeInitializeSpinLock(&Vcb->dirty_fcbs_lock);
3625 KeInitializeSpinLock(&Vcb->dirty_filerefs_lock);
3626 KeInitializeSpinLock(&Vcb->shared_extents_lock);
3627
3628 InitializeListHead(&Vcb->DirNotifyList);
3629
3630 FsRtlNotifyInitializeSync(&Vcb->NotifySync);
3631
3632 Status = load_chunk_root(Vcb);
3633 if (!NT_SUCCESS(Status)) {
3634 ERR("load_chunk_root returned %08x\n", Status);
3635 goto exit;
3636 }
3637
3638 if (Vcb->superblock.num_devices > 1) {
3639 if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3640 ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
3641
3642 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
3643
3644 Status = STATUS_INTERNAL_ERROR;
3645 goto exit;
3646 }
3647
3648 if (!raid_generations_okay(Vcb)) {
3649 ERR("could not mount as generation mismatch\n");
3650
3651 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
3652
3653 Status = STATUS_INTERNAL_ERROR;
3654 goto exit;
3655 }
3656 }
3657
3658 add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
3659
3660 if (!Vcb->root_root) {
3661 ERR("Could not load root of roots.\n");
3662 Status = STATUS_INTERNAL_ERROR;
3663 goto exit;
3664 }
3665
3666 Status = look_for_roots(Vcb);
3667 if (!NT_SUCCESS(Status)) {
3668 ERR("look_for_roots returned %08x\n", Status);
3669 goto exit;
3670 }
3671
3672 Status = find_chunk_usage(Vcb);
3673 if (!NT_SUCCESS(Status)) {
3674 ERR("find_chunk_usage returned %08x\n", Status);
3675 goto exit;
3676 }
3677
3678 // We've already increased the generation by one
3679 if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) {
3680 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
3681 Status = clear_free_space_cache(Vcb);
3682 if (!NT_SUCCESS(Status)) {
3683 ERR("clear_free_space_cache returned %08x\n", Status);
3684 goto exit;
3685 }
3686 }
3687
3688 Vcb->volume_fcb = create_fcb();
3689 if (!Vcb->volume_fcb) {
3690 ERR("out of memory\n");
3691 Status = STATUS_INSUFFICIENT_RESOURCES;
3692 goto exit;
3693 }
3694
3695 Vcb->volume_fcb->Vcb = Vcb;
3696 Vcb->volume_fcb->sd = NULL;
3697
3698 root_fcb = create_fcb();
3699 if (!root_fcb) {
3700 ERR("out of memory\n");
3701 Status = STATUS_INSUFFICIENT_RESOURCES;
3702 goto exit;
3703 }
3704
3705 root_fcb->Vcb = Vcb;
3706 root_fcb->inode = SUBVOL_ROOT_INODE;
3707 root_fcb->type = BTRFS_TYPE_DIRECTORY;
3708
3709 #ifdef DEBUG_FCB_REFCOUNTS
3710 WARN("volume FCB = %p\n", Vcb->volume_fcb);
3711 WARN("root FCB = %p\n", root_fcb);
3712 #endif
3713
3714 root_fcb->subvol = find_default_subvol(Vcb);
3715
3716 if (!root_fcb->subvol) {
3717 ERR("could not find top subvol\n");
3718 Status = STATUS_INTERNAL_ERROR;
3719 goto exit;
3720 }
3721
3722 searchkey.obj_id = root_fcb->inode;
3723 searchkey.obj_type = TYPE_INODE_ITEM;
3724 searchkey.offset = 0xffffffffffffffff;
3725
3726 Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE);
3727 if (!NT_SUCCESS(Status)) {
3728 ERR("error - find_item returned %08x\n", Status);
3729 goto exit;
3730 }
3731
3732 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
3733 ERR("couldn't find INODE_ITEM for root directory\n");
3734 Status = STATUS_INTERNAL_ERROR;
3735 goto exit;
3736 }
3737
3738 if (tp.item->size > 0)
3739 RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
3740
3741 fcb_get_sd(root_fcb, NULL);
3742
3743 root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE);
3744
3745 Vcb->root_fileref = create_fileref();
3746 if (!Vcb->root_fileref) {
3747 ERR("out of memory\n");
3748 Status = STATUS_INSUFFICIENT_RESOURCES;
3749 goto exit;
3750 }
3751
3752 Vcb->root_fileref->fcb = root_fcb;
3753 InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
3754 InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
3755
3756 root_fcb->fileref = Vcb->root_fileref;
3757
3758 root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
3759 if (!root_ccb) {
3760 ERR("out of memory\n");
3761 Status = STATUS_INSUFFICIENT_RESOURCES;
3762 goto exit;
3763 }
3764
3765 Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
3766 Vcb->root_file->FsContext = root_fcb;
3767 #ifdef __REACTOS__
3768 Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
3769 Vcb->root_file->Vpb = DeviceObject->Vpb;
3770 #endif
3771
3772 RtlZeroMemory(root_ccb, sizeof(ccb));
3773 root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
3774 root_ccb->NodeSize = sizeof(ccb);
3775
3776 #ifndef __REACTOS__
3777 Vcb->root_file->FsContext = root_ccb;
3778 #else
3779 Vcb->root_file->FsContext2 = root_ccb;
3780
3781 _SEH2_TRY {
3782 CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), FALSE, cache_callbacks, Vcb->root_file);
3783 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3784 Status = _SEH2_GetExceptionCode();
3785 goto exit;
3786 } _SEH2_END;
3787 #endif
3788
3789 for (i = 0; i < Vcb->superblock.num_devices; i++) {
3790 Status = find_disk_holes(Vcb, &Vcb->devices[i]);
3791 if (!NT_SUCCESS(Status)) {
3792 ERR("find_disk_holes returned %08x\n", Status);
3793 goto exit;
3794 }
3795 }
3796
3797 // root_test(Vcb);
3798
3799 KeInitializeSpinLock(&Vcb->FcbListLock);
3800
3801 NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
3802 Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
3803 Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
3804 NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
3805 NewDeviceObject->Vpb->VolumeLabel[0] = '?';
3806 NewDeviceObject->Vpb->VolumeLabel[1] = 0;
3807 NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
3808 Vcb->Vpb = NewDeviceObject->Vpb;
3809
3810 KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE);
3811
3812 Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
3813 if (!NT_SUCCESS(Status)) {
3814 ERR("PsCreateSystemThread returned %08x\n", Status);
3815 goto exit;
3816 }
3817
3818 Status = create_worker_threads(NewDeviceObject);
3819 if (!NT_SUCCESS(Status)) {
3820 ERR("create_worker_threads returned %08x\n", Status);
3821 goto exit;
3822 }
3823
3824 Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
3825 if (!NT_SUCCESS(Status))
3826 WARN("registry_mark_volume_mounted returned %08x\n", Status);
3827
3828 Status = STATUS_SUCCESS;
3829
3830 exit:
3831 if (Vcb) {
3832 ExReleaseResourceLite(&Vcb->load_lock);
3833 }
3834
3835 if (!NT_SUCCESS(Status)) {
3836 if (Vcb) {
3837 if (Vcb->root_file)
3838 ObDereferenceObject(Vcb->root_file);
3839 else if (Vcb->root_fileref)
3840 free_fileref(Vcb->root_fileref);
3841 else if (root_fcb)
3842 free_fcb(root_fcb);
3843
3844 if (Vcb->volume_fcb)
3845 free_fcb(Vcb->volume_fcb);
3846
3847 ExDeleteResourceLite(&Vcb->tree_lock);
3848 ExDeleteResourceLite(&Vcb->load_lock);
3849 ExDeleteResourceLite(&Vcb->fcb_lock);
3850 ExDeleteResourceLite(&Vcb->DirResource);
3851 ExDeleteResourceLite(&Vcb->checksum_lock);
3852 ExDeleteResourceLite(&Vcb->chunk_lock);
3853
3854 if (Vcb->devices)
3855 ExFreePoolWithTag(Vcb->devices, ALLOC_TAG);
3856
3857 RemoveEntryList(&Vcb->list_entry);
3858 }
3859
3860 if (NewDeviceObject)
3861 IoDeleteDevice(NewDeviceObject);
3862 } else
3863 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
3864
3865 TRACE("mount_vol done (status: %lx)\n", Status);
3866
3867 return Status;
3868 }
3869
3870 static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3871 PIO_STACK_LOCATION IrpSp;
3872 NTSTATUS Status;
3873 device_extension* Vcb = DeviceObject->DeviceExtension;
3874 BOOL top_level;
3875
3876 TRACE("file system control\n");
3877
3878 FsRtlEnterFileSystem();
3879
3880 top_level = is_top_level(Irp);
3881
3882 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
3883 Status = part0_passthrough(DeviceObject, Irp);
3884 goto exit;
3885 }
3886
3887 Status = STATUS_NOT_IMPLEMENTED;
3888
3889 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3890
3891 Irp->IoStatus.Information = 0;
3892
3893 switch (IrpSp->MinorFunction) {
3894 case IRP_MN_MOUNT_VOLUME:
3895 TRACE("IRP_MN_MOUNT_VOLUME\n");
3896
3897 Status = mount_vol(DeviceObject, Irp);
3898 break;
3899
3900 case IRP_MN_KERNEL_CALL:
3901 TRACE("IRP_MN_KERNEL_CALL\n");
3902
3903 Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, FALSE);
3904 break;
3905
3906 case IRP_MN_USER_FS_REQUEST:
3907 TRACE("IRP_MN_USER_FS_REQUEST\n");
3908
3909 Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE);
3910 break;
3911
3912 case IRP_MN_VERIFY_VOLUME:
3913 FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
3914 break;
3915
3916 default:
3917 break;
3918 }
3919
3920 Irp->IoStatus.Status = Status;
3921
3922 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3923
3924 exit:
3925 if (top_level)
3926 IoSetTopLevelIrp(NULL);
3927
3928 FsRtlExitFileSystem();
3929
3930 return Status;
3931 }
3932
3933 static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3934 NTSTATUS Status;
3935 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3936 fcb* fcb = IrpSp->FileObject->FsContext;
3937 device_extension* Vcb = DeviceObject->DeviceExtension;
3938 BOOL top_level;
3939
3940 FsRtlEnterFileSystem();
3941
3942 top_level = is_top_level(Irp);
3943
3944 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
3945 Status = part0_passthrough(DeviceObject, Irp);
3946 goto exit;
3947 }
3948
3949 TRACE("lock control\n");
3950
3951 Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
3952
3953 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3954
3955 exit:
3956 if (top_level)
3957 IoSetTopLevelIrp(NULL);
3958
3959 FsRtlExitFileSystem();
3960
3961 return Status;
3962 }
3963
3964 NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3965 NTSTATUS Status;
3966 part0_device_extension* p0de = DeviceObject->DeviceExtension;
3967
3968 IoSkipCurrentIrpStackLocation(Irp);
3969
3970 Status = IoCallDriver(p0de->devobj, Irp);
3971
3972 return Status;
3973 }
3974
3975 static NTSTATUS part0_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3976 NTSTATUS Status;
3977 part0_device_extension* p0de = DeviceObject->DeviceExtension;
3978 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3979
3980 TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
3981
3982 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
3983 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
3984 {
3985 MOUNTDEV_UNIQUE_ID* mduid;
3986
3987 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
3988 Status = STATUS_BUFFER_TOO_SMALL;
3989 Irp->IoStatus.Status = Status;
3990 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
3991 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3992 return Status;
3993 }
3994
3995 mduid = Irp->AssociatedIrp.SystemBuffer;
3996 mduid->UniqueIdLength = sizeof(BTRFS_UUID);
3997
3998 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength) {
3999 Status = STATUS_BUFFER_OVERFLOW;
4000 Irp->IoStatus.Status = Status;
4001 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
4002 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4003 return Status;
4004 }
4005
4006 RtlCopyMemory(mduid->UniqueId, &p0de->uuid, sizeof(BTRFS_UUID));
4007
4008 Status = STATUS_SUCCESS;
4009 Irp->IoStatus.Status = Status;
4010 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength;
4011 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4012
4013 return Status;
4014 }
4015
4016 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
4017 {
4018 PMOUNTDEV_NAME name;
4019
4020 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
4021 Status = STATUS_BUFFER_TOO_SMALL;
4022 Irp->IoStatus.Status = Status;
4023 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4024 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4025 return Status;
4026 }
4027
4028 name = Irp->AssociatedIrp.SystemBuffer;
4029 name->NameLength = p0de->name.Length;
4030
4031 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME) - 1 + name->NameLength) {
4032 Status = STATUS_BUFFER_OVERFLOW;
4033 Irp->IoStatus.Status = Status;
4034 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4035 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4036 return Status;
4037 }
4038
4039 RtlCopyMemory(name->Name, p0de->name.Buffer, p0de->name.Length);
4040
4041 Status = STATUS_SUCCESS;
4042 Irp->IoStatus.Status = Status;
4043 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME) - 1 + name->NameLength;
4044 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4045
4046 return Status;
4047 }
4048 }
4049
4050 IoSkipCurrentIrpStackLocation(Irp);
4051
4052 Status = IoCallDriver(p0de->devobj, Irp);
4053
4054 TRACE("returning %08x\n", Status);
4055
4056 return Status;
4057 }
4058
4059 static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4060 NTSTATUS Status;
4061 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4062 PFILE_OBJECT FileObject = IrpSp->FileObject;
4063 device_extension* Vcb = DeviceObject->DeviceExtension;
4064 fcb* fcb;
4065 BOOL top_level;
4066
4067 FsRtlEnterFileSystem();
4068
4069 top_level = is_top_level(Irp);
4070
4071 Irp->IoStatus.Information = 0;
4072
4073 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
4074 Status = part0_device_control(DeviceObject, Irp);
4075 goto end2;
4076 }
4077
4078 TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
4079
4080 if (!FileObject) {
4081 ERR("FileObject was NULL\n");
4082 Status = STATUS_INVALID_PARAMETER;
4083 goto end;
4084 }
4085
4086 fcb = FileObject->FsContext;
4087
4088 if (!fcb) {
4089 ERR("FCB was NULL\n");
4090 Status = STATUS_INVALID_PARAMETER;
4091 goto end;
4092 }
4093
4094 if (fcb != Vcb->volume_fcb) {
4095 Status = STATUS_NOT_IMPLEMENTED;
4096 goto end;
4097 }
4098
4099 IoSkipCurrentIrpStackLocation(Irp);
4100
4101 Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
4102
4103 goto end2;
4104
4105 end:
4106 Irp->IoStatus.Status = Status;
4107
4108 if (Status != STATUS_PENDING)
4109 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4110
4111 end2:
4112 if (top_level)
4113 IoSetTopLevelIrp(NULL);
4114
4115 FsRtlExitFileSystem();
4116
4117 return Status;
4118 }
4119
4120 static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4121 NTSTATUS Status;
4122 BOOL top_level;
4123 device_extension* Vcb = DeviceObject->DeviceExtension;
4124
4125 TRACE("shutdown\n");
4126
4127 FsRtlEnterFileSystem();
4128
4129 top_level = is_top_level(Irp);
4130
4131 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
4132 Status = part0_passthrough(DeviceObject, Irp);
4133 goto exit;
4134 }
4135
4136 Status = STATUS_SUCCESS;
4137
4138 while (!IsListEmpty(&VcbList)) {
4139 LIST_ENTRY* le = RemoveHeadList(&VcbList);
4140 Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
4141
4142 TRACE("shutting down Vcb %p\n", Vcb);
4143
4144 uninit(Vcb, TRUE);
4145 }
4146
4147 Irp->IoStatus.Status = Status;
4148 Irp->IoStatus.Information = 0;
4149
4150 IoCompleteRequest( Irp, IO_NO_INCREMENT );
4151
4152 exit:
4153 if (top_level)
4154 IoSetTopLevelIrp(NULL);
4155
4156 FsRtlExitFileSystem();
4157
4158 return Status;
4159 }
4160
4161 BOOL is_file_name_valid(PUNICODE_STRING us) {
4162 ULONG i;
4163
4164 if (us->Length < sizeof(WCHAR))
4165 return FALSE;
4166
4167 if (us->Length > 255 * sizeof(WCHAR))
4168 return FALSE;
4169
4170 for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
4171 if (us->Buffer[i] == '/' || us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
4172 us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))
4173 return FALSE;
4174 }
4175
4176 if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
4177 return FALSE;
4178
4179 return TRUE;
4180 }
4181
4182 #ifdef _DEBUG
4183 static void STDCALL init_serial() {
4184 NTSTATUS Status;
4185
4186 Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
4187 if (!NT_SUCCESS(Status)) {
4188 ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
4189 }
4190 }
4191 #endif
4192
4193 #ifndef __REACTOS__
4194 static void STDCALL check_cpu() {
4195 unsigned int cpuInfo[4];
4196 #ifndef _MSC_VER
4197 __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
4198 have_sse42 = cpuInfo[2] & bit_SSE4_2;
4199 #else
4200 __cpuid(cpuInfo, 1);
4201 have_sse42 = cpuInfo[2] & (1 << 20);
4202 #endif
4203
4204 if (have_sse42)
4205 TRACE("SSE4.2 is supported\n");
4206 else
4207 TRACE("SSE4.2 not supported\n");
4208 }
4209 #endif
4210
4211 #ifdef _DEBUG
4212 static void init_logging() {
4213 if (log_device.Length > 0)
4214 init_serial();
4215 else if (log_file.Length > 0) {
4216 NTSTATUS Status;
4217 OBJECT_ATTRIBUTES oa;
4218 IO_STATUS_BLOCK iosb;
4219 char* dateline;
4220 LARGE_INTEGER time;
4221 TIME_FIELDS tf;
4222
4223 InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
4224
4225 Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
4226 FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
4227
4228 if (!NT_SUCCESS(Status)) {
4229 ERR("ZwCreateFile returned %08x\n", Status);
4230 return;
4231 }
4232
4233 if (iosb.Information == FILE_OPENED) { // already exists
4234 FILE_STANDARD_INFORMATION fsi;
4235 FILE_POSITION_INFORMATION fpi;
4236
4237 static char delim[] = "\n---\n";
4238
4239 // move to end of file
4240
4241 Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
4242
4243 if (!NT_SUCCESS(Status)) {
4244 ERR("ZwQueryInformationFile returned %08x\n", Status);
4245 return;
4246 }
4247
4248 fpi.CurrentByteOffset = fsi.EndOfFile;
4249
4250 Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
4251
4252 if (!NT_SUCCESS(Status)) {
4253 ERR("ZwSetInformationFile returned %08x\n", Status);
4254 return;
4255 }
4256
4257 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim, strlen(delim), NULL, NULL);
4258
4259 if (!NT_SUCCESS(Status)) {
4260 ERR("ZwWriteFile returned %08x\n", Status);
4261 return;
4262 }
4263 }
4264
4265 dateline = ExAllocatePoolWithTag(PagedPool, 256, ALLOC_TAG);
4266
4267 if (!dateline) {
4268 ERR("out of memory\n");
4269 return;
4270 }
4271
4272 KeQuerySystemTime(&time);
4273
4274 RtlTimeToTimeFields(&time, &tf);
4275
4276 sprintf(dateline, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
4277
4278 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, strlen(dateline), NULL, NULL);
4279
4280 if (!NT_SUCCESS(Status)) {
4281 ERR("ZwWriteFile returned %08x\n", Status);
4282 return;
4283 }
4284
4285 ExFreePool(dateline);
4286 }
4287 }
4288 #endif
4289
4290 NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
4291 NTSTATUS Status;
4292 PDEVICE_OBJECT DeviceObject;
4293 UNICODE_STRING device_nameW;
4294 UNICODE_STRING dosdevice_nameW;
4295
4296 InitializeListHead(&uid_map_list);
4297
4298 log_device.Buffer = NULL;
4299 log_device.Length = log_device.MaximumLength = 0;
4300 log_file.Buffer = NULL;
4301 log_file.Length = log_file.MaximumLength = 0;
4302
4303 read_registry(RegistryPath);
4304
4305 #ifdef _DEBUG
4306 if (debug_log_level > 0)
4307 init_logging();
4308
4309 log_started = TRUE;
4310 #endif
4311
4312 TRACE("DriverEntry\n");
4313
4314 registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
4315 registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
4316
4317 if (!registry_path.Buffer) {
4318 ERR("out of memory\n");
4319 return STATUS_INSUFFICIENT_RESOURCES;
4320 }
4321
4322 RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
4323
4324 #ifndef __REACTOS__
4325 check_cpu();
4326 #endif
4327
4328 // TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
4329
4330 drvobj = DriverObject;
4331
4332 DriverObject->DriverUnload = DriverUnload;
4333
4334 DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)drv_create;
4335 DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)drv_close;
4336 DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)drv_read;
4337 DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)drv_write;
4338 DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)drv_query_information;
4339 DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)drv_set_information;
4340 DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)drv_query_ea;
4341 DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)drv_set_ea;
4342 DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)drv_flush_buffers;
4343 DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
4344 DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_set_volume_information;
4345 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)drv_cleanup;
4346 DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)drv_directory_control;
4347 DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)drv_file_system_control;
4348 DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)drv_lock_control;
4349 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)drv_device_control;
4350 DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)drv_shutdown;
4351 DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)drv_pnp;
4352 DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = (PDRIVER_DISPATCH)drv_query_security;
4353 DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = (PDRIVER_DISPATCH)drv_set_security;
4354
4355 init_fast_io_dispatch(&DriverObject->FastIoDispatch);
4356
4357 device_nameW.Buffer = device_name;
4358 device_nameW.Length = device_nameW.MaximumLength = (USHORT)wcslen(device_name) * sizeof(WCHAR);
4359 dosdevice_nameW.Buffer = dosdevice_name;
4360 dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
4361
4362 Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
4363 if (!NT_SUCCESS(Status)) {
4364 ERR("IoCreateDevice returned %08x\n", Status);
4365 return Status;
4366 }
4367
4368 devobj = DeviceObject;
4369
4370 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
4371
4372 Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
4373 if (!NT_SUCCESS(Status)) {
4374 ERR("IoCreateSymbolicLink returned %08x\n", Status);
4375 return Status;
4376 }
4377
4378 Status = init_cache();
4379 if (!NT_SUCCESS(Status)) {
4380 ERR("init_cache returned %08x\n", Status);
4381 return Status;
4382 }
4383
4384 InitializeListHead(&volumes);
4385 look_for_vols(DriverObject, &volumes);
4386
4387 InitializeListHead(&VcbList);
4388 ExInitializeResourceLite(&global_loading_lock);
4389
4390 IoRegisterFileSystem(DeviceObject);
4391
4392 return STATUS_SUCCESS;
4393 }