[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) {