[BTRFS]
[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 RemoveEntryList(&Vcb->list_entry);
1939
1940 Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
1941 if (!NT_SUCCESS(Status))
1942 WARN("registry_mark_volume_unmounted returned %08x\n", Status);
1943
1944 if (flush) {
1945 InitializeListHead(&rollback);
1946
1947 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1948
1949 if (Vcb->need_write)
1950 do_write(Vcb, &rollback);
1951
1952 free_trees(Vcb);
1953
1954 clear_rollback(&rollback);
1955
1956 ExReleaseResourceLite(&Vcb->tree_lock);
1957 }
1958
1959 for (i = 0; i < Vcb->threads.num_threads; i++) {
1960 Vcb->threads.threads[i].quit = TRUE;
1961 KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
1962
1963 KeWaitForSingleObject(&Vcb->threads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
1964
1965 ZwClose(Vcb->threads.threads[i].handle);
1966 }
1967
1968 ExFreePool(Vcb->threads.threads);
1969
1970 Vcb->removing = TRUE;
1971
1972 time.QuadPart = 0;
1973 KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
1974 KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
1975
1976 free_fcb(Vcb->volume_fcb);
1977
1978 if (Vcb->root_file)
1979 ObDereferenceObject(Vcb->root_file);
1980
1981 le = Vcb->chunks.Flink;
1982 while (le != &Vcb->chunks) {
1983 chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
1984
1985 if (c->cache) {
1986 free_fcb(c->cache);
1987 c->cache = NULL;
1988 }
1989
1990 le = le->Flink;
1991 }
1992
1993 while (!IsListEmpty(&Vcb->roots)) {
1994 LIST_ENTRY* le = RemoveHeadList(&Vcb->roots);
1995 root* r = CONTAINING_RECORD(le, root, list_entry);
1996
1997 ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
1998 ExFreePool(r->nonpaged);
1999 ExFreePool(r);
2000 }
2001
2002 while (!IsListEmpty(&Vcb->chunks)) {
2003 chunk* c;
2004
2005 le = RemoveHeadList(&Vcb->chunks);
2006 c = CONTAINING_RECORD(le, chunk, list_entry);
2007
2008 while (!IsListEmpty(&c->space)) {
2009 LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2010 s = CONTAINING_RECORD(le2, space, list_entry);
2011
2012 ExFreePool(s);
2013 }
2014
2015 while (!IsListEmpty(&c->deleting)) {
2016 LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
2017 s = CONTAINING_RECORD(le2, space, list_entry);
2018
2019 ExFreePool(s);
2020 }
2021
2022 if (c->devices)
2023 ExFreePool(c->devices);
2024
2025 if (c->cache)
2026 free_fcb(c->cache);
2027
2028 ExDeleteResourceLite(&c->nonpaged->lock);
2029 ExDeleteResourceLite(&c->nonpaged->changed_extents_lock);
2030
2031 ExFreePool(c->nonpaged);
2032 ExFreePool(c->chunk_item);
2033 ExFreePool(c);
2034 }
2035
2036 // FIXME - free any open fcbs?
2037
2038 while (!IsListEmpty(&Vcb->sector_checksums)) {
2039 LIST_ENTRY* le = RemoveHeadList(&Vcb->sector_checksums);
2040 changed_sector* cs = (changed_sector*)le;
2041
2042 ExFreePool(cs);
2043 }
2044
2045 for (i = 0; i < Vcb->superblock.num_devices; i++) {
2046 while (!IsListEmpty(&Vcb->devices[i].space)) {
2047 LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].space);
2048 space* s = CONTAINING_RECORD(le, space, list_entry);
2049
2050 ExFreePool(s);
2051 }
2052 }
2053
2054 ExFreePool(Vcb->devices);
2055
2056 ExDeleteResourceLite(&Vcb->fcb_lock);
2057 ExDeleteResourceLite(&Vcb->load_lock);
2058 ExDeleteResourceLite(&Vcb->tree_lock);
2059 ExDeleteResourceLite(&Vcb->checksum_lock);
2060 ExDeleteResourceLite(&Vcb->chunk_lock);
2061
2062 ZwClose(Vcb->flush_thread_handle);
2063 }
2064
2065 NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
2066 LARGE_INTEGER newlength, time;
2067 BTRFS_TIME now;
2068 NTSTATUS Status;
2069
2070 KeQuerySystemTime(&time);
2071 win_time_to_unix(time, &now);
2072
2073 ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
2074
2075 if (fileref->deleted) {
2076 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2077 return STATUS_SUCCESS;
2078 }
2079
2080 fileref->deleted = TRUE;
2081 mark_fileref_dirty(fileref);
2082
2083 // delete INODE_ITEM (0x1)
2084
2085 TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
2086
2087 if (!fileref->fcb->ads) {
2088 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
2089 LIST_ENTRY* le;
2090
2091 mark_fcb_dirty(fileref->fcb);
2092
2093 if (fileref->fcb->inode_item.st_nlink > 1) {
2094 fileref->fcb->inode_item.st_nlink--;
2095 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2096 fileref->fcb->inode_item.sequence++;
2097 fileref->fcb->inode_item.st_ctime = now;
2098 } else {
2099 fileref->fcb->deleted = TRUE;
2100
2101 // excise extents
2102
2103 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
2104 Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), rollback);
2105 if (!NT_SUCCESS(Status)) {
2106 ERR("excise_extents returned %08x\n", Status);
2107 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2108 return Status;
2109 }
2110 }
2111
2112 fileref->fcb->Header.AllocationSize.QuadPart = 0;
2113 fileref->fcb->Header.FileSize.QuadPart = 0;
2114 fileref->fcb->Header.ValidDataLength.QuadPart = 0;
2115
2116 if (FileObject) {
2117 CC_FILE_SIZES ccfs;
2118
2119 ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
2120 ccfs.FileSize = fileref->fcb->Header.FileSize;
2121 ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
2122
2123 CcSetFileSizes(FileObject, &ccfs);
2124 }
2125 }
2126
2127 le = fileref->fcb->hardlinks.Flink;
2128 while (le != &fileref->fcb->hardlinks) {
2129 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
2130
2131 if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->index) {
2132 RemoveEntryList(&hl->list_entry);
2133
2134 if (hl->name.Buffer)
2135 ExFreePool(hl->name.Buffer);
2136
2137 if (hl->utf8.Buffer)
2138 ExFreePool(hl->utf8.Buffer);
2139
2140 ExFreePool(hl);
2141 break;
2142 }
2143
2144 le = le->Flink;
2145 }
2146 } else { // subvolume
2147 if (fileref->fcb->subvol->root_item.num_references > 1) {
2148 fileref->fcb->subvol->root_item.num_references--;
2149
2150 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2151 } else {
2152 // FIXME - we need a lock here
2153
2154 RemoveEntryList(&fileref->fcb->subvol->list_entry);
2155
2156 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2157 }
2158 }
2159 } else {
2160 fileref->fcb->deleted = TRUE;
2161 mark_fcb_dirty(fileref->fcb);
2162 }
2163
2164 // update INODE_ITEM of parent
2165
2166 TRACE("delete file %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer);
2167 ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE);
2168 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2169 fileref->parent->fcb->inode_item.st_size -= fileref->utf8.Length * 2;
2170 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2171 fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2172 fileref->parent->fcb->inode_item.sequence++;
2173 fileref->parent->fcb->inode_item.st_ctime = now;
2174 fileref->parent->fcb->inode_item.st_mtime = now;
2175 ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2176
2177 mark_fcb_dirty(fileref->parent->fcb);
2178
2179 send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
2180
2181 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2182 fileref->fcb->subvol->root_item.ctime = now;
2183
2184 if (FileObject && FileObject->Flags & FO_CACHE_SUPPORTED && fileref->fcb->nonpaged->segment_object.DataSectionObject)
2185 CcPurgeCacheSection(&fileref->fcb->nonpaged->segment_object, NULL, 0, FALSE);
2186
2187 newlength.QuadPart = 0;
2188
2189 if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2190 TRACE("CcUninitializeCacheMap failed\n");
2191
2192 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2193
2194 return STATUS_SUCCESS;
2195 }
2196
2197 static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2198 NTSTATUS Status;
2199 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2200 PFILE_OBJECT FileObject = IrpSp->FileObject;
2201 device_extension* Vcb = DeviceObject->DeviceExtension;
2202 fcb* fcb;
2203 BOOL top_level;
2204
2205 TRACE("cleanup\n");
2206
2207 FsRtlEnterFileSystem();
2208
2209 top_level = is_top_level(Irp);
2210
2211 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
2212 Status = part0_passthrough(DeviceObject, Irp);
2213 goto exit2;
2214 }
2215
2216 if (DeviceObject == devobj) {
2217 TRACE("closing file system\n");
2218 Status = STATUS_SUCCESS;
2219 goto exit;
2220 }
2221
2222 if (FileObject && FileObject->FsContext) {
2223 LONG oc;
2224 ccb* ccb;
2225 file_ref* fileref;
2226
2227 fcb = FileObject->FsContext;
2228 ccb = FileObject->FsContext2;
2229 fileref = ccb ? ccb->fileref : NULL;
2230
2231 TRACE("cleanup called for FileObject %p\n", FileObject);
2232 TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb, file_desc(FileObject), fcb->refcount, fcb->open_count);
2233
2234 IoRemoveShareAccess(FileObject, &fcb->share_access);
2235
2236 FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
2237
2238 oc = InterlockedDecrement(&fcb->open_count);
2239 #ifdef DEBUG_FCB_REFCOUNTS
2240 ERR("fcb %p: open_count now %i\n", fcb, oc);
2241 #endif
2242
2243 if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2244 fileref->delete_on_close = TRUE;
2245
2246 if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
2247 fileref->delete_on_close = FALSE;
2248
2249 if (Vcb->locked && Vcb->locked_fileobj == FileObject) {
2250 TRACE("unlocking volume\n");
2251 do_unlock_volume(Vcb);
2252 FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
2253 }
2254
2255 if (oc == 0) {
2256 if (!Vcb->removing) {
2257 LIST_ENTRY rollback;
2258
2259 InitializeListHead(&rollback);
2260
2261 if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2262 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
2263
2264 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
2265
2266 Status = delete_fileref(fileref, FileObject, &rollback);
2267 if (!NT_SUCCESS(Status)) {
2268 ERR("delete_fileref returned %08x\n", Status);
2269 do_rollback(Vcb, &rollback);
2270 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2271 goto exit;
2272 }
2273
2274 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2275 clear_rollback(&rollback);
2276 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
2277 IO_STATUS_BLOCK iosb;
2278 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2279
2280 if (!NT_SUCCESS(iosb.Status)) {
2281 ERR("CcFlushCache returned %08x\n", iosb.Status);
2282 }
2283
2284 if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2285 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
2286 ExReleaseResourceLite(fcb->Header.PagingIoResource);
2287 }
2288
2289 CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
2290
2291 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2292 FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2293 }
2294 }
2295
2296 if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2297 CcUninitializeCacheMap(FileObject, NULL, NULL);
2298 }
2299
2300 FileObject->Flags |= FO_CLEANUP_COMPLETE;
2301 }
2302
2303 Status = STATUS_SUCCESS;
2304
2305 exit:
2306 Irp->IoStatus.Status = Status;
2307 Irp->IoStatus.Information = 0;
2308
2309 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2310
2311 exit2:
2312 if (top_level)
2313 IoSetTopLevelIrp(NULL);
2314
2315 FsRtlExitFileSystem();
2316
2317 return Status;
2318 }
2319
2320 ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
2321 ULONG att;
2322 char* eaval;
2323 UINT16 ealen;
2324
2325 // ii can be NULL
2326
2327 if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
2328 if (ealen > 2) {
2329 if (eaval[0] == '0' && eaval[1] == 'x') {
2330 int i;
2331 ULONG dosnum = 0;
2332
2333 for (i = 2; i < ealen; i++) {
2334 dosnum *= 0x10;
2335
2336 if (eaval[i] >= '0' && eaval[i] <= '9')
2337 dosnum |= eaval[i] - '0';
2338 else if (eaval[i] >= 'a' && eaval[i] <= 'f')
2339 dosnum |= eaval[i] + 10 - 'a';
2340 else if (eaval[i] >= 'A' && eaval[i] <= 'F')
2341 dosnum |= eaval[i] + 10 - 'a';
2342 }
2343
2344 TRACE("DOSATTRIB: %08x\n", dosnum);
2345
2346 ExFreePool(eaval);
2347
2348 if (type == BTRFS_TYPE_DIRECTORY)
2349 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2350 else if (type == BTRFS_TYPE_SYMLINK)
2351 dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2352
2353 return dosnum;
2354 }
2355 }
2356
2357 ExFreePool(eaval);
2358 }
2359
2360 switch (type) {
2361 case BTRFS_TYPE_DIRECTORY:
2362 att = FILE_ATTRIBUTE_DIRECTORY;
2363 break;
2364
2365 case BTRFS_TYPE_SYMLINK:
2366 att = FILE_ATTRIBUTE_REPARSE_POINT;
2367 break;
2368
2369 default:
2370 att = 0;
2371 break;
2372 }
2373
2374 if (dotfile) {
2375 att |= FILE_ATTRIBUTE_HIDDEN;
2376 }
2377
2378 att |= FILE_ATTRIBUTE_ARCHIVE;
2379
2380 // FIXME - get READONLY from ii->st_mode
2381 // FIXME - return SYSTEM for block/char devices?
2382
2383 if (att == 0)
2384 att = FILE_ATTRIBUTE_NORMAL;
2385
2386 return att;
2387 }
2388
2389 NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
2390 IO_STATUS_BLOCK* IoStatus;
2391 LARGE_INTEGER Offset;
2392 PIRP Irp;
2393 PIO_STACK_LOCATION IrpSp;
2394 NTSTATUS Status;
2395 read_context* context;
2396
2397 num_reads++;
2398
2399 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
2400 if (!context) {
2401 ERR("out of memory\n");
2402 return STATUS_INSUFFICIENT_RESOURCES;
2403 }
2404
2405 RtlZeroMemory(context, sizeof(read_context));
2406 KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
2407
2408 IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
2409 if (!IoStatus) {
2410 ERR("out of memory\n");
2411 ExFreePool(context);
2412 return STATUS_INSUFFICIENT_RESOURCES;
2413 }
2414
2415 Offset.QuadPart = StartingOffset;
2416
2417 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2418 Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
2419
2420 if (!Irp) {
2421 ERR("IoAllocateIrp failed\n");
2422 Status = STATUS_INSUFFICIENT_RESOURCES;
2423 goto exit;
2424 }
2425
2426 IrpSp = IoGetNextIrpStackLocation(Irp);
2427 IrpSp->MajorFunction = IRP_MJ_READ;
2428
2429 if (DeviceObject->Flags & DO_BUFFERED_IO) {
2430 FIXME("FIXME - buffered IO\n");
2431 } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2432 // TRACE("direct IO\n");
2433
2434 Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
2435 if (!Irp->MdlAddress) {
2436 ERR("IoAllocateMdl failed\n");
2437 Status = STATUS_INSUFFICIENT_RESOURCES;
2438 // IoFreeIrp(Irp);
2439 goto exit;
2440 // } else {
2441 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2442 }
2443
2444 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
2445 } else {
2446 // TRACE("neither buffered nor direct IO\n");
2447 Irp->UserBuffer = Buffer;
2448 }
2449
2450 IrpSp->Parameters.Read.Length = Length;
2451 IrpSp->Parameters.Read.ByteOffset = Offset;
2452
2453 Irp->UserIosb = IoStatus;
2454 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2455
2456 Irp->UserEvent = &context->Event;
2457
2458 // IoQueueThreadIrp(Irp);
2459
2460 IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
2461
2462 // if (Override)
2463 // {
2464 // Stack = IoGetNextIrpStackLocation(Irp);
2465 // Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2466 // }
2467
2468 // TRACE("Calling IO Driver... with irp %p\n", Irp);
2469 Status = IoCallDriver(DeviceObject, Irp);
2470
2471 // TRACE("Waiting for IO Operation for %p\n", Irp);
2472 if (Status == STATUS_PENDING) {
2473 // TRACE("Operation pending\n");
2474 KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
2475 // TRACE("Getting IO Status... for %p\n", Irp);
2476 Status = context->iosb.Status;
2477 }
2478
2479 if (DeviceObject->Flags & DO_DIRECT_IO) {
2480 MmUnlockPages(Irp->MdlAddress);
2481 IoFreeMdl(Irp->MdlAddress);
2482 }
2483
2484 exit:
2485 IoFreeIrp(Irp);
2486
2487 ExFreePool(IoStatus);
2488 ExFreePool(context);
2489
2490 return Status;
2491 }
2492
2493 static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device, UINT64 length) {
2494 NTSTATUS Status;
2495 superblock* sb;
2496 unsigned int i, to_read;
2497 UINT32 crc32;
2498
2499 to_read = sector_align(sizeof(superblock), device->SectorSize);
2500
2501 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
2502 if (!sb) {
2503 ERR("out of memory\n");
2504 return STATUS_INSUFFICIENT_RESOURCES;
2505 }
2506
2507 i = 0;
2508
2509 while (superblock_addrs[i] > 0) {
2510 if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
2511 break;
2512
2513 Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
2514 if (!NT_SUCCESS(Status)) {
2515 ERR("Failed to read superblock %u: %08x\n", i, Status);
2516 ExFreePool(sb);
2517 return Status;
2518 }
2519
2520 // FIXME - check checksum before accepting?
2521
2522 TRACE("got superblock %u!\n", i);
2523
2524 if (i == 0 || sb->generation > Vcb->superblock.generation)
2525 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2526
2527 i++;
2528 }
2529
2530 ExFreePool(sb);
2531
2532 crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
2533 crc32 = ~crc32;
2534 TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
2535
2536 if (crc32 != *((UINT32*)Vcb->superblock.checksum))
2537 return STATUS_INTERNAL_ERROR; // FIXME - correct error?
2538
2539 TRACE("label is %s\n", Vcb->superblock.label);
2540 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2541
2542 return STATUS_SUCCESS;
2543 }
2544
2545 NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer, ULONG InputBufferSize,
2546 PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb)
2547 {
2548 PIRP Irp;
2549 KEVENT Event;
2550 NTSTATUS Status;
2551 PIO_STACK_LOCATION Stack;
2552 IO_STATUS_BLOCK IoStatus;
2553
2554 KeInitializeEvent(&Event, NotificationEvent, FALSE);
2555
2556 Irp = IoBuildDeviceIoControlRequest(ControlCode,
2557 DeviceObject,
2558 InputBuffer,
2559 InputBufferSize,
2560 OutputBuffer,
2561 OutputBufferSize,
2562 FALSE,
2563 &Event,
2564 &IoStatus);
2565
2566 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2567
2568 if (Override) {
2569 Stack = IoGetNextIrpStackLocation(Irp);
2570 Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2571 }
2572
2573 Status = IoCallDriver(DeviceObject, Irp);
2574
2575 if (Status == STATUS_PENDING) {
2576 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2577 Status = IoStatus.Status;
2578 }
2579
2580 if (iosb)
2581 *iosb = IoStatus;
2582
2583 return Status;
2584 }
2585
2586 static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
2587 root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
2588 if (!r) {
2589 ERR("out of memory\n");
2590 return STATUS_INSUFFICIENT_RESOURCES;
2591 }
2592
2593 r->id = id;
2594 r->path.Buffer = NULL;
2595 r->treeholder.address = addr;
2596 r->treeholder.tree = NULL;
2597 InitializeListHead(&r->fcbs);
2598
2599 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
2600 if (!r->nonpaged) {
2601 ERR("out of memory\n");
2602 ExFreePool(r);
2603 return STATUS_INSUFFICIENT_RESOURCES;
2604 }
2605
2606 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
2607
2608 r->lastinode = 0;
2609
2610 if (tp) {
2611 RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
2612 if (tp->item->size < sizeof(ROOT_ITEM))
2613 RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
2614 }
2615
2616 InsertTailList(&Vcb->roots, &r->list_entry);
2617
2618 switch (r->id) {
2619 case BTRFS_ROOT_ROOT:
2620 Vcb->root_root = r;
2621 break;
2622
2623 case BTRFS_ROOT_EXTENT:
2624 Vcb->extent_root = r;
2625 break;
2626
2627 case BTRFS_ROOT_CHUNK:
2628 Vcb->chunk_root = r;
2629 break;
2630
2631 case BTRFS_ROOT_DEVTREE:
2632 Vcb->dev_root = r;
2633 break;
2634
2635 case BTRFS_ROOT_CHECKSUM:
2636 Vcb->checksum_root = r;
2637 break;
2638
2639 case BTRFS_ROOT_UUID:
2640 Vcb->uuid_root = r;
2641 break;
2642 }
2643
2644 return STATUS_SUCCESS;
2645 }
2646
2647 static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
2648 traverse_ptr tp, next_tp;
2649 KEY searchkey;
2650 BOOL b;
2651 NTSTATUS Status;
2652
2653 searchkey.obj_id = 0;
2654 searchkey.obj_type = 0;
2655 searchkey.offset = 0;
2656
2657 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
2658 if (!NT_SUCCESS(Status)) {
2659 ERR("error - find_tree returned %08x\n", Status);
2660 return Status;
2661 }
2662
2663 do {
2664 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2665
2666 if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
2667 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2668
2669 if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
2670 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));
2671 } else {
2672 TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
2673
2674 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
2675 if (!NT_SUCCESS(Status)) {
2676 ERR("add_root returned %08x\n", Status);
2677 return Status;
2678 }
2679 }
2680 }
2681
2682 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
2683
2684 if (b)
2685 tp = next_tp;
2686 } while (b);
2687
2688 return STATUS_SUCCESS;
2689 }
2690
2691 static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
2692 KEY searchkey;
2693 traverse_ptr tp, next_tp;
2694 BOOL b;
2695 UINT64 lastaddr;
2696 NTSTATUS Status;
2697
2698 InitializeListHead(&dev->space);
2699
2700 searchkey.obj_id = dev->devitem.dev_id;
2701 searchkey.obj_type = TYPE_DEV_EXTENT;
2702 searchkey.offset = 0;
2703
2704 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
2705 if (!NT_SUCCESS(Status)) {
2706 ERR("error - find_tree returned %08x\n", Status);
2707 return Status;
2708 }
2709
2710 lastaddr = 0;
2711
2712 do {
2713 if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
2714 if (tp.item->size >= sizeof(DEV_EXTENT)) {
2715 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
2716
2717 if (tp.item->key.offset > lastaddr) {
2718 Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
2719 if (!NT_SUCCESS(Status)) {
2720 ERR("add_space_entry returned %08x\n", Status);
2721 return Status;
2722 }
2723 }
2724
2725 lastaddr = tp.item->key.offset + de->length;
2726 } else {
2727 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));
2728 }
2729 }
2730
2731 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
2732
2733 if (b) {
2734 tp = next_tp;
2735 if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
2736 break;
2737 }
2738 } while (b);
2739
2740 if (lastaddr < dev->devitem.num_bytes) {
2741 Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
2742 if (!NT_SUCCESS(Status)) {
2743 ERR("add_space_entry returned %08x\n", Status);
2744 return Status;
2745 }
2746 }
2747
2748 return STATUS_SUCCESS;
2749 }
2750
2751 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
2752 UINT64 i;
2753
2754 for (i = 0; i < Vcb->devices_loaded; i++) {
2755 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
2756 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],
2757 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]);
2758
2759 if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2760 TRACE("returning device %llx\n", i);
2761 return &Vcb->devices[i];
2762 }
2763 }
2764
2765 if (Vcb->devices_loaded < Vcb->superblock.num_devices && !IsListEmpty(&volumes)) {
2766 LIST_ENTRY* le = volumes.Flink;
2767
2768 while (le != &volumes) {
2769 volume* v = CONTAINING_RECORD(le, volume, list_entry);
2770
2771 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
2772 RtlCompareMemory(uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
2773 ) {
2774 NTSTATUS Status;
2775 PFILE_OBJECT FileObject;
2776 PDEVICE_OBJECT DeviceObject;
2777
2778 Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
2779 if (!NT_SUCCESS(Status)) {
2780 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
2781 return NULL;
2782 }
2783
2784 DeviceObject = FileObject->DeviceObject;
2785
2786 ObReferenceObject(DeviceObject);
2787 ObDereferenceObject(FileObject);
2788
2789 Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
2790 Vcb->devices[Vcb->devices_loaded].devitem.device_uuid = *uuid;
2791 Vcb->devices_loaded++;
2792
2793 return &Vcb->devices[Vcb->devices_loaded - 1];
2794 }
2795
2796 le = le->Flink;
2797 }
2798 }
2799
2800 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2801 uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
2802 uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
2803
2804 return NULL;
2805 }
2806
2807 static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
2808 NTSTATUS Status;
2809 STORAGE_HOTPLUG_INFO shi;
2810
2811 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
2812
2813 if (!NT_SUCCESS(Status)) {
2814 ERR("dev_ioctl returned %08x\n", Status);
2815 return FALSE;
2816 }
2817
2818 return shi.MediaRemovable != 0 ? TRUE : FALSE;
2819 }
2820
2821 static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
2822 NTSTATUS Status;
2823 ULONG cc;
2824 IO_STATUS_BLOCK iosb;
2825
2826 Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
2827
2828 if (!NT_SUCCESS(Status)) {
2829 ERR("dev_ioctl returned %08x\n", Status);
2830 return 0;
2831 }
2832
2833 if (iosb.Information < sizeof(ULONG)) {
2834 ERR("iosb.Information was too short\n");
2835 return 0;
2836 }
2837
2838 return cc;
2839 }
2840
2841 static void init_device(device_extension* Vcb, device* dev, BOOL get_length) {
2842 NTSTATUS Status;
2843 GET_LENGTH_INFORMATION gli;
2844
2845 dev->removable = is_device_removable(dev->devobj);
2846 dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
2847
2848 if (get_length) {
2849 Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
2850 &gli, sizeof(gli), TRUE, NULL);
2851 if (!NT_SUCCESS(Status)) {
2852 ERR("error reading length information: %08x\n", Status);
2853 }
2854
2855 dev->length = gli.Length.QuadPart;
2856 }
2857 }
2858
2859 static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
2860 traverse_ptr tp, next_tp;
2861 KEY searchkey;
2862 BOOL b;
2863 chunk* c;
2864 UINT64 i;
2865 NTSTATUS Status;
2866
2867 searchkey.obj_id = 0;
2868 searchkey.obj_type = 0;
2869 searchkey.offset = 0;
2870
2871 Vcb->data_flags = 0;
2872
2873 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
2874 if (!NT_SUCCESS(Status)) {
2875 ERR("error - find_item returned %08x\n", Status);
2876 return Status;
2877 }
2878
2879 do {
2880 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2881
2882 if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
2883 if (tp.item->size < sizeof(DEV_ITEM)) {
2884 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));
2885 } else {
2886 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
2887 BOOL done = FALSE;
2888
2889 for (i = 0; i < Vcb->devices_loaded; i++) {
2890 if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2891 RtlCopyMemory(&Vcb->devices[i].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
2892
2893 if (i > 0)
2894 init_device(Vcb, &Vcb->devices[i], TRUE);
2895
2896 done = TRUE;
2897 break;
2898 }
2899 }
2900
2901 if (!done) {
2902 if (!IsListEmpty(&volumes) && Vcb->devices_loaded < Vcb->superblock.num_devices) {
2903 LIST_ENTRY* le = volumes.Flink;
2904
2905 while (le != &volumes) {
2906 volume* v = CONTAINING_RECORD(le, volume, list_entry);
2907
2908 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
2909 RtlCompareMemory(&di->device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
2910 ) {
2911 PFILE_OBJECT FileObject;
2912 PDEVICE_OBJECT DeviceObject;
2913
2914 Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_DATA | FILE_WRITE_DATA, &FileObject, &DeviceObject);
2915 if (!NT_SUCCESS(Status)) {
2916 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
2917 return Status;
2918 }
2919
2920 DeviceObject = FileObject->DeviceObject;
2921
2922 ObReferenceObject(DeviceObject);
2923 ObDereferenceObject(FileObject);
2924
2925 Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
2926 RtlCopyMemory(&Vcb->devices[Vcb->devices_loaded].devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
2927 init_device(Vcb, &Vcb->devices[i], FALSE);
2928 Vcb->devices[i].length = v->length;
2929 Vcb->devices_loaded++;
2930
2931 done = TRUE;
2932 break;
2933 }
2934
2935 le = le->Flink;
2936 }
2937
2938 if (!done) {
2939 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,
2940 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],
2941 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]);
2942 }
2943 } else
2944 ERR("unexpected device %llx found\n", tp.item->key.offset);
2945 }
2946 }
2947 } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
2948 if (tp.item->size < sizeof(CHUNK_ITEM)) {
2949 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));
2950 } else {
2951 c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
2952
2953 if (!c) {
2954 ERR("out of memory\n");
2955 return STATUS_INSUFFICIENT_RESOURCES;
2956 }
2957
2958 c->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk_nonpaged), ALLOC_TAG);
2959
2960 if (!c->nonpaged) {
2961 ERR("out of memory\n");
2962 ExFreePool(c);
2963 return STATUS_INSUFFICIENT_RESOURCES;
2964 }
2965
2966 c->size = tp.item->size;
2967 c->offset = tp.item->key.offset;
2968 c->used = c->oldused = 0;
2969 c->cache = NULL;
2970 c->created = FALSE;
2971
2972 c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
2973
2974 if (!c->chunk_item) {
2975 ERR("out of memory\n");
2976 ExFreePool(c);
2977 ExFreePool(c->nonpaged);
2978 return STATUS_INSUFFICIENT_RESOURCES;
2979 }
2980
2981 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
2982
2983 if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
2984 Vcb->data_flags = c->chunk_item->type;
2985
2986 if (c->chunk_item->num_stripes > 0) {
2987 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
2988
2989 c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
2990
2991 if (!c->devices) {
2992 ERR("out of memory\n");
2993 ExFreePool(c);
2994 ExFreePool(c->nonpaged);
2995 ExFreePool(c->chunk_item);
2996 return STATUS_INSUFFICIENT_RESOURCES;
2997 }
2998
2999 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3000 c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3001 TRACE("device %llu = %p\n", i, c->devices[i]);
3002 }
3003 } else
3004 c->devices = NULL;
3005
3006 ExInitializeResourceLite(&c->nonpaged->lock);
3007 ExInitializeResourceLite(&c->nonpaged->changed_extents_lock);
3008
3009 InitializeListHead(&c->space);
3010 InitializeListHead(&c->space_size);
3011 InitializeListHead(&c->deleting);
3012 InitializeListHead(&c->changed_extents);
3013
3014 InsertTailList(&Vcb->chunks, &c->list_entry);
3015
3016 c->list_entry_changed.Flink = NULL;
3017 }
3018 }
3019
3020 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
3021
3022 if (b)
3023 tp = next_tp;
3024 } while (b);
3025
3026 Vcb->log_to_phys_loaded = TRUE;
3027
3028 if (Vcb->data_flags == 0)
3029 Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
3030
3031 return STATUS_SUCCESS;
3032 }
3033
3034 void protect_superblocks(device_extension* Vcb, chunk* c) {
3035 UINT16 i = 0, j;
3036 UINT64 off_start, off_end;
3037
3038 // The Linux driver also protects all the space before the first superblock.
3039 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3040 // evidently Linux assumes the chunk at 0 is always SINGLE.
3041 if (c->offset < superblock_addrs[0])
3042 space_list_subtract(Vcb, c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
3043
3044 while (superblock_addrs[i] != 0) {
3045 CHUNK_ITEM* ci = c->chunk_item;
3046 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3047
3048 if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
3049 for (j = 0; j < ci->num_stripes; j++) {
3050 ULONG sub_stripes = max(ci->sub_stripes, 1);
3051
3052 if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3053 #ifdef _DEBUG
3054 UINT64 startoff;
3055 UINT16 startoffstripe;
3056 #endif
3057
3058 TRACE("cut out superblock in chunk %llx\n", c->offset);
3059
3060 off_start = superblock_addrs[i] - cis[j].offset;
3061 off_start -= off_start % ci->stripe_length;
3062 off_start *= ci->num_stripes / sub_stripes;
3063 off_start += (j / sub_stripes) * ci->stripe_length;
3064
3065 off_end = off_start + ci->stripe_length;
3066
3067 #ifdef _DEBUG
3068 get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
3069 TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
3070 TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]);
3071 #endif
3072
3073 space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3074 }
3075 }
3076 } else { // SINGLE, DUPLICATE, RAID1
3077 for (j = 0; j < ci->num_stripes; j++) {
3078 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3079 TRACE("cut out superblock in chunk %llx\n", c->offset);
3080
3081 // The Linux driver protects the whole stripe in which the superblock lives
3082
3083 off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3084 off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3085
3086 space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3087 }
3088 }
3089 }
3090
3091 i++;
3092 }
3093 }
3094
3095 static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
3096 LIST_ENTRY* le = Vcb->chunks.Flink;
3097 chunk* c;
3098 KEY searchkey;
3099 traverse_ptr tp;
3100 BLOCK_GROUP_ITEM* bgi;
3101 NTSTATUS Status;
3102
3103 // c00000,c0,800000
3104 // block_group_item size=7f0000 chunktreeid=100 flags=1
3105
3106 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3107
3108 while (le != &Vcb->chunks) {
3109 c = CONTAINING_RECORD(le, chunk, list_entry);
3110
3111 searchkey.obj_id = c->offset;
3112 searchkey.offset = c->chunk_item->size;
3113
3114 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3115 if (!NT_SUCCESS(Status)) {
3116 ERR("error - find_item returned %08x\n", Status);
3117 return Status;
3118 }
3119
3120 if (!keycmp(&searchkey, &tp.item->key)) {
3121 if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
3122 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
3123
3124 c->used = c->oldused = bgi->used;
3125
3126 TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
3127 } else {
3128 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3129 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));
3130 }
3131 }
3132
3133 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3134 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3135 //
3136 // return (addr - c->offset) + cis->offset;
3137 // }
3138
3139 // FIXME - make sure we free occasionally after doing one of these, or we
3140 // might use up a lot of memory with a big disk.
3141
3142 Status = load_free_space_cache(Vcb, c);
3143 if (!NT_SUCCESS(Status)) {
3144 ERR("load_free_space_cache returned %08x\n", Status);
3145 return Status;
3146 }
3147
3148 protect_superblocks(Vcb, c);
3149
3150 le = le->Flink;
3151 }
3152
3153 return STATUS_SUCCESS;
3154 }
3155
3156 // static void STDCALL root_test(device_extension* Vcb) {
3157 // root* r;
3158 // KEY searchkey;
3159 // traverse_ptr tp, next_tp;
3160 // BOOL b;
3161 //
3162 // r = Vcb->roots;
3163 // while (r) {
3164 // if (r->id == 0x102)
3165 // break;
3166 // r = r->next;
3167 // }
3168 //
3169 // if (!r) {
3170 // ERR("Could not find root tree.\n");
3171 // return;
3172 // }
3173 //
3174 // searchkey.obj_id = 0x1b6;
3175 // searchkey.obj_type = 0xb;
3176 // searchkey.offset = 0;
3177 //
3178 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3179 // ERR("Could not find first item.\n");
3180 // return;
3181 // }
3182 //
3183 // b = TRUE;
3184 // do {
3185 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3186 //
3187 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3188 //
3189 // if (b) {
3190 // free_traverse_ptr(&tp);
3191 // tp = next_tp;
3192 // }
3193 // } while (b);
3194 //
3195 // free_traverse_ptr(&tp);
3196 // }
3197
3198 static NTSTATUS load_sys_chunks(device_extension* Vcb) {
3199 KEY key;
3200 ULONG n = Vcb->superblock.n;
3201
3202 while (n > 0) {
3203 if (n > sizeof(KEY)) {
3204 RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
3205 n -= sizeof(KEY);
3206 } else
3207 return STATUS_SUCCESS;
3208
3209 TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
3210
3211 if (key.obj_type == TYPE_CHUNK_ITEM) {
3212 CHUNK_ITEM* ci;
3213 ULONG cisize;
3214 sys_chunk* sc;
3215
3216 if (n < sizeof(CHUNK_ITEM))
3217 return STATUS_SUCCESS;
3218
3219 ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
3220 cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
3221
3222 if (n < cisize)
3223 return STATUS_SUCCESS;
3224
3225 sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
3226
3227 if (!sc) {
3228 ERR("out of memory\n");
3229 return STATUS_INSUFFICIENT_RESOURCES;
3230 }
3231
3232 sc->key = key;
3233 sc->size = cisize;
3234 sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
3235
3236 if (!sc->data) {
3237 ERR("out of memory\n");
3238 return STATUS_INSUFFICIENT_RESOURCES;
3239 }
3240
3241 RtlCopyMemory(sc->data, ci, sc->size);
3242 InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
3243
3244 n -= cisize;
3245 } else {
3246 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
3247 return STATUS_INTERNAL_ERROR;
3248 }
3249 }
3250
3251 return STATUS_SUCCESS;
3252 }
3253
3254 static root* find_default_subvol(device_extension* Vcb) {
3255 LIST_ENTRY* le;
3256
3257 static char fn[] = "default";
3258 static UINT32 crc32 = 0x8dbfc2d2;
3259
3260 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
3261 NTSTATUS Status;
3262 KEY searchkey;
3263 traverse_ptr tp;
3264 DIR_ITEM* di;
3265
3266 searchkey.obj_id = Vcb->superblock.root_dir_objectid;
3267 searchkey.obj_type = TYPE_DIR_ITEM;
3268 searchkey.offset = crc32;
3269
3270 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
3271 if (!NT_SUCCESS(Status)) {
3272 ERR("error - find_item returned %08x\n", Status);
3273 goto end;
3274 }
3275
3276 if (keycmp(&tp.item->key, &searchkey)) {
3277 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3278 goto end;
3279 }
3280
3281 if (tp.item->size < sizeof(DIR_ITEM)) {
3282 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));
3283 goto end;
3284 }
3285
3286 di = (DIR_ITEM*)tp.item->data;
3287
3288 if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
3289 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);
3290 goto end;
3291 }
3292
3293 if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
3294 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3295 goto end;
3296 }
3297
3298 if (di->key.obj_type != TYPE_ROOT_ITEM) {
3299 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
3300 goto end;
3301 }
3302
3303 le = Vcb->roots.Flink;
3304 while (le != &Vcb->roots) {
3305 root* r = CONTAINING_RECORD(le, root, list_entry);
3306
3307 if (r->id == di->key.obj_id)
3308 return r;
3309
3310 le = le->Flink;
3311 }
3312
3313 ERR("could not find root %llx, using default instead\n", di->key.obj_id);
3314 }
3315
3316 end:
3317 le = Vcb->roots.Flink;
3318 while (le != &Vcb->roots) {
3319 root* r = CONTAINING_RECORD(le, root, list_entry);
3320
3321 if (r->id == BTRFS_ROOT_FSTREE)
3322 return r;
3323
3324 le = le->Flink;
3325 }
3326
3327 return NULL;
3328 }
3329
3330 static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
3331 device_extension* Vcb = DeviceObject->DeviceExtension;
3332 ULONG i;
3333 NTSTATUS Status;
3334
3335 Vcb->threads.num_threads = max(3, KeQueryActiveProcessorCount(NULL)); // FIXME - number of processors?
3336
3337 Vcb->threads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_thread) * Vcb->threads.num_threads, ALLOC_TAG);
3338 if (!Vcb->threads.threads) {
3339 ERR("out of memory\n");
3340 return STATUS_INSUFFICIENT_RESOURCES;
3341 }
3342
3343 RtlZeroMemory(Vcb->threads.threads, sizeof(drv_thread) * Vcb->threads.num_threads);
3344
3345 for (i = 0; i < Vcb->threads.num_threads; i++) {
3346 Vcb->threads.threads[i].DeviceObject = DeviceObject;
3347 KeInitializeEvent(&Vcb->threads.threads[i].event, SynchronizationEvent, FALSE);
3348 KeInitializeEvent(&Vcb->threads.threads[i].finished, NotificationEvent, FALSE);
3349 InitializeListHead(&Vcb->threads.threads[i].jobs);
3350 KeInitializeSpinLock(&Vcb->threads.threads[i].spin_lock);
3351
3352 Status = PsCreateSystemThread(&Vcb->threads.threads[i].handle, 0, NULL, NULL, NULL, worker_thread, &Vcb->threads.threads[i]);
3353 if (!NT_SUCCESS(Status)) {
3354 ULONG j;
3355
3356 ERR("PsCreateSystemThread returned %08x\n", Status);
3357
3358 for (j = 0; j < i; j++) {
3359 Vcb->threads.threads[i].quit = TRUE;
3360 KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
3361 }
3362
3363 return Status;
3364 }
3365 }
3366
3367 return STATUS_SUCCESS;
3368 }
3369
3370 BOOL add_thread_job(device_extension* Vcb, PIRP Irp) {
3371 ULONG threadnum;
3372 thread_job* tj;
3373
3374 threadnum = InterlockedIncrement(&Vcb->threads.next_thread) % Vcb->threads.num_threads;
3375
3376 if (Vcb->threads.threads[threadnum].quit)
3377 return FALSE;
3378
3379 tj = ExAllocatePoolWithTag(NonPagedPool, sizeof(thread_job), ALLOC_TAG);
3380 if (!tj) {
3381 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
3382 Irp->IoStatus.Information = 0;
3383 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3384 return FALSE;
3385 }
3386
3387 tj->Irp = Irp;
3388
3389 ExInterlockedInsertTailList(&Vcb->threads.threads[threadnum].jobs, &tj->list_entry, &Vcb->threads.threads[threadnum].spin_lock);
3390 KeSetEvent(&Vcb->threads.threads[threadnum].event, 0, FALSE);
3391
3392 return TRUE;
3393 }
3394
3395 static BOOL raid_generations_okay(device_extension* Vcb) {
3396 UINT64 i;
3397
3398 // FIXME - if the difference between superblocks is small, we should try to recover
3399
3400 for (i = 0; i < Vcb->superblock.num_devices; i++) {
3401 LIST_ENTRY* le = volumes.Flink;
3402 while (le != &volumes) {
3403 volume* v = CONTAINING_RECORD(le, volume, list_entry);
3404
3405 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
3406 RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
3407 ) {
3408 if (v->gen1 != Vcb->superblock.generation - 1) {
3409 WARN("device %llu had generation %llx, expected %llx\n", i, v->gen1, Vcb->superblock.generation - 1);
3410 return FALSE;
3411 } else
3412 break;
3413 }
3414 le = le->Flink;
3415 }
3416 }
3417
3418 return TRUE;
3419 }
3420
3421 static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
3422 PIO_STACK_LOCATION Stack;
3423 PDEVICE_OBJECT NewDeviceObject = NULL;
3424 PDEVICE_OBJECT DeviceToMount;
3425 NTSTATUS Status;
3426 device_extension* Vcb = NULL;
3427 GET_LENGTH_INFORMATION gli;
3428 UINT64 i;
3429 LIST_ENTRY* le;
3430 KEY searchkey;
3431 traverse_ptr tp;
3432 fcb* root_fcb = NULL;
3433 ccb* root_ccb = NULL;
3434
3435 TRACE("mount_vol called\n");
3436
3437 if (DeviceObject != devobj)
3438 {
3439 Status = STATUS_INVALID_DEVICE_REQUEST;
3440 goto exit;
3441 }
3442
3443 Stack = IoGetCurrentIrpStackLocation(Irp);
3444 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
3445
3446 Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
3447 &gli, sizeof(gli), TRUE, NULL);
3448 if (!NT_SUCCESS(Status)) {
3449 ERR("error reading length information: %08x\n", Status);
3450 Status = STATUS_UNRECOGNIZED_VOLUME;
3451 goto exit;
3452 }
3453
3454 Status = IoCreateDevice(drvobj,
3455 sizeof(device_extension),
3456 NULL,
3457 FILE_DEVICE_DISK_FILE_SYSTEM,
3458 0,
3459 FALSE,
3460 &NewDeviceObject);
3461 if (!NT_SUCCESS(Status)) {
3462 ERR("IoCreateDevice returned %08x\n", Status);
3463 Status = STATUS_UNRECOGNIZED_VOLUME;
3464 goto exit;
3465 }
3466
3467 // TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
3468
3469 NewDeviceObject->Flags |= DO_DIRECT_IO;
3470 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
3471 RtlZeroMemory(Vcb, sizeof(device_extension));
3472 Vcb->type = VCB_TYPE_VOLUME;
3473
3474 ExInitializeResourceLite(&Vcb->tree_lock);
3475 Vcb->open_trees = 0;
3476 Vcb->need_write = FALSE;
3477
3478 ExInitializeResourceLite(&Vcb->fcb_lock);
3479 ExInitializeResourceLite(&Vcb->DirResource);
3480 ExInitializeResourceLite(&Vcb->checksum_lock);
3481 ExInitializeResourceLite(&Vcb->chunk_lock);
3482
3483 ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
3484 InsertTailList(&VcbList, &Vcb->list_entry);
3485 ExReleaseResourceLite(&global_loading_lock);
3486
3487 ExInitializeResourceLite(&Vcb->load_lock);
3488 ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
3489
3490 // Vcb->Identifier.Type = NTFS_TYPE_VCB;
3491 // Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
3492 //
3493 // Status = NtfsGetVolumeData(DeviceToMount,
3494 // Vcb);
3495 // if (!NT_SUCCESS(Status))
3496 // goto ByeBye;
3497
3498 // Vcb->device = DeviceToMount;
3499 DeviceToMount->Flags |= DO_DIRECT_IO;
3500
3501 // Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
3502 // &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
3503 // if (!NT_SUCCESS(Status)) {
3504 // ERR("error reading disk geometry: %08x\n", Status);
3505 // goto exit;
3506 // } else {
3507 // TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
3508 // Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
3509 // Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
3510 // }
3511
3512 TRACE("partition length = %llx\n", gli.Length.QuadPart);
3513
3514 Status = read_superblock(Vcb, DeviceToMount, gli.Length.QuadPart);
3515 if (!NT_SUCCESS(Status)) {
3516 Status = STATUS_UNRECOGNIZED_VOLUME;
3517 goto exit;
3518 }
3519
3520 if (Vcb->superblock.magic != BTRFS_MAGIC) {
3521 ERR("not a BTRFS volume\n");
3522 Status = STATUS_UNRECOGNIZED_VOLUME;
3523 goto exit;
3524 } else {
3525 TRACE("btrfs magic found\n");
3526 }
3527
3528 Status = registry_load_volume_options(&Vcb->superblock.uuid, &Vcb->options);
3529 if (!NT_SUCCESS(Status)) {
3530 ERR("registry_load_volume_options returned %08x\n", Status);
3531 goto exit;
3532 }
3533
3534 if (Vcb->options.ignore) {
3535 TRACE("ignoring volume\n");
3536 Status = STATUS_UNRECOGNIZED_VOLUME;
3537 goto exit;
3538 }
3539
3540 if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
3541 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
3542 Status = STATUS_UNRECOGNIZED_VOLUME;
3543 goto exit;
3544 }
3545
3546 le = volumes.Flink;
3547 while (le != &volumes) {
3548 volume* v = CONTAINING_RECORD(le, volume, list_entry);
3549
3550 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
3551 // skipping over device in RAID which isn't the first one
3552 Status = STATUS_UNRECOGNIZED_VOLUME;
3553 goto exit;
3554 }
3555
3556 le = le->Flink;
3557 }
3558
3559 Vcb->readonly = FALSE;
3560 if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
3561 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
3562 Vcb->readonly = TRUE;
3563 }
3564
3565 Vcb->superblock.generation++;
3566 Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
3567
3568 Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
3569 if (!Vcb->devices) {
3570 ERR("out of memory\n");
3571 Status = STATUS_INSUFFICIENT_RESOURCES;
3572 goto exit;
3573 }
3574
3575 Vcb->devices[0].devobj = DeviceToMount;
3576 RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
3577 init_device(Vcb, &Vcb->devices[0], FALSE);
3578 Vcb->devices[0].length = gli.Length.QuadPart;
3579
3580 if (Vcb->superblock.num_devices > 1)
3581 RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
3582
3583 Vcb->devices_loaded = 1;
3584
3585 TRACE("DeviceToMount = %p\n", DeviceToMount);
3586 TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb);
3587
3588 NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
3589 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
3590
3591 InitializeListHead(&Vcb->roots);
3592 InitializeListHead(&Vcb->drop_roots);
3593
3594 Vcb->log_to_phys_loaded = FALSE;
3595
3596 Vcb->max_inline = Vcb->superblock.node_size / 2;
3597
3598 add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
3599
3600 if (!Vcb->chunk_root) {
3601 ERR("Could not load chunk root.\n");
3602 Status = STATUS_INTERNAL_ERROR;
3603 goto exit;
3604 }
3605
3606 InitializeListHead(&Vcb->sys_chunks);
3607 Status = load_sys_chunks(Vcb);
3608 if (!NT_SUCCESS(Status)) {
3609 ERR("load_sys_chunks returned %08x\n", Status);
3610 goto exit;
3611 }
3612
3613 InitializeListHead(&Vcb->chunks);
3614 InitializeListHead(&Vcb->chunks_changed);
3615 InitializeListHead(&Vcb->trees);
3616 InitializeListHead(&Vcb->all_fcbs);
3617 InitializeListHead(&Vcb->dirty_fcbs);
3618 InitializeListHead(&Vcb->dirty_filerefs);
3619 InitializeListHead(&Vcb->shared_extents);
3620 InitializeListHead(&Vcb->sector_checksums);
3621
3622 KeInitializeSpinLock(&Vcb->dirty_fcbs_lock);
3623 KeInitializeSpinLock(&Vcb->dirty_filerefs_lock);
3624 KeInitializeSpinLock(&Vcb->shared_extents_lock);
3625
3626 InitializeListHead(&Vcb->DirNotifyList);
3627
3628 FsRtlNotifyInitializeSync(&Vcb->NotifySync);
3629
3630 Status = load_chunk_root(Vcb);
3631 if (!NT_SUCCESS(Status)) {
3632 ERR("load_chunk_root returned %08x\n", Status);
3633 goto exit;
3634 }
3635
3636 if (Vcb->superblock.num_devices > 1) {
3637 if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3638 ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
3639
3640 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
3641
3642 Status = STATUS_INTERNAL_ERROR;
3643 goto exit;
3644 }
3645
3646 if (!raid_generations_okay(Vcb)) {
3647 ERR("could not mount as generation mismatch\n");
3648
3649 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
3650
3651 Status = STATUS_INTERNAL_ERROR;
3652 goto exit;
3653 }
3654 }
3655
3656 add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
3657
3658 if (!Vcb->root_root) {
3659 ERR("Could not load root of roots.\n");
3660 Status = STATUS_INTERNAL_ERROR;
3661 goto exit;
3662 }
3663
3664 Status = look_for_roots(Vcb);
3665 if (!NT_SUCCESS(Status)) {
3666 ERR("look_for_roots returned %08x\n", Status);
3667 goto exit;
3668 }
3669
3670 Status = find_chunk_usage(Vcb);
3671 if (!NT_SUCCESS(Status)) {
3672 ERR("find_chunk_usage returned %08x\n", Status);
3673 goto exit;
3674 }
3675
3676 // We've already increased the generation by one
3677 if (!Vcb->readonly && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) {
3678 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
3679 Status = clear_free_space_cache(Vcb);
3680 if (!NT_SUCCESS(Status)) {
3681 ERR("clear_free_space_cache returned %08x\n", Status);
3682 goto exit;
3683 }
3684 }
3685
3686 Vcb->volume_fcb = create_fcb();
3687 if (!Vcb->volume_fcb) {
3688 ERR("out of memory\n");
3689 Status = STATUS_INSUFFICIENT_RESOURCES;
3690 goto exit;
3691 }
3692
3693 Vcb->volume_fcb->Vcb = Vcb;
3694 Vcb->volume_fcb->sd = NULL;
3695
3696 root_fcb = create_fcb();
3697 if (!root_fcb) {
3698 ERR("out of memory\n");
3699 Status = STATUS_INSUFFICIENT_RESOURCES;
3700 goto exit;
3701 }
3702
3703 root_fcb->Vcb = Vcb;
3704 root_fcb->inode = SUBVOL_ROOT_INODE;
3705 root_fcb->type = BTRFS_TYPE_DIRECTORY;
3706
3707 #ifdef DEBUG_FCB_REFCOUNTS
3708 WARN("volume FCB = %p\n", Vcb->volume_fcb);
3709 WARN("root FCB = %p\n", root_fcb);
3710 #endif
3711
3712 root_fcb->subvol = find_default_subvol(Vcb);
3713
3714 if (!root_fcb->subvol) {
3715 ERR("could not find top subvol\n");
3716 Status = STATUS_INTERNAL_ERROR;
3717 goto exit;
3718 }
3719
3720 searchkey.obj_id = root_fcb->inode;
3721 searchkey.obj_type = TYPE_INODE_ITEM;
3722 searchkey.offset = 0xffffffffffffffff;
3723
3724 Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE);
3725 if (!NT_SUCCESS(Status)) {
3726 ERR("error - find_item returned %08x\n", Status);
3727 goto exit;
3728 }
3729
3730 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
3731 ERR("couldn't find INODE_ITEM for root directory\n");
3732 Status = STATUS_INTERNAL_ERROR;
3733 goto exit;
3734 }
3735
3736 if (tp.item->size > 0)
3737 RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
3738
3739 fcb_get_sd(root_fcb, NULL);
3740
3741 root_fcb->atts = get_file_attributes(Vcb, &root_fcb->inode_item, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE);
3742
3743 Vcb->root_fileref = create_fileref();
3744 if (!Vcb->root_fileref) {
3745 ERR("out of memory\n");
3746 Status = STATUS_INSUFFICIENT_RESOURCES;
3747 goto exit;
3748 }
3749
3750 Vcb->root_fileref->fcb = root_fcb;
3751 InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
3752 InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
3753
3754 root_fcb->fileref = Vcb->root_fileref;
3755
3756 root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
3757 if (!root_ccb) {
3758 ERR("out of memory\n");
3759 Status = STATUS_INSUFFICIENT_RESOURCES;
3760 goto exit;
3761 }
3762
3763 Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
3764 Vcb->root_file->FsContext = root_fcb;
3765
3766 RtlZeroMemory(root_ccb, sizeof(ccb));
3767 root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
3768 root_ccb->NodeSize = sizeof(ccb);
3769
3770 Vcb->root_file->FsContext = root_ccb;
3771
3772 for (i = 0; i < Vcb->superblock.num_devices; i++) {
3773 Status = find_disk_holes(Vcb, &Vcb->devices[i]);
3774 if (!NT_SUCCESS(Status)) {
3775 ERR("find_disk_holes returned %08x\n", Status);
3776 goto exit;
3777 }
3778 }
3779
3780 // root_test(Vcb);
3781
3782 KeInitializeSpinLock(&Vcb->FcbListLock);
3783
3784 NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
3785 Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
3786 Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
3787 NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
3788 NewDeviceObject->Vpb->VolumeLabel[0] = '?';
3789 NewDeviceObject->Vpb->VolumeLabel[1] = 0;
3790 NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
3791 Vcb->Vpb = NewDeviceObject->Vpb;
3792
3793 KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE);
3794
3795 Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
3796 if (!NT_SUCCESS(Status)) {
3797 ERR("PsCreateSystemThread returned %08x\n", Status);
3798 goto exit;
3799 }
3800
3801 Status = create_worker_threads(NewDeviceObject);
3802 if (!NT_SUCCESS(Status)) {
3803 ERR("create_worker_threads returned %08x\n", Status);
3804 goto exit;
3805 }
3806
3807 Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
3808 if (!NT_SUCCESS(Status))
3809 WARN("registry_mark_volume_mounted returned %08x\n", Status);
3810
3811 Status = STATUS_SUCCESS;
3812
3813 exit:
3814 if (Vcb) {
3815 ExReleaseResourceLite(&Vcb->load_lock);
3816 }
3817
3818 if (!NT_SUCCESS(Status)) {
3819 if (Vcb) {
3820 if (Vcb->root_file)
3821 ObDereferenceObject(Vcb->root_file);
3822 else if (Vcb->root_fileref)
3823 free_fileref(Vcb->root_fileref);
3824 else if (root_fcb)
3825 free_fcb(root_fcb);
3826
3827 if (Vcb->volume_fcb)
3828 free_fcb(Vcb->volume_fcb);
3829
3830 ExDeleteResourceLite(&Vcb->tree_lock);
3831 ExDeleteResourceLite(&Vcb->load_lock);
3832 ExDeleteResourceLite(&Vcb->fcb_lock);
3833 ExDeleteResourceLite(&Vcb->DirResource);
3834 ExDeleteResourceLite(&Vcb->checksum_lock);
3835 ExDeleteResourceLite(&Vcb->chunk_lock);
3836
3837 if (Vcb->devices)
3838 ExFreePoolWithTag(Vcb->devices, ALLOC_TAG);
3839
3840 RemoveEntryList(&Vcb->list_entry);
3841 }
3842
3843 if (NewDeviceObject)
3844 IoDeleteDevice(NewDeviceObject);
3845 } else
3846 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
3847
3848 TRACE("mount_vol done (status: %lx)\n", Status);
3849
3850 return Status;
3851 }
3852
3853 static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3854 PIO_STACK_LOCATION IrpSp;
3855 NTSTATUS Status;
3856 device_extension* Vcb = DeviceObject->DeviceExtension;
3857 BOOL top_level;
3858
3859 TRACE("file system control\n");
3860
3861 FsRtlEnterFileSystem();
3862
3863 top_level = is_top_level(Irp);
3864
3865 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
3866 Status = part0_passthrough(DeviceObject, Irp);
3867 goto exit;
3868 }
3869
3870 Status = STATUS_NOT_IMPLEMENTED;
3871
3872 IrpSp = IoGetCurrentIrpStackLocation( Irp );
3873
3874 Irp->IoStatus.Information = 0;
3875
3876 switch (IrpSp->MinorFunction) {
3877 case IRP_MN_MOUNT_VOLUME:
3878 TRACE("IRP_MN_MOUNT_VOLUME\n");
3879
3880 Status = mount_vol(DeviceObject, Irp);
3881 break;
3882
3883 case IRP_MN_KERNEL_CALL:
3884 TRACE("IRP_MN_KERNEL_CALL\n");
3885
3886 Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, FALSE);
3887 break;
3888
3889 case IRP_MN_USER_FS_REQUEST:
3890 TRACE("IRP_MN_USER_FS_REQUEST\n");
3891
3892 Status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE);
3893 break;
3894
3895 case IRP_MN_VERIFY_VOLUME:
3896 FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
3897 break;
3898
3899 default:
3900 break;
3901 }
3902
3903 Irp->IoStatus.Status = Status;
3904
3905 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3906
3907 exit:
3908 if (top_level)
3909 IoSetTopLevelIrp(NULL);
3910
3911 FsRtlExitFileSystem();
3912
3913 return Status;
3914 }
3915
3916 static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3917 NTSTATUS Status;
3918 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3919 fcb* fcb = IrpSp->FileObject->FsContext;
3920 device_extension* Vcb = DeviceObject->DeviceExtension;
3921 BOOL top_level;
3922
3923 FsRtlEnterFileSystem();
3924
3925 top_level = is_top_level(Irp);
3926
3927 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
3928 Status = part0_passthrough(DeviceObject, Irp);
3929 goto exit;
3930 }
3931
3932 TRACE("lock control\n");
3933
3934 Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
3935
3936 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
3937
3938 exit:
3939 if (top_level)
3940 IoSetTopLevelIrp(NULL);
3941
3942 FsRtlExitFileSystem();
3943
3944 return Status;
3945 }
3946
3947 NTSTATUS part0_passthrough(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3948 NTSTATUS Status;
3949 part0_device_extension* p0de = DeviceObject->DeviceExtension;
3950
3951 IoSkipCurrentIrpStackLocation(Irp);
3952
3953 Status = IoCallDriver(p0de->devobj, Irp);
3954
3955 return Status;
3956 }
3957
3958 static NTSTATUS part0_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
3959 NTSTATUS Status;
3960 part0_device_extension* p0de = DeviceObject->DeviceExtension;
3961 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
3962
3963 TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
3964
3965 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
3966 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
3967 {
3968 MOUNTDEV_UNIQUE_ID* mduid;
3969
3970 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
3971 Status = STATUS_BUFFER_TOO_SMALL;
3972 Irp->IoStatus.Status = Status;
3973 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
3974 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3975 return Status;
3976 }
3977
3978 mduid = Irp->AssociatedIrp.SystemBuffer;
3979 mduid->UniqueIdLength = sizeof(BTRFS_UUID);
3980
3981 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength) {
3982 Status = STATUS_BUFFER_OVERFLOW;
3983 Irp->IoStatus.Status = Status;
3984 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
3985 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3986 return Status;
3987 }
3988
3989 RtlCopyMemory(mduid->UniqueId, &p0de->uuid, sizeof(BTRFS_UUID));
3990
3991 Status = STATUS_SUCCESS;
3992 Irp->IoStatus.Status = Status;
3993 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength;
3994 IoCompleteRequest(Irp, IO_NO_INCREMENT);
3995
3996 return Status;
3997 }
3998
3999 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
4000 {
4001 PMOUNTDEV_NAME name;
4002
4003 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
4004 Status = STATUS_BUFFER_TOO_SMALL;
4005 Irp->IoStatus.Status = Status;
4006 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4007 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4008 return Status;
4009 }
4010
4011 name = Irp->AssociatedIrp.SystemBuffer;
4012 name->NameLength = p0de->name.Length;
4013
4014 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME) - 1 + name->NameLength) {
4015 Status = STATUS_BUFFER_OVERFLOW;
4016 Irp->IoStatus.Status = Status;
4017 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4018 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4019 return Status;
4020 }
4021
4022 RtlCopyMemory(name->Name, p0de->name.Buffer, p0de->name.Length);
4023
4024 Status = STATUS_SUCCESS;
4025 Irp->IoStatus.Status = Status;
4026 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME) - 1 + name->NameLength;
4027 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4028
4029 return Status;
4030 }
4031 }
4032
4033 IoSkipCurrentIrpStackLocation(Irp);
4034
4035 Status = IoCallDriver(p0de->devobj, Irp);
4036
4037 TRACE("returning %08x\n", Status);
4038
4039 return Status;
4040 }
4041
4042 static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4043 NTSTATUS Status;
4044 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4045 PFILE_OBJECT FileObject = IrpSp->FileObject;
4046 device_extension* Vcb = DeviceObject->DeviceExtension;
4047 fcb* fcb;
4048 BOOL top_level;
4049
4050 FsRtlEnterFileSystem();
4051
4052 top_level = is_top_level(Irp);
4053
4054 Irp->IoStatus.Information = 0;
4055
4056 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
4057 Status = part0_device_control(DeviceObject, Irp);
4058 goto end2;
4059 }
4060
4061 TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
4062
4063 if (!FileObject) {
4064 ERR("FileObject was NULL\n");
4065 Status = STATUS_INVALID_PARAMETER;
4066 goto end;
4067 }
4068
4069 fcb = FileObject->FsContext;
4070
4071 if (!fcb) {
4072 ERR("FCB was NULL\n");
4073 Status = STATUS_INVALID_PARAMETER;
4074 goto end;
4075 }
4076
4077 if (fcb != Vcb->volume_fcb) {
4078 Status = STATUS_NOT_IMPLEMENTED;
4079 goto end;
4080 }
4081
4082 IoSkipCurrentIrpStackLocation(Irp);
4083
4084 Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
4085
4086 goto end2;
4087
4088 end:
4089 Irp->IoStatus.Status = Status;
4090
4091 if (Status != STATUS_PENDING)
4092 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4093
4094 end2:
4095 if (top_level)
4096 IoSetTopLevelIrp(NULL);
4097
4098 FsRtlExitFileSystem();
4099
4100 return Status;
4101 }
4102
4103 static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4104 NTSTATUS Status;
4105 BOOL top_level;
4106 device_extension* Vcb = DeviceObject->DeviceExtension;
4107
4108 TRACE("shutdown\n");
4109
4110 FsRtlEnterFileSystem();
4111
4112 top_level = is_top_level(Irp);
4113
4114 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
4115 Status = part0_passthrough(DeviceObject, Irp);
4116 goto exit;
4117 }
4118
4119 Status = STATUS_SUCCESS;
4120
4121 while (!IsListEmpty(&VcbList)) {
4122 LIST_ENTRY* le = RemoveHeadList(&VcbList);
4123 Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
4124
4125 TRACE("shutting down Vcb %p\n", Vcb);
4126
4127 uninit(Vcb, TRUE);
4128 }
4129
4130 Irp->IoStatus.Status = Status;
4131 Irp->IoStatus.Information = 0;
4132
4133 IoCompleteRequest( Irp, IO_NO_INCREMENT );
4134
4135 exit:
4136 if (top_level)
4137 IoSetTopLevelIrp(NULL);
4138
4139 FsRtlExitFileSystem();
4140
4141 return Status;
4142 }
4143
4144 BOOL is_file_name_valid(PUNICODE_STRING us) {
4145 ULONG i;
4146
4147 if (us->Length < sizeof(WCHAR))
4148 return FALSE;
4149
4150 if (us->Length > 255 * sizeof(WCHAR))
4151 return FALSE;
4152
4153 for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
4154 if (us->Buffer[i] == '/' || us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
4155 us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))
4156 return FALSE;
4157 }
4158
4159 if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
4160 return FALSE;
4161
4162 return TRUE;
4163 }
4164
4165 #ifdef _DEBUG
4166 static void STDCALL init_serial() {
4167 NTSTATUS Status;
4168
4169 Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
4170 if (!NT_SUCCESS(Status)) {
4171 ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
4172 }
4173 }
4174 #endif
4175
4176 #ifndef __REACTOS__
4177 static void STDCALL check_cpu() {
4178 unsigned int cpuInfo[4];
4179 #ifndef _MSC_VER
4180 __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
4181 have_sse42 = cpuInfo[2] & bit_SSE4_2;
4182 #else
4183 __cpuid(cpuInfo, 1);
4184 have_sse42 = cpuInfo[2] & (1 << 20);
4185 #endif
4186
4187 if (have_sse42)
4188 TRACE("SSE4.2 is supported\n");
4189 else
4190 TRACE("SSE4.2 not supported\n");
4191 }
4192 #endif
4193
4194 #ifdef _DEBUG
4195 static void init_logging() {
4196 if (log_device.Length > 0)
4197 init_serial();
4198 else if (log_file.Length > 0) {
4199 NTSTATUS Status;
4200 OBJECT_ATTRIBUTES oa;
4201 IO_STATUS_BLOCK iosb;
4202 char* dateline;
4203 LARGE_INTEGER time;
4204 TIME_FIELDS tf;
4205
4206 InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
4207
4208 Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
4209 FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
4210
4211 if (!NT_SUCCESS(Status)) {
4212 ERR("ZwCreateFile returned %08x\n", Status);
4213 return;
4214 }
4215
4216 if (iosb.Information == FILE_OPENED) { // already exists
4217 FILE_STANDARD_INFORMATION fsi;
4218 FILE_POSITION_INFORMATION fpi;
4219
4220 static char delim[] = "\n---\n";
4221
4222 // move to end of file
4223
4224 Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
4225
4226 if (!NT_SUCCESS(Status)) {
4227 ERR("ZwQueryInformationFile returned %08x\n", Status);
4228 return;
4229 }
4230
4231 fpi.CurrentByteOffset = fsi.EndOfFile;
4232
4233 Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
4234
4235 if (!NT_SUCCESS(Status)) {
4236 ERR("ZwSetInformationFile returned %08x\n", Status);
4237 return;
4238 }
4239
4240 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim, strlen(delim), NULL, NULL);
4241
4242 if (!NT_SUCCESS(Status)) {
4243 ERR("ZwWriteFile returned %08x\n", Status);
4244 return;
4245 }
4246 }
4247
4248 dateline = ExAllocatePoolWithTag(PagedPool, 256, ALLOC_TAG);
4249
4250 if (!dateline) {
4251 ERR("out of memory\n");
4252 return;
4253 }
4254
4255 KeQuerySystemTime(&time);
4256
4257 RtlTimeToTimeFields(&time, &tf);
4258
4259 sprintf(dateline, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
4260
4261 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, strlen(dateline), NULL, NULL);
4262
4263 if (!NT_SUCCESS(Status)) {
4264 ERR("ZwWriteFile returned %08x\n", Status);
4265 return;
4266 }
4267
4268 ExFreePool(dateline);
4269 }
4270 }
4271 #endif
4272
4273 NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
4274 NTSTATUS Status;
4275 PDEVICE_OBJECT DeviceObject;
4276 UNICODE_STRING device_nameW;
4277 UNICODE_STRING dosdevice_nameW;
4278
4279 InitializeListHead(&uid_map_list);
4280
4281 log_device.Buffer = NULL;
4282 log_device.Length = log_device.MaximumLength = 0;
4283 log_file.Buffer = NULL;
4284 log_file.Length = log_file.MaximumLength = 0;
4285
4286 read_registry(RegistryPath);
4287
4288 #ifdef _DEBUG
4289 if (debug_log_level > 0)
4290 init_logging();
4291
4292 log_started = TRUE;
4293 #endif
4294
4295 TRACE("DriverEntry\n");
4296
4297 registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
4298 registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
4299
4300 if (!registry_path.Buffer) {
4301 ERR("out of memory\n");
4302 return STATUS_INSUFFICIENT_RESOURCES;
4303 }
4304
4305 RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
4306
4307 #ifndef __REACTOS__
4308 check_cpu();
4309 #endif
4310
4311 // TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
4312
4313 drvobj = DriverObject;
4314
4315 DriverObject->DriverUnload = DriverUnload;
4316
4317 DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)drv_create;
4318 DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)drv_close;
4319 DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)drv_read;
4320 DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)drv_write;
4321 DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)drv_query_information;
4322 DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)drv_set_information;
4323 DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)drv_query_ea;
4324 DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)drv_set_ea;
4325 DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)drv_flush_buffers;
4326 DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
4327 DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_set_volume_information;
4328 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)drv_cleanup;
4329 DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)drv_directory_control;
4330 DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)drv_file_system_control;
4331 DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)drv_lock_control;
4332 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)drv_device_control;
4333 DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)drv_shutdown;
4334 DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)drv_pnp;
4335 DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = (PDRIVER_DISPATCH)drv_query_security;
4336 DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = (PDRIVER_DISPATCH)drv_set_security;
4337
4338 init_fast_io_dispatch(&DriverObject->FastIoDispatch);
4339
4340 device_nameW.Buffer = device_name;
4341 device_nameW.Length = device_nameW.MaximumLength = (USHORT)wcslen(device_name) * sizeof(WCHAR);
4342 dosdevice_nameW.Buffer = dosdevice_name;
4343 dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
4344
4345 Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
4346 if (!NT_SUCCESS(Status)) {
4347 ERR("IoCreateDevice returned %08x\n", Status);
4348 return Status;
4349 }
4350
4351 devobj = DeviceObject;
4352
4353 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
4354
4355 Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
4356 if (!NT_SUCCESS(Status)) {
4357 ERR("IoCreateSymbolicLink returned %08x\n", Status);
4358 return Status;
4359 }
4360
4361 Status = init_cache();
4362 if (!NT_SUCCESS(Status)) {
4363 ERR("init_cache returned %08x\n", Status);
4364 return Status;
4365 }
4366
4367 InitializeListHead(&volumes);
4368 look_for_vols(DriverObject, &volumes);
4369
4370 InitializeListHead(&VcbList);
4371 ExInitializeResourceLite(&global_loading_lock);
4372
4373 IoRegisterFileSystem(DeviceObject);
4374
4375 return STATUS_SUCCESS;
4376 }