[PRINTING]
[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_MIXED_GROUPS | \
39 BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | \
40 BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
41 #define COMPAT_RO_SUPPORTED 0
42
43 static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
44 static WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
45
46 PDRIVER_OBJECT drvobj;
47 PDEVICE_OBJECT devobj;
48 #ifndef __REACTOS__
49 BOOL have_sse42 = FALSE;
50 #endif
51 UINT64 num_reads = 0;
52 LIST_ENTRY uid_map_list;
53 LIST_ENTRY volumes;
54 LIST_ENTRY VcbList;
55 ERESOURCE global_loading_lock;
56 UINT32 debug_log_level = 0;
57 UINT32 mount_compress = 0;
58 UINT32 mount_compress_force = 0;
59 UINT32 mount_compress_type = 0;
60 UINT32 mount_zlib_level = 3;
61 UINT32 mount_flush_interval = 30;
62 UINT32 mount_max_inline = 2048;
63 BOOL log_started = FALSE;
64 UNICODE_STRING log_device, log_file, registry_path;
65
66 #ifdef _DEBUG
67 PFILE_OBJECT comfo = NULL;
68 PDEVICE_OBJECT comdo = NULL;
69 HANDLE log_handle = NULL;
70 #endif
71
72 int __security_cookie = __LINE__;
73
74 static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject);
75
76 typedef struct {
77 KEVENT Event;
78 IO_STATUS_BLOCK iosb;
79 } read_context;
80
81 #ifdef _DEBUG
82 static NTSTATUS STDCALL dbg_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
83 read_context* context = conptr;
84
85 // DbgPrint("dbg_completion\n");
86
87 context->iosb = Irp->IoStatus;
88 KeSetEvent(&context->Event, 0, FALSE);
89
90 // return STATUS_SUCCESS;
91 return STATUS_MORE_PROCESSING_REQUIRED;
92 }
93
94 #ifdef DEBUG_LONG_MESSAGES
95 void STDCALL _debug_message(const char* func, const char* file, unsigned int line, char* s, ...) {
96 #else
97 void STDCALL _debug_message(const char* func, char* s, ...) {
98 #endif
99 LARGE_INTEGER offset;
100 PIO_STACK_LOCATION IrpSp;
101 NTSTATUS Status;
102 PIRP Irp;
103 va_list ap;
104 char *buf2 = NULL, *buf;
105 read_context* context = NULL;
106 UINT32 length;
107
108 buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
109
110 if (!buf2) {
111 DbgPrint("Couldn't allocate buffer in debug_message\n");
112 return;
113 }
114
115 #ifdef DEBUG_LONG_MESSAGES
116 sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
117 #else
118 sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
119 #endif
120 buf = &buf2[strlen(buf2)];
121
122 va_start(ap, s);
123 vsprintf(buf, s, ap);
124
125 if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
126 DbgPrint(buf2);
127 } else if (log_device.Length > 0) {
128 if (!comdo) {
129 DbgPrint("comdo is NULL :-(\n");
130 DbgPrint(buf2);
131 goto exit2;
132 }
133
134 length = (UINT32)strlen(buf2);
135
136 offset.u.LowPart = 0;
137 offset.u.HighPart = 0;
138
139 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
140 if (!context) {
141 DbgPrint("Couldn't allocate context in debug_message\n");
142 return;
143 }
144
145 RtlZeroMemory(context, sizeof(read_context));
146
147 KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
148
149 // status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
150
151 Irp = IoAllocateIrp(comdo->StackSize, FALSE);
152
153 if (!Irp) {
154 DbgPrint("IoAllocateIrp failed\n");
155 goto exit2;
156 }
157
158 IrpSp = IoGetNextIrpStackLocation(Irp);
159 IrpSp->MajorFunction = IRP_MJ_WRITE;
160
161 if (comdo->Flags & DO_BUFFERED_IO) {
162 Irp->AssociatedIrp.SystemBuffer = buf2;
163
164 Irp->Flags = IRP_BUFFERED_IO;
165 } else if (comdo->Flags & DO_DIRECT_IO) {
166 Irp->MdlAddress = IoAllocateMdl(buf2, length, FALSE, FALSE, NULL);
167 if (!Irp->MdlAddress) {
168 DbgPrint("IoAllocateMdl failed\n");
169 goto exit;
170 }
171
172 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
173 } else {
174 Irp->UserBuffer = buf2;
175 }
176
177 IrpSp->Parameters.Write.Length = length;
178 IrpSp->Parameters.Write.ByteOffset = offset;
179
180 Irp->UserIosb = &context->iosb;
181
182 Irp->UserEvent = &context->Event;
183
184 IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE);
185
186 Status = IoCallDriver(comdo, Irp);
187
188 if (Status == STATUS_PENDING) {
189 KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
190 Status = context->iosb.Status;
191 }
192
193 if (comdo->Flags & DO_DIRECT_IO) {
194 MmUnlockPages(Irp->MdlAddress);
195 IoFreeMdl(Irp->MdlAddress);
196 }
197
198 if (!NT_SUCCESS(Status)) {
199 DbgPrint("failed to write to COM1 - error %08x\n", Status);
200 goto exit;
201 }
202
203 exit:
204 IoFreeIrp(Irp);
205 } else if (log_handle != NULL) {
206 IO_STATUS_BLOCK iosb;
207
208 length = (UINT32)strlen(buf2);
209
210 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
211
212 if (!NT_SUCCESS(Status)) {
213 DbgPrint("failed to write to file - error %08x\n", Status);
214 }
215 }
216
217 exit2:
218 va_end(ap);
219
220 if (context)
221 ExFreePool(context);
222
223 if (buf2)
224 ExFreePool(buf2);
225 }
226 #endif
227
228 UINT64 sector_align( UINT64 NumberToBeAligned, UINT64 Alignment )
229 {
230 if( Alignment & ( Alignment - 1 ) )
231 {
232 //
233 // Alignment not a power of 2
234 // Just returning
235 //
236 return NumberToBeAligned;
237 }
238 if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
239 {
240 NumberToBeAligned = NumberToBeAligned + Alignment;
241 NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
242 }
243 return NumberToBeAligned;
244 }
245
246 int keycmp(const KEY* key1, const KEY* key2) {
247 if (key1->obj_id < key2->obj_id) {
248 return -1;
249 } else if (key1->obj_id > key2->obj_id) {
250 return 1;
251 }
252
253 if (key1->obj_type < key2->obj_type) {
254 return -1;
255 } else if (key1->obj_type > key2->obj_type) {
256 return 1;
257 }
258
259 if (key1->offset < key2->offset) {
260 return -1;
261 } else if (key1->offset > key2->offset) {
262 return 1;
263 }
264
265 return 0;
266 }
267
268 BOOL is_top_level(PIRP Irp) {
269 if (!IoGetTopLevelIrp()) {
270 IoSetTopLevelIrp(Irp);
271 return TRUE;
272 }
273
274 return FALSE;
275 }
276
277 static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
278 UNICODE_STRING dosdevice_nameW;
279
280 ERR("DriverUnload\n");
281
282 free_cache();
283
284 IoUnregisterFileSystem(DriverObject->DeviceObject);
285
286 dosdevice_nameW.Buffer = dosdevice_name;
287 dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
288
289 IoDeleteSymbolicLink(&dosdevice_nameW);
290 IoDeleteDevice(DriverObject->DeviceObject);
291
292 while (!IsListEmpty(&uid_map_list)) {
293 LIST_ENTRY* le = RemoveHeadList(&uid_map_list);
294 uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
295
296 ExFreePool(um->sid);
297
298 ExFreePool(um);
299 }
300
301 // FIXME - free volumes and their devpaths
302
303 #ifdef _DEBUG
304 if (comfo)
305 ObDereferenceObject(comfo);
306
307 if (log_handle)
308 ZwClose(log_handle);
309 #endif
310
311 ExDeleteResourceLite(&global_loading_lock);
312
313 if (log_device.Buffer)
314 ExFreePool(log_device.Buffer);
315
316 if (log_file.Buffer)
317 ExFreePool(log_file.Buffer);
318
319 if (registry_path.Buffer)
320 ExFreePool(registry_path.Buffer);
321 }
322
323 BOOL STDCALL get_last_inode(device_extension* Vcb, root* r, PIRP Irp) {
324 KEY searchkey;
325 traverse_ptr tp, prev_tp;
326 NTSTATUS Status;
327
328 // get last entry
329 searchkey.obj_id = 0xffffffffffffffff;
330 searchkey.obj_type = 0xff;
331 searchkey.offset = 0xffffffffffffffff;
332
333 Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
334 if (!NT_SUCCESS(Status)) {
335 ERR("error - find_item returned %08x\n", Status);
336 return FALSE;
337 }
338
339 while (find_prev_item(Vcb, &tp, &prev_tp, FALSE, Irp)) {
340 tp = prev_tp;
341
342 TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
343
344 if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
345 r->lastinode = tp.item->key.obj_id;
346 TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
347 return TRUE;
348 }
349 }
350
351 r->lastinode = SUBVOL_ROOT_INODE;
352
353 WARN("no INODE_ITEMs in tree %llx\n", r->id);
354
355 return TRUE;
356 }
357
358 BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen, PIRP Irp) {
359 KEY searchkey;
360 traverse_ptr tp;
361 DIR_ITEM* xa;
362 ULONG size, xasize;
363 NTSTATUS Status;
364
365 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
366
367 searchkey.obj_id = inode;
368 searchkey.obj_type = TYPE_XATTR_ITEM;
369 searchkey.offset = crc32;
370
371 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
372 if (!NT_SUCCESS(Status)) {
373 ERR("error - find_item returned %08x\n", Status);
374 return FALSE;
375 }
376
377 if (keycmp(&tp.item->key, &searchkey)) {
378 TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
379 return FALSE;
380 }
381
382 if (tp.item->size < sizeof(DIR_ITEM)) {
383 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));
384 return FALSE;
385 }
386
387 xa = (DIR_ITEM*)tp.item->data;
388 size = tp.item->size;
389
390 while (TRUE) {
391 if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
392 WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
393 return FALSE;
394 }
395
396 if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
397 TRACE("found xattr %s in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
398
399 *datalen = xa->m;
400
401 if (xa->m > 0) {
402 *data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
403 if (!*data) {
404 ERR("out of memory\n");
405 return FALSE;
406 }
407
408 RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
409 } else
410 *data = NULL;
411
412 return TRUE;
413 }
414
415 xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
416
417 if (size > xasize) {
418 size -= xasize;
419 xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
420 } else
421 break;
422 }
423
424 TRACE("xattr %s not found in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
425
426 return FALSE;
427 }
428
429 NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, PIRP Irp, LIST_ENTRY* rollback) {
430 KEY searchkey;
431 traverse_ptr tp;
432 UINT8* di2;
433 NTSTATUS Status;
434
435 searchkey.obj_id = inode;
436 searchkey.obj_type = TYPE_DIR_ITEM;
437 searchkey.offset = crc32;
438
439 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
440 if (!NT_SUCCESS(Status)) {
441 ERR("error - find_item returned %08x\n", Status);
442 return Status;
443 }
444
445 if (!keycmp(&tp.item->key, &searchkey)) {
446 ULONG maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
447
448 if (tp.item->size + disize > maxlen) {
449 WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp.item->size, disize, maxlen);
450 return STATUS_INTERNAL_ERROR;
451 }
452
453 di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size + disize, ALLOC_TAG);
454 if (!di2) {
455 ERR("out of memory\n");
456 return STATUS_INSUFFICIENT_RESOURCES;
457 }
458
459 if (tp.item->size > 0)
460 RtlCopyMemory(di2, tp.item->data, tp.item->size);
461
462 RtlCopyMemory(di2 + tp.item->size, di, disize);
463
464 delete_tree_item(Vcb, &tp, rollback);
465
466 insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, Irp, rollback);
467
468 ExFreePool(di);
469 } else {
470 insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, Irp, rollback);
471 }
472
473 return STATUS_SUCCESS;
474 }
475
476 static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
477 NTSTATUS Status;
478 PIO_STACK_LOCATION IrpSp;
479 device_extension* Vcb = DeviceObject->DeviceExtension;
480 BOOL top_level;
481
482 TRACE("close\n");
483
484 FsRtlEnterFileSystem();
485
486 top_level = is_top_level(Irp);
487
488 if (DeviceObject == devobj || (Vcb && Vcb->type == VCB_TYPE_PARTITION0)) {
489 TRACE("Closing file system\n");
490 Status = STATUS_SUCCESS;
491 goto exit;
492 }
493
494 IrpSp = IoGetCurrentIrpStackLocation(Irp);
495
496 // FIXME - unmount if called for volume
497 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
498
499 Status = close_file(DeviceObject->DeviceExtension, IrpSp->FileObject);
500
501 exit:
502 Irp->IoStatus.Status = Status;
503 Irp->IoStatus.Information = 0;
504
505 IoCompleteRequest( Irp, IO_DISK_INCREMENT );
506
507 if (top_level)
508 IoSetTopLevelIrp(NULL);
509
510 FsRtlExitFileSystem();
511
512 TRACE("returning %08x\n", Status);
513
514 return Status;
515 }
516
517 static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
518 NTSTATUS Status;
519 BOOL top_level;
520 device_extension* Vcb = DeviceObject->DeviceExtension;
521
522 FsRtlEnterFileSystem();
523
524 top_level = is_top_level(Irp);
525
526 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
527 Status = part0_passthrough(DeviceObject, Irp);
528 goto exit;
529 }
530
531 FIXME("STUB: query ea\n");
532 Status = STATUS_NOT_IMPLEMENTED;
533
534 Irp->IoStatus.Status = Status;
535 Irp->IoStatus.Information = 0;
536
537 IoCompleteRequest( Irp, IO_NO_INCREMENT );
538
539 exit:
540 if (top_level)
541 IoSetTopLevelIrp(NULL);
542
543 FsRtlExitFileSystem();
544
545 return Status;
546 }
547
548 static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
549 NTSTATUS Status;
550 device_extension* Vcb = DeviceObject->DeviceExtension;
551 BOOL top_level;
552
553 FsRtlEnterFileSystem();
554
555 top_level = is_top_level(Irp);
556
557 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
558 Status = part0_passthrough(DeviceObject, Irp);
559 goto exit;
560 }
561
562 FIXME("STUB: set ea\n");
563 Status = STATUS_NOT_IMPLEMENTED;
564
565 if (Vcb->readonly)
566 Status = STATUS_MEDIA_WRITE_PROTECTED;
567
568 // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
569
570 Irp->IoStatus.Status = Status;
571 Irp->IoStatus.Information = 0;
572
573 IoCompleteRequest( Irp, IO_NO_INCREMENT );
574
575 exit:
576 if (top_level)
577 IoSetTopLevelIrp(NULL);
578
579 FsRtlExitFileSystem();
580
581 return Status;
582 }
583
584 static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
585 NTSTATUS Status;
586 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
587 PFILE_OBJECT FileObject = IrpSp->FileObject;
588 fcb* fcb = FileObject->FsContext;
589 device_extension* Vcb = DeviceObject->DeviceExtension;
590 BOOL top_level;
591
592 TRACE("flush buffers\n");
593
594 FsRtlEnterFileSystem();
595
596 top_level = is_top_level(Irp);
597
598 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
599 Status = part0_passthrough(DeviceObject, Irp);
600 goto exit;
601 }
602
603 Status = STATUS_SUCCESS;
604 Irp->IoStatus.Status = Status;
605 Irp->IoStatus.Information = 0;
606
607 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
608 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
609
610 if (fcb->Header.PagingIoResource) {
611 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
612 ExReleaseResourceLite(fcb->Header.PagingIoResource);
613 }
614
615 Status = Irp->IoStatus.Status;
616 }
617
618 IoCompleteRequest(Irp, IO_NO_INCREMENT);
619
620 exit:
621 if (top_level)
622 IoSetTopLevelIrp(NULL);
623
624 FsRtlExitFileSystem();
625
626 return Status;
627 }
628
629 static void calculate_total_space(device_extension* Vcb, LONGLONG* totalsize, LONGLONG* freespace) {
630 UINT8 factor;
631
632 if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10)
633 factor = 2;
634 else
635 factor = 1;
636
637 *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) / factor;
638 *freespace = ((Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size) / factor;
639 }
640
641 static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
642 PIO_STACK_LOCATION IrpSp;
643 NTSTATUS Status;
644 ULONG BytesCopied = 0;
645 device_extension* Vcb = DeviceObject->DeviceExtension;
646 BOOL top_level;
647
648 #ifndef __REACTOS__
649 // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
650 // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
651 // which prevents UAC from working.
652 // FIXME - only lie if we detect that we're being called by mpr.dll
653
654 WCHAR* fs_name = L"NTFS";
655 ULONG fs_name_len = 4 * sizeof(WCHAR);
656 #else
657 WCHAR* fs_name = L"Btrfs";
658 ULONG fs_name_len = 5 * sizeof(WCHAR);
659 #endif
660
661 TRACE("query volume information\n");
662
663 FsRtlEnterFileSystem();
664 top_level = is_top_level(Irp);
665
666 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
667 Status = part0_passthrough(DeviceObject, Irp);
668 goto exit;
669 }
670
671 IrpSp = IoGetCurrentIrpStackLocation(Irp);
672
673 Status = STATUS_NOT_IMPLEMENTED;
674
675 switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
676 case FileFsAttributeInformation:
677 {
678 FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
679 BOOL overflow = FALSE;
680 ULONG orig_fs_name_len = fs_name_len;
681
682 TRACE("FileFsAttributeInformation\n");
683
684 if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
685 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
686 fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
687 else
688 fs_name_len = 0;
689
690 overflow = TRUE;
691 }
692
693 data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
694 FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
695 FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS;
696 if (Vcb->readonly)
697 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
698
699 // should also be FILE_FILE_COMPRESSION when supported
700 data->MaximumComponentNameLength = 255; // FIXME - check
701 data->FileSystemNameLength = orig_fs_name_len;
702 RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
703
704 BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
705 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
706 break;
707 }
708
709 case FileFsControlInformation:
710 FIXME("STUB: FileFsControlInformation\n");
711 break;
712
713 case FileFsDeviceInformation:
714 {
715 FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
716
717 TRACE("FileFsDeviceInformation\n");
718
719 ffdi->DeviceType = FILE_DEVICE_DISK;
720 ffdi->Characteristics = Vcb->devices[0].devobj->Characteristics;
721
722 if (Vcb->readonly)
723 ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
724 else
725 ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
726
727 BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
728 Status = STATUS_SUCCESS;
729
730 break;
731 }
732
733 case FileFsDriverPathInformation:
734 FIXME("STUB: FileFsDriverPathInformation\n");
735 break;
736
737 case FileFsFullSizeInformation:
738 {
739 FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
740
741 TRACE("FileFsFullSizeInformation\n");
742
743 calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->ActualAvailableAllocationUnits.QuadPart);
744 ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
745 ffsi->SectorsPerAllocationUnit = 1;
746 ffsi->BytesPerSector = Vcb->superblock.sector_size;
747
748 BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
749 Status = STATUS_SUCCESS;
750
751 break;
752 }
753
754 case FileFsObjectIdInformation:
755 {
756 FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
757
758 TRACE("FileFsObjectIdInformation\n");
759
760 RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
761 RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
762
763 BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
764 Status = STATUS_SUCCESS;
765
766 break;
767 }
768
769 case FileFsSizeInformation:
770 {
771 FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
772
773 TRACE("FileFsSizeInformation\n");
774
775 calculate_total_space(Vcb, &ffsi->TotalAllocationUnits.QuadPart, &ffsi->AvailableAllocationUnits.QuadPart);
776 ffsi->SectorsPerAllocationUnit = 1;
777 ffsi->BytesPerSector = Vcb->superblock.sector_size;
778
779 BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
780 Status = STATUS_SUCCESS;
781
782 break;
783 }
784
785 case FileFsVolumeInformation:
786 {
787 FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
788 FILE_FS_VOLUME_INFORMATION ffvi;
789 BOOL overflow = FALSE;
790 ULONG label_len, orig_label_len;
791
792 TRACE("FileFsVolumeInformation\n");
793 TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
794
795 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
796
797 // orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
798 RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
799 orig_label_len = label_len;
800
801 if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
802 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
803 label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
804 else
805 label_len = 0;
806
807 overflow = TRUE;
808 }
809
810 TRACE("label_len = %u\n", label_len);
811
812 ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
813 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];
814 ffvi.VolumeLabelLength = orig_label_len;
815 ffvi.SupportsObjects = FALSE;
816
817 RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
818
819 if (label_len > 0) {
820 ULONG bytecount;
821
822 // RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
823 RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
824 TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
825 }
826
827 ExReleaseResourceLite(&Vcb->tree_lock);
828
829 BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
830 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
831 break;
832 }
833
834 default:
835 Status = STATUS_INVALID_PARAMETER;
836 WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
837 break;
838 }
839
840 // if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
841 // WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
842 // BytesCopied = IrpSp->Parameters.QueryVolume.Length;
843 // Status = STATUS_BUFFER_OVERFLOW;
844 // }
845
846 Irp->IoStatus.Status = Status;
847
848 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
849 Irp->IoStatus.Information = 0;
850 else
851 Irp->IoStatus.Information = BytesCopied;
852
853 IoCompleteRequest( Irp, IO_DISK_INCREMENT );
854
855 exit:
856 if (top_level)
857 IoSetTopLevelIrp(NULL);
858
859 FsRtlExitFileSystem();
860
861 TRACE("query volume information returning %08x\n", Status);
862
863 return Status;
864 }
865
866 static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
867 read_context* context = conptr;
868
869 // DbgPrint("read_completion\n");
870
871 context->iosb = Irp->IoStatus;
872 KeSetEvent(&context->Event, 0, FALSE);
873
874 // return STATUS_SUCCESS;
875 return STATUS_MORE_PROCESSING_REQUIRED;
876 }
877
878 // static void test_tree_deletion(device_extension* Vcb) {
879 // KEY searchkey/*, endkey*/;
880 // traverse_ptr tp, next_tp;
881 // root* r;
882 //
883 // searchkey.obj_id = 0x100;
884 // searchkey.obj_type = 0x54;
885 // searchkey.offset = 0xca4ab2f5;
886 //
887 // // endkey.obj_id = 0x100;
888 // // endkey.obj_type = 0x60;
889 // // endkey.offset = 0x15a;
890 //
891 // r = Vcb->roots;
892 // while (r && r->id != 0x102)
893 // r = r->next;
894 //
895 // if (!r) {
896 // ERR("error - could not find root\n");
897 // return;
898 // }
899 //
900 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
901 // ERR("error - could not find key\n");
902 // return;
903 // }
904 //
905 // while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
906 // tp.item->ignore = TRUE;
907 // add_to_tree_cache(tc, tp.tree);
908 //
909 // if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
910 // free_traverse_ptr(&tp);
911 // tp = next_tp;
912 // } else
913 // break;
914 // }
915 //
916 // free_traverse_ptr(&tp);
917 // }
918
919 // static void test_tree_splitting(device_extension* Vcb) {
920 // int i;
921 //
922 // for (i = 0; i < 1000; i++) {
923 // char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
924 //
925 // insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
926 // }
927 // }
928
929 // static void test_dropping_tree(device_extension* Vcb) {
930 // LIST_ENTRY* le = Vcb->roots.Flink;
931 //
932 // while (le != &Vcb->roots) {
933 // root* r = CONTAINING_RECORD(le, root, list_entry);
934 //
935 // if (r->id == 0x101) {
936 // RemoveEntryList(&r->list_entry);
937 // InsertTailList(&Vcb->drop_roots, &r->list_entry);
938 // return;
939 // }
940 //
941 // le = le->Flink;
942 // }
943 // }
944
945 NTSTATUS create_root(device_extension* Vcb, UINT64 id, root** rootptr, BOOL no_tree, UINT64 offset, PIRP Irp, LIST_ENTRY* rollback) {
946 root* r;
947 tree* t;
948 ROOT_ITEM* ri;
949 traverse_ptr tp;
950
951 r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
952 if (!r) {
953 ERR("out of memory\n");
954 return STATUS_INSUFFICIENT_RESOURCES;
955 }
956
957 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
958 if (!r->nonpaged) {
959 ERR("out of memory\n");
960 ExFreePool(r);
961 return STATUS_INSUFFICIENT_RESOURCES;
962 }
963
964 if (!no_tree) {
965 t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
966 if (!t) {
967 ERR("out of memory\n");
968 ExFreePool(r->nonpaged);
969 ExFreePool(r);
970 return STATUS_INSUFFICIENT_RESOURCES;
971 }
972 }
973
974 ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
975 if (!ri) {
976 ERR("out of memory\n");
977
978 if (!no_tree)
979 ExFreePool(t);
980
981 ExFreePool(r->nonpaged);
982 ExFreePool(r);
983 return STATUS_INSUFFICIENT_RESOURCES;
984 }
985
986 r->id = id;
987 r->treeholder.address = 0;
988 r->treeholder.generation = Vcb->superblock.generation;
989 r->treeholder.tree = no_tree ? NULL : t;
990 r->lastinode = 0;
991 r->path.Buffer = NULL;
992 RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
993 r->root_item.num_references = 1;
994 InitializeListHead(&r->fcbs);
995
996 RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
997
998 // We ask here for a traverse_ptr to the item we're inserting, so we can
999 // copy some of the tree's variables
1000
1001 if (!insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp, rollback)) {
1002 ERR("insert_tree_item failed\n");
1003 ExFreePool(ri);
1004
1005 if (!no_tree)
1006 ExFreePool(t);
1007
1008 ExFreePool(r->nonpaged);
1009 ExFreePool(r);
1010 return STATUS_INTERNAL_ERROR;
1011 }
1012
1013 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
1014
1015 InsertTailList(&Vcb->roots, &r->list_entry);
1016
1017 if (!no_tree) {
1018 t->header.fs_uuid = tp.tree->header.fs_uuid;
1019 t->header.address = 0;
1020 t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
1021 t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
1022 t->header.generation = Vcb->superblock.generation;
1023 t->header.tree_id = id;
1024 t->header.num_items = 0;
1025 t->header.level = 0;
1026
1027 t->has_address = FALSE;
1028 t->size = 0;
1029 t->Vcb = Vcb;
1030 t->parent = NULL;
1031 t->paritem = NULL;
1032 t->root = r;
1033
1034 InitializeListHead(&t->itemlist);
1035
1036 t->new_address = 0;
1037 t->has_new_address = FALSE;
1038 t->flags = tp.tree->flags;
1039
1040 InsertTailList(&Vcb->trees, &t->list_entry);
1041
1042 t->write = TRUE;
1043 Vcb->need_write = TRUE;
1044 }
1045
1046 *rootptr = r;
1047
1048 return STATUS_SUCCESS;
1049 }
1050
1051 // static void test_creating_root(device_extension* Vcb) {
1052 // NTSTATUS Status;
1053 // LIST_ENTRY rollback;
1054 // UINT64 id;
1055 // root* r;
1056 //
1057 // InitializeListHead(&rollback);
1058 //
1059 // if (Vcb->root_root->lastinode == 0)
1060 // get_last_inode(Vcb, Vcb->root_root);
1061 //
1062 // id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
1063 // Status = create_root(Vcb, id, &r, &rollback);
1064 //
1065 // if (!NT_SUCCESS(Status)) {
1066 // ERR("create_root returned %08x\n", Status);
1067 // do_rollback(Vcb, &rollback);
1068 // } else {
1069 // Vcb->root_root->lastinode = id;
1070 // clear_rollback(&rollback);
1071 // }
1072 // }
1073
1074 // static void test_alloc_chunk(device_extension* Vcb) {
1075 // LIST_ENTRY rollback;
1076 // chunk* c;
1077 //
1078 // InitializeListHead(&rollback);
1079 //
1080 // c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback);
1081 // if (!c) {
1082 // ERR("alloc_chunk failed\n");
1083 // do_rollback(Vcb, &rollback);
1084 // } else {
1085 // clear_rollback(&rollback);
1086 // }
1087 // }
1088
1089 // static void test_space_list(device_extension* Vcb) {
1090 // chunk* c;
1091 // int i, j;
1092 // LIST_ENTRY* le;
1093 //
1094 // typedef struct {
1095 // UINT64 address;
1096 // UINT64 length;
1097 // BOOL add;
1098 // } space_test;
1099 //
1100 // static const space_test entries[] = {
1101 // { 0x1000, 0x1000 },
1102 // { 0x3000, 0x2000 },
1103 // { 0x6000, 0x1000 },
1104 // { 0, 0 }
1105 // };
1106 //
1107 // static const space_test tests[] = {
1108 // { 0x0, 0x800, TRUE },
1109 // { 0x1800, 0x400, TRUE },
1110 // { 0x800, 0x2000, TRUE },
1111 // { 0x1000, 0x2000, TRUE },
1112 // { 0x2000, 0x3800, TRUE },
1113 // { 0x800, 0x1000, TRUE },
1114 // { 0x1800, 0x1000, TRUE },
1115 // { 0x5000, 0x800, TRUE },
1116 // { 0x5000, 0x1000, TRUE },
1117 // { 0x7000, 0x1000, TRUE },
1118 // { 0x8000, 0x1000, TRUE },
1119 // { 0x800, 0x800, TRUE },
1120 // { 0x0, 0x3800, TRUE },
1121 // { 0x1000, 0x2800, TRUE },
1122 // { 0x1000, 0x1000, FALSE },
1123 // { 0x800, 0x2000, FALSE },
1124 // { 0x0, 0x3800, FALSE },
1125 // { 0x2800, 0x1000, FALSE },
1126 // { 0x1800, 0x2000, FALSE },
1127 // { 0x3800, 0x1000, FALSE },
1128 // { 0, 0, FALSE }
1129 // };
1130 //
1131 // c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry);
1132 //
1133 // i = 0;
1134 // while (tests[i].length > 0) {
1135 // InitializeListHead(&c->space);
1136 // InitializeListHead(&c->space_size);
1137 // ERR("test %u\n", i);
1138 //
1139 // j = 0;
1140 // while (entries[j].length > 0) {
1141 // space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
1142 // s->address = entries[j].address;
1143 // s->size = entries[j].length;
1144 // InsertTailList(&c->space, &s->list_entry);
1145 //
1146 // order_space_entry(s, &c->space_size);
1147 //
1148 // j++;
1149 // }
1150 //
1151 // if (tests[i].add)
1152 // space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1153 // else
1154 // space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1155 //
1156 // le = c->space.Flink;
1157 // while (le != &c->space) {
1158 // space* s = CONTAINING_RECORD(le, space, list_entry);
1159 //
1160 // ERR("(%llx,%llx)\n", s->address, s->size);
1161 //
1162 // le = le->Flink;
1163 // }
1164 //
1165 // ERR("--\n");
1166 //
1167 // le = c->space_size.Flink;
1168 // while (le != &c->space_size) {
1169 // space* s = CONTAINING_RECORD(le, space, list_entry_size);
1170 //
1171 // ERR("(%llx,%llx)\n", s->address, s->size);
1172 //
1173 // le = le->Flink;
1174 // }
1175 //
1176 // i++;
1177 // }
1178 //
1179 // int3;
1180 // }
1181
1182 static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
1183 ULONG utf8len;
1184 NTSTATUS Status;
1185 USHORT vollen, i;
1186
1187 TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
1188
1189 vollen = ffli->VolumeLabelLength;
1190
1191 for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
1192 if (ffli->VolumeLabel[i] == 0) {
1193 vollen = i * sizeof(WCHAR);
1194 break;
1195 } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
1196 Status = STATUS_INVALID_VOLUME_LABEL;
1197 goto end;
1198 }
1199 }
1200
1201 if (vollen == 0) {
1202 utf8len = 0;
1203 } else {
1204 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
1205 if (!NT_SUCCESS(Status))
1206 goto end;
1207
1208 if (utf8len > MAX_LABEL_SIZE) {
1209 Status = STATUS_INVALID_VOLUME_LABEL;
1210 goto end;
1211 }
1212 }
1213
1214 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
1215
1216 if (utf8len > 0) {
1217 Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
1218 if (!NT_SUCCESS(Status))
1219 goto release;
1220 } else
1221 Status = STATUS_SUCCESS;
1222
1223 if (utf8len < MAX_LABEL_SIZE)
1224 RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
1225
1226 // test_tree_deletion(Vcb); // TESTING
1227 // test_tree_splitting(Vcb);
1228 // test_dropping_tree(Vcb);
1229 // test_creating_root(Vcb);
1230 // test_alloc_chunk(Vcb);
1231 // test_space_list(Vcb);
1232
1233 Vcb->need_write = TRUE;
1234
1235 release:
1236 ExReleaseResourceLite(&Vcb->tree_lock);
1237
1238 end:
1239 TRACE("returning %08x\n", Status);
1240
1241 return Status;
1242 }
1243
1244 static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
1245 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
1246 device_extension* Vcb = DeviceObject->DeviceExtension;
1247 NTSTATUS Status;
1248 BOOL top_level;
1249
1250 TRACE("set volume information\n");
1251
1252 FsRtlEnterFileSystem();
1253
1254 top_level = is_top_level(Irp);
1255
1256 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
1257 Status = part0_passthrough(DeviceObject, Irp);
1258 goto exit;
1259 }
1260
1261 Status = STATUS_NOT_IMPLEMENTED;
1262
1263 if (Vcb->readonly) {
1264 Status = STATUS_MEDIA_WRITE_PROTECTED;
1265 goto end;
1266 }
1267
1268 if (Vcb->removing || Vcb->locked) {
1269 Status = STATUS_ACCESS_DENIED;
1270 goto end;
1271 }
1272
1273 switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
1274 case FileFsControlInformation:
1275 FIXME("STUB: FileFsControlInformation\n");
1276 break;
1277
1278 case FileFsLabelInformation:
1279 TRACE("FileFsLabelInformation\n");
1280
1281 Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
1282 break;
1283
1284 case FileFsObjectIdInformation:
1285 FIXME("STUB: FileFsObjectIdInformation\n");
1286 break;
1287
1288 default:
1289 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
1290 break;
1291 }
1292
1293 end:
1294 Irp->IoStatus.Status = Status;
1295 Irp->IoStatus.Information = 0;
1296
1297 IoCompleteRequest( Irp, IO_NO_INCREMENT );
1298
1299 exit:
1300 if (top_level)
1301 IoSetTopLevelIrp(NULL);
1302
1303 FsRtlExitFileSystem();
1304
1305 return Status;
1306 }
1307
1308 NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
1309 KEY searchkey;
1310 traverse_ptr tp;
1311 NTSTATUS Status;
1312
1313 searchkey.obj_id = parinode;
1314 searchkey.obj_type = TYPE_DIR_ITEM;
1315 searchkey.offset = crc32;
1316
1317 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
1318 if (!NT_SUCCESS(Status)) {
1319 ERR("error - find_item returned %08x\n", Status);
1320 return Status;
1321 }
1322
1323 if (!keycmp(&searchkey, &tp.item->key)) {
1324 if (tp.item->size < sizeof(DIR_ITEM)) {
1325 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));
1326 } else {
1327 DIR_ITEM* di;
1328 LONG len;
1329
1330 di = (DIR_ITEM*)tp.item->data;
1331 len = tp.item->size;
1332
1333 do {
1334 if (di->n == utf8->Length && RtlCompareMemory(di->name, utf8->Buffer, di->n) == di->n) {
1335 ULONG newlen = tp.item->size - (sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m);
1336
1337 delete_tree_item(Vcb, &tp, rollback);
1338
1339 if (newlen == 0) {
1340 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1341 } else {
1342 UINT8 *newdi = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *dioff;
1343
1344 if (!newdi) {
1345 ERR("out of memory\n");
1346 return STATUS_INSUFFICIENT_RESOURCES;
1347 }
1348
1349 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1350
1351 if ((UINT8*)di > tp.item->data) {
1352 RtlCopyMemory(newdi, tp.item->data, (UINT8*)di - tp.item->data);
1353 dioff = newdi + ((UINT8*)di - tp.item->data);
1354 } else {
1355 dioff = newdi;
1356 }
1357
1358 if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
1359 RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
1360
1361 insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, Irp, rollback);
1362 }
1363
1364 break;
1365 }
1366
1367 len -= sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m;
1368 di = (DIR_ITEM*)&di->name[di->n + di->m];
1369 } while (len > 0);
1370 }
1371 } else {
1372 WARN("could not find DIR_ITEM for crc32 %08x\n", crc32);
1373 }
1374
1375 return STATUS_SUCCESS;
1376 }
1377
1378 NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
1379 KEY searchkey;
1380 traverse_ptr tp;
1381 BOOL changed = FALSE;
1382 NTSTATUS Status;
1383
1384 searchkey.obj_id = inode;
1385 searchkey.obj_type = TYPE_INODE_REF;
1386 searchkey.offset = parinode;
1387
1388 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
1389 if (!NT_SUCCESS(Status)) {
1390 ERR("error - find_item returned %08x\n", Status);
1391 return Status;
1392 }
1393
1394 if (!keycmp(&searchkey, &tp.item->key)) {
1395 if (tp.item->size < sizeof(INODE_REF)) {
1396 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));
1397 } else {
1398 INODE_REF* ir;
1399 ULONG len;
1400
1401 ir = (INODE_REF*)tp.item->data;
1402 len = tp.item->size;
1403
1404 do {
1405 ULONG itemlen;
1406
1407 if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
1408 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1409 break;
1410 }
1411
1412 itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
1413
1414 if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
1415 ULONG newlen = tp.item->size - itemlen;
1416
1417 delete_tree_item(Vcb, &tp, rollback);
1418 changed = TRUE;
1419
1420 if (newlen == 0) {
1421 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1422 } else {
1423 UINT8 *newir = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *iroff;
1424
1425 if (!newir) {
1426 ERR("out of memory\n");
1427 return STATUS_INSUFFICIENT_RESOURCES;
1428 }
1429
1430 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1431
1432 if ((UINT8*)ir > tp.item->data) {
1433 RtlCopyMemory(newir, tp.item->data, (UINT8*)ir - tp.item->data);
1434 iroff = newir + ((UINT8*)ir - tp.item->data);
1435 } else {
1436 iroff = newir;
1437 }
1438
1439 if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
1440 RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
1441
1442 insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, Irp, rollback);
1443 }
1444
1445 break;
1446 }
1447
1448 if (len > itemlen) {
1449 len -= itemlen;
1450 ir = (INODE_REF*)&ir->name[ir->n];
1451 } else
1452 break;
1453 } while (len > 0);
1454
1455 if (!changed) {
1456 WARN("found INODE_REF entry, but couldn't find filename\n");
1457 }
1458 }
1459 } else {
1460 WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey.obj_id, searchkey.offset);
1461 }
1462
1463 if (changed)
1464 return STATUS_SUCCESS;
1465
1466 if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF))
1467 return STATUS_INTERNAL_ERROR;
1468
1469 searchkey.obj_id = inode;
1470 searchkey.obj_type = TYPE_INODE_EXTREF;
1471 searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
1472
1473 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
1474 if (!NT_SUCCESS(Status)) {
1475 ERR("error - find_item returned %08x\n", Status);
1476 return Status;
1477 }
1478
1479 if (!keycmp(&searchkey, &tp.item->key)) {
1480 if (tp.item->size < sizeof(INODE_EXTREF)) {
1481 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));
1482 } else {
1483 INODE_EXTREF* ier;
1484 ULONG len;
1485
1486 ier = (INODE_EXTREF*)tp.item->data;
1487 len = tp.item->size;
1488
1489 do {
1490 ULONG itemlen;
1491
1492 if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
1493 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1494 break;
1495 }
1496
1497 itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
1498
1499 if (ier->dir == parinode && ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
1500 ULONG newlen = tp.item->size - itemlen;
1501
1502 delete_tree_item(Vcb, &tp, rollback);
1503 changed = TRUE;
1504
1505 if (newlen == 0) {
1506 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1507 } else {
1508 UINT8 *newier = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *ieroff;
1509
1510 if (!newier) {
1511 ERR("out of memory\n");
1512 return STATUS_INSUFFICIENT_RESOURCES;
1513 }
1514
1515 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1516
1517 if ((UINT8*)ier > tp.item->data) {
1518 RtlCopyMemory(newier, tp.item->data, (UINT8*)ier - tp.item->data);
1519 ieroff = newier + ((UINT8*)ier - tp.item->data);
1520 } else {
1521 ieroff = newier;
1522 }
1523
1524 if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
1525 RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
1526
1527 insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, Irp, rollback);
1528 }
1529
1530 break;
1531 }
1532
1533 if (len > itemlen) {
1534 len -= itemlen;
1535 ier = (INODE_EXTREF*)&ier->name[ier->n];
1536 } else
1537 break;
1538 } while (len > 0);
1539 }
1540 } else {
1541 WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32)searchkey.offset);
1542 }
1543
1544 return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
1545 }
1546
1547 static WCHAR* file_desc_fcb(fcb* fcb) {
1548 char s[60];
1549 UNICODE_STRING us;
1550 ANSI_STRING as;
1551
1552 if (fcb->debug_desc)
1553 return fcb->debug_desc;
1554
1555 if (fcb == fcb->Vcb->volume_fcb)
1556 return L"volume FCB";
1557
1558 fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
1559 if (!fcb->debug_desc)
1560 return L"(memory error)";
1561
1562 // I know this is pretty hackish...
1563 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1564 // without the CRT, which breaks drivers.
1565
1566 sprintf(s, "subvol %x, inode %x", (UINT32)fcb->subvol->id, (UINT32)fcb->inode);
1567
1568 as.Buffer = s;
1569 as.Length = as.MaximumLength = strlen(s);
1570
1571 us.Buffer = fcb->debug_desc;
1572 us.MaximumLength = 60 * sizeof(WCHAR);
1573 us.Length = 0;
1574
1575 RtlAnsiStringToUnicodeString(&us, &as, FALSE);
1576
1577 us.Buffer[us.Length / sizeof(WCHAR)] = 0;
1578
1579 return fcb->debug_desc;
1580 }
1581
1582 WCHAR* file_desc_fileref(file_ref* fileref) {
1583 NTSTATUS Status;
1584 UNICODE_STRING fn;
1585
1586 if (fileref->debug_desc)
1587 return fileref->debug_desc;
1588
1589 Status = fileref_get_filename(fileref, &fn, NULL);
1590 if (!NT_SUCCESS(Status)) {
1591 return L"ERROR";
1592 }
1593
1594 fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, fn.Length + sizeof(WCHAR), ALLOC_TAG);
1595 if (!fileref->debug_desc) {
1596 ExFreePool(fn.Buffer);
1597 return L"(memory error)";
1598 }
1599
1600 RtlCopyMemory(fileref->debug_desc, fn.Buffer, fn.Length);
1601 fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
1602
1603 ExFreePool(fn.Buffer);
1604
1605 return fileref->debug_desc;
1606 }
1607
1608 WCHAR* file_desc(PFILE_OBJECT FileObject) {
1609 fcb* fcb = FileObject->FsContext;
1610 ccb* ccb = FileObject->FsContext2;
1611 file_ref* fileref = ccb ? ccb->fileref : NULL;
1612
1613 if (fileref)
1614 return file_desc_fileref(fileref);
1615 else
1616 return file_desc_fcb(fcb);
1617 }
1618
1619 void send_notification_fileref(file_ref* fileref, ULONG filter_match, ULONG action) {
1620 UNICODE_STRING fn;
1621 NTSTATUS Status;
1622 USHORT name_offset;
1623 fcb* fcb = fileref->fcb;
1624
1625 Status = fileref_get_filename(fileref, &fn, &name_offset);
1626 if (!NT_SUCCESS(Status)) {
1627 ERR("fileref_get_filename returned %08x\n", Status);
1628 return;
1629 }
1630
1631 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
1632 NULL, NULL, filter_match, action, NULL, NULL);
1633 ExFreePool(fn.Buffer);
1634 }
1635
1636 void send_notification_fcb(file_ref* fileref, ULONG filter_match, ULONG action) {
1637 fcb* fcb = fileref->fcb;
1638 LIST_ENTRY* le;
1639 NTSTATUS Status;
1640
1641 // no point looking for hardlinks if st_nlink == 1
1642 if (fileref->fcb->inode_item.st_nlink == 1) {
1643 send_notification_fileref(fileref, filter_match, action);
1644 return;
1645 }
1646
1647 ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
1648
1649 le = fcb->hardlinks.Flink;
1650 while (le != &fcb->hardlinks) {
1651 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1652 file_ref* parfr;
1653
1654 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
1655
1656 if (!NT_SUCCESS(Status)) {
1657 ERR("open_fileref_by_inode returned %08x\n", Status);
1658 } else if (!parfr->deleted) {
1659 LIST_ENTRY* le2;
1660 BOOL found = FALSE, deleted = FALSE;
1661 UNICODE_STRING* fn;
1662
1663 le2 = parfr->children.Flink;
1664 while (le2 != &parfr->children) {
1665 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry);
1666
1667 if (fr2->index == hl->index) {
1668 found = TRUE;
1669 deleted = fr2->deleted;
1670
1671 if (!deleted)
1672 fn = &fr2->filepart;
1673
1674 break;
1675 }
1676
1677 le2 = le2->Flink;
1678 }
1679
1680 if (!found)
1681 fn = &hl->name;
1682
1683 if (!deleted) {
1684 UNICODE_STRING path;
1685
1686 Status = fileref_get_filename(parfr, &path, NULL);
1687 if (!NT_SUCCESS(Status)) {
1688 ERR("fileref_get_filename returned %08x\n", Status);
1689 } else {
1690 UNICODE_STRING fn2;
1691 ULONG name_offset;
1692
1693 name_offset = path.Length;
1694 if (parfr != fileref->fcb->Vcb->root_fileref) name_offset += sizeof(WCHAR);
1695
1696 fn2.Length = fn2.MaximumLength = fn->Length + name_offset;
1697 fn2.Buffer = ExAllocatePoolWithTag(PagedPool, fn2.MaximumLength, ALLOC_TAG);
1698
1699 RtlCopyMemory(fn2.Buffer, path.Buffer, path.Length);
1700 if (parfr != fileref->fcb->Vcb->root_fileref) fn2.Buffer[path.Length / sizeof(WCHAR)] = '\\';
1701 RtlCopyMemory(&fn2.Buffer[name_offset / sizeof(WCHAR)], fn->Buffer, fn->Length);
1702
1703 TRACE("%.*S\n", fn2.Length / sizeof(WCHAR), fn2.Buffer);
1704
1705 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn2, name_offset,
1706 NULL, NULL, filter_match, action, NULL, NULL);
1707
1708 ExFreePool(fn2.Buffer);
1709 ExFreePool(path.Buffer);
1710 }
1711 }
1712
1713 free_fileref(parfr);
1714 }
1715
1716 le = le->Flink;
1717 }
1718
1719 ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
1720 }
1721
1722 void mark_fcb_dirty(fcb* fcb) {
1723 if (!fcb->dirty) {
1724 #ifdef DEBUG_FCB_REFCOUNTS
1725 LONG rc;
1726 #endif
1727 dirty_fcb* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fcb), ALLOC_TAG);
1728
1729 if (!dirt) {
1730 ExFreePool("out of memory\n");
1731 return;
1732 }
1733
1734 fcb->dirty = TRUE;
1735
1736 #ifdef DEBUG_FCB_REFCOUNTS
1737 rc = InterlockedIncrement(&fcb->refcount);
1738 WARN("fcb %p: refcount now %i\n", fcb, rc);
1739 #else
1740 InterlockedIncrement(&fcb->refcount);
1741 #endif
1742
1743 dirt->fcb = fcb;
1744
1745 ExInterlockedInsertTailList(&fcb->Vcb->dirty_fcbs, &dirt->list_entry, &fcb->Vcb->dirty_fcbs_lock);
1746 }
1747
1748 fcb->Vcb->need_write = TRUE;
1749 }
1750
1751 void mark_fileref_dirty(file_ref* fileref) {
1752 if (!fileref->dirty) {
1753 dirty_fileref* dirt = ExAllocatePoolWithTag(NonPagedPool, sizeof(dirty_fileref), ALLOC_TAG);
1754
1755 if (!dirt) {
1756 ExFreePool("out of memory\n");
1757 return;
1758 }
1759
1760 fileref->dirty = TRUE;
1761 increase_fileref_refcount(fileref);
1762
1763 dirt->fileref = fileref;
1764
1765 ExInterlockedInsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &dirt->list_entry, &fileref->fcb->Vcb->dirty_filerefs_lock);
1766 }
1767
1768 fileref->fcb->Vcb->need_write = TRUE;
1769 }
1770
1771 void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
1772 LONG rc;
1773
1774 // #ifdef DEBUG
1775 // if (!ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->tree_lock)) {
1776 // ERR("fcb_lock not acquired exclusively\n");
1777 // int3;
1778 // }
1779 // #endif
1780
1781 rc = InterlockedDecrement(&fcb->refcount);
1782
1783 #ifdef DEBUG_FCB_REFCOUNTS
1784 // WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1785 #ifdef DEBUG_LONG_MESSAGES
1786 _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);
1787 #else
1788 _debug_message(func, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1789 #endif
1790 #endif
1791
1792 if (rc > 0)
1793 return;
1794
1795 // ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
1796
1797 if (fcb->list_entry.Flink)
1798 RemoveEntryList(&fcb->list_entry);
1799
1800 if (fcb->list_entry_all.Flink)
1801 RemoveEntryList(&fcb->list_entry_all);
1802
1803 // ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
1804
1805 ExDeleteResourceLite(&fcb->nonpaged->resource);
1806 ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
1807 ExDeleteResourceLite(&fcb->nonpaged->index_lock);
1808 ExFreePool(fcb->nonpaged);
1809
1810 if (fcb->sd)
1811 ExFreePool(fcb->sd);
1812
1813 if (fcb->adsxattr.Buffer)
1814 ExFreePool(fcb->adsxattr.Buffer);
1815
1816 if (fcb->reparse_xattr.Buffer)
1817 ExFreePool(fcb->reparse_xattr.Buffer);
1818
1819 if (fcb->adsdata.Buffer)
1820 ExFreePool(fcb->adsdata.Buffer);
1821
1822 if (fcb->debug_desc)
1823 ExFreePool(fcb->debug_desc);
1824
1825 while (!IsListEmpty(&fcb->extents)) {
1826 LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
1827 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
1828
1829 ExFreePool(ext->data);
1830 ExFreePool(ext);
1831 }
1832
1833 while (!IsListEmpty(&fcb->index_list)) {
1834 LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
1835 index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
1836
1837 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
1838 if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
1839 ExFreePool(ie);
1840 }
1841
1842 while (!IsListEmpty(&fcb->hardlinks)) {
1843 LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
1844 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
1845
1846 if (hl->name.Buffer)
1847 ExFreePool(hl->name.Buffer);
1848
1849 if (hl->utf8.Buffer)
1850 ExFreePool(hl->utf8.Buffer);
1851
1852 ExFreePool(hl);
1853 }
1854
1855 FsRtlUninitializeFileLock(&fcb->lock);
1856
1857 ExFreePool(fcb);
1858 #ifdef DEBUG_FCB_REFCOUNTS
1859 #ifdef DEBUG_LONG_MESSAGES
1860 _debug_message(func, file, line, "freeing fcb %p\n", fcb);
1861 #else
1862 _debug_message(func, "freeing fcb %p\n", fcb);
1863 #endif
1864 #endif
1865 }
1866
1867 void _free_fileref(file_ref* fr, const char* func, const char* file, unsigned int line) {
1868 LONG rc;
1869
1870 // #ifdef DEBUG
1871 // if (!ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->tree_lock) && !fr->dirty) {
1872 // ERR("fcb_lock not acquired exclusively\n");
1873 // int3;
1874 // }
1875 // #endif
1876
1877 rc = InterlockedDecrement(&fr->refcount);
1878
1879 #ifdef DEBUG_FCB_REFCOUNTS
1880 #ifdef DEBUG_LONG_MESSAGES
1881 _debug_message(func, file, line, "fileref %p: refcount now %i\n", fr, rc);
1882 #else
1883 _debug_message(func, "fileref %p: refcount now %i\n", fr, rc);
1884 #endif
1885 #endif
1886
1887 #ifdef _DEBUG
1888 if (rc < 0) {
1889 ERR("fileref %p: refcount now %i\n", fr, rc);
1890 int3;
1891 }
1892 #endif
1893
1894 if (rc > 0)
1895 return;
1896
1897 if (fr->parent)
1898 ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE);
1899
1900 // FIXME - do we need a file_ref lock?
1901
1902 // FIXME - do delete if needed
1903
1904 if (fr->filepart.Buffer)
1905 ExFreePool(fr->filepart.Buffer);
1906
1907 if (fr->filepart_uc.Buffer)
1908 ExFreePool(fr->filepart_uc.Buffer);
1909
1910 if (fr->utf8.Buffer)
1911 ExFreePool(fr->utf8.Buffer);
1912
1913 if (fr->debug_desc)
1914 ExFreePool(fr->debug_desc);
1915
1916 ExDeleteResourceLite(&fr->nonpaged->children_lock);
1917
1918 ExFreePool(fr->nonpaged);
1919
1920 // FIXME - throw error if children not empty
1921
1922 if (fr->fcb->fileref == fr)
1923 fr->fcb->fileref = NULL;
1924
1925 if (fr->list_entry.Flink)
1926 RemoveEntryList(&fr->list_entry);
1927
1928 if (fr->parent) {
1929 ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
1930 free_fileref(fr->parent);
1931 }
1932
1933 free_fcb(fr->fcb);
1934 ExFreePool(fr);
1935 }
1936
1937 static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
1938 fcb* fcb;
1939 ccb* ccb;
1940 file_ref* fileref = NULL;
1941
1942 TRACE("FileObject = %p\n", FileObject);
1943
1944 fcb = FileObject->FsContext;
1945 if (!fcb) {
1946 TRACE("FCB was NULL, returning success\n");
1947 return STATUS_SUCCESS;
1948 }
1949
1950 ccb = FileObject->FsContext2;
1951
1952 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
1953
1954 // FIXME - make sure notification gets sent if file is being deleted
1955
1956 if (ccb) {
1957 if (ccb->query_string.Buffer)
1958 RtlFreeUnicodeString(&ccb->query_string);
1959
1960 if (ccb->filename.Buffer)
1961 ExFreePool(ccb->filename.Buffer);
1962
1963 // FIXME - use refcounts for fileref
1964 fileref = ccb->fileref;
1965
1966 ExFreePool(ccb);
1967 }
1968
1969 CcUninitializeCacheMap(FileObject, NULL, NULL);
1970
1971 if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
1972 return STATUS_SUCCESS;
1973
1974 ExAcquireResourceExclusiveLite(&Vcb->fcb_lock, TRUE);
1975
1976 if (fileref)
1977 free_fileref(fileref);
1978 else
1979 free_fcb(fcb);
1980
1981 ExReleaseResourceLite(&Vcb->fcb_lock);
1982
1983 return STATUS_SUCCESS;
1984 }
1985
1986 void STDCALL uninit(device_extension* Vcb, BOOL flush) {
1987 space* s;
1988 UINT64 i;
1989 LIST_ENTRY rollback;
1990 NTSTATUS Status;
1991 LIST_ENTRY* le;
1992 LARGE_INTEGER time;
1993
1994 Vcb->removing = TRUE;
1995
1996 RemoveEntryList(&Vcb->list_entry);
1997
1998 Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
1999 if (!NT_SUCCESS(Status))
2000 WARN("registry_mark_volume_unmounted returned %08x\n", Status);
2001
2002 if (flush) {
2003 InitializeListHead(&rollback);
2004
2005 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
2006
2007 if (Vcb->need_write && !Vcb->readonly)
2008 do_write(Vcb, NULL, &rollback);
2009
2010 free_trees(Vcb);
2011
2012 clear_rollback(&rollback);
2013
2014 ExReleaseResourceLite(&Vcb->tree_lock);
2015 }
2016
2017 for (i = 0; i < Vcb->threads.num_threads; i++) {
2018 Vcb->threads.threads[i].quit = TRUE;
2019 KeSetEvent(&Vcb->threads.threads[i].event, 0, FALSE);
2020
2021 KeWaitForSingleObject(&Vcb->threads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
2022
2023 ZwClose(Vcb->threads.threads[i].handle);
2024 }
2025
2026 ExFreePool(Vcb->threads.threads);
2027
2028 time.QuadPart = 0;
2029 KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
2030 KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
2031
2032 free_fcb(Vcb->volume_fcb);
2033
2034 if (Vcb->root_file)
2035 ObDereferenceObject(Vcb->root_file);
2036
2037 le = Vcb->chunks.Flink;
2038 while (le != &Vcb->chunks) {
2039 chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
2040
2041 if (c->cache) {
2042 free_fcb(c->cache);
2043 c->cache = NULL;
2044 }
2045
2046 le = le->Flink;
2047 }
2048
2049 while (!IsListEmpty(&Vcb->roots)) {
2050 LIST_ENTRY* le = RemoveHeadList(&Vcb->roots);
2051 root* r = CONTAINING_RECORD(le, root, list_entry);
2052
2053 ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
2054 ExFreePool(r->nonpaged);
2055 ExFreePool(r);
2056 }
2057
2058 while (!IsListEmpty(&Vcb->chunks)) {
2059 chunk* c;
2060
2061 le = RemoveHeadList(&Vcb->chunks);
2062 c = CONTAINING_RECORD(le, chunk, list_entry);
2063
2064 while (!IsListEmpty(&c->space)) {
2065 LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2066 s = CONTAINING_RECORD(le2, space, list_entry);
2067
2068 ExFreePool(s);
2069 }
2070
2071 while (!IsListEmpty(&c->deleting)) {
2072 LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
2073 s = CONTAINING_RECORD(le2, space, list_entry);
2074
2075 ExFreePool(s);
2076 }
2077
2078 if (c->devices)
2079 ExFreePool(c->devices);
2080
2081 if (c->cache)
2082 free_fcb(c->cache);
2083
2084 ExDeleteResourceLite(&c->lock);
2085 ExDeleteResourceLite(&c->changed_extents_lock);
2086
2087 ExFreePool(c->chunk_item);
2088 ExFreePool(c);
2089 }
2090
2091 // FIXME - free any open fcbs?
2092
2093 while (!IsListEmpty(&Vcb->sector_checksums)) {
2094 LIST_ENTRY* le = RemoveHeadList(&Vcb->sector_checksums);
2095 changed_sector* cs = (changed_sector*)le;
2096
2097 ExFreePool(cs);
2098 }
2099
2100 for (i = 0; i < Vcb->superblock.num_devices; i++) {
2101 while (!IsListEmpty(&Vcb->devices[i].space)) {
2102 LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].space);
2103 space* s = CONTAINING_RECORD(le, space, list_entry);
2104
2105 ExFreePool(s);
2106 }
2107 }
2108
2109 ExFreePool(Vcb->devices);
2110
2111 ExDeleteResourceLite(&Vcb->fcb_lock);
2112 ExDeleteResourceLite(&Vcb->load_lock);
2113 ExDeleteResourceLite(&Vcb->tree_lock);
2114 ExDeleteResourceLite(&Vcb->checksum_lock);
2115 ExDeleteResourceLite(&Vcb->chunk_lock);
2116
2117 ZwClose(Vcb->flush_thread_handle);
2118 }
2119
2120 NTSTATUS delete_fileref(file_ref* fileref, PFILE_OBJECT FileObject, PIRP Irp, LIST_ENTRY* rollback) {
2121 LARGE_INTEGER newlength, time;
2122 BTRFS_TIME now;
2123 NTSTATUS Status;
2124
2125 KeQuerySystemTime(&time);
2126 win_time_to_unix(time, &now);
2127
2128 ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
2129
2130 if (fileref->deleted) {
2131 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2132 return STATUS_SUCCESS;
2133 }
2134
2135 fileref->deleted = TRUE;
2136 mark_fileref_dirty(fileref);
2137
2138 // delete INODE_ITEM (0x1)
2139
2140 TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
2141
2142 if (!fileref->fcb->ads) {
2143 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
2144 LIST_ENTRY* le;
2145
2146 mark_fcb_dirty(fileref->fcb);
2147
2148 if (fileref->fcb->inode_item.st_nlink > 1) {
2149 fileref->fcb->inode_item.st_nlink--;
2150 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2151 fileref->fcb->inode_item.sequence++;
2152 fileref->fcb->inode_item.st_ctime = now;
2153 } else {
2154 fileref->fcb->deleted = TRUE;
2155
2156 // excise extents
2157
2158 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
2159 Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
2160 if (!NT_SUCCESS(Status)) {
2161 ERR("excise_extents returned %08x\n", Status);
2162 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2163 return Status;
2164 }
2165 }
2166
2167 fileref->fcb->Header.AllocationSize.QuadPart = 0;
2168 fileref->fcb->Header.FileSize.QuadPart = 0;
2169 fileref->fcb->Header.ValidDataLength.QuadPart = 0;
2170
2171 if (FileObject) {
2172 CC_FILE_SIZES ccfs;
2173
2174 ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
2175 ccfs.FileSize = fileref->fcb->Header.FileSize;
2176 ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
2177
2178 CcSetFileSizes(FileObject, &ccfs);
2179 }
2180 }
2181
2182 le = fileref->fcb->hardlinks.Flink;
2183 while (le != &fileref->fcb->hardlinks) {
2184 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
2185
2186 if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->index) {
2187 RemoveEntryList(&hl->list_entry);
2188
2189 if (hl->name.Buffer)
2190 ExFreePool(hl->name.Buffer);
2191
2192 if (hl->utf8.Buffer)
2193 ExFreePool(hl->utf8.Buffer);
2194
2195 ExFreePool(hl);
2196 break;
2197 }
2198
2199 le = le->Flink;
2200 }
2201 } else { // subvolume
2202 if (fileref->fcb->subvol->root_item.num_references > 1) {
2203 fileref->fcb->subvol->root_item.num_references--;
2204
2205 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2206 } else {
2207 // FIXME - we need a lock here
2208
2209 RemoveEntryList(&fileref->fcb->subvol->list_entry);
2210
2211 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2212 }
2213 }
2214 } else {
2215 fileref->fcb->deleted = TRUE;
2216 mark_fcb_dirty(fileref->fcb);
2217 }
2218
2219 // update INODE_ITEM of parent
2220
2221 TRACE("delete file %.*S\n", fileref->filepart.Length / sizeof(WCHAR), fileref->filepart.Buffer);
2222 ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE);
2223 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2224 fileref->parent->fcb->inode_item.st_size -= fileref->utf8.Length * 2;
2225 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2226 fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2227 fileref->parent->fcb->inode_item.sequence++;
2228 fileref->parent->fcb->inode_item.st_ctime = now;
2229 fileref->parent->fcb->inode_item.st_mtime = now;
2230 ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2231
2232 mark_fcb_dirty(fileref->parent->fcb);
2233
2234 send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
2235
2236 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2237 fileref->fcb->subvol->root_item.ctime = now;
2238
2239 if (FileObject && FileObject->Flags & FO_CACHE_SUPPORTED && fileref->fcb->nonpaged->segment_object.DataSectionObject)
2240 CcPurgeCacheSection(&fileref->fcb->nonpaged->segment_object, NULL, 0, FALSE);
2241
2242 newlength.QuadPart = 0;
2243
2244 if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2245 TRACE("CcUninitializeCacheMap failed\n");
2246
2247 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2248
2249 return STATUS_SUCCESS;
2250 }
2251
2252 static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2253 NTSTATUS Status;
2254 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2255 PFILE_OBJECT FileObject = IrpSp->FileObject;
2256 device_extension* Vcb = DeviceObject->DeviceExtension;
2257 fcb* fcb;
2258 BOOL top_level;
2259
2260 TRACE("cleanup\n");
2261
2262 FsRtlEnterFileSystem();
2263
2264 top_level = is_top_level(Irp);
2265
2266 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
2267 Status = part0_passthrough(DeviceObject, Irp);
2268 goto exit2;
2269 }
2270
2271 if (DeviceObject == devobj) {
2272 TRACE("closing file system\n");
2273 Status = STATUS_SUCCESS;
2274 goto exit;
2275 }
2276
2277 if (FileObject && FileObject->FsContext) {
2278 LONG oc;
2279 ccb* ccb;
2280 file_ref* fileref;
2281
2282 fcb = FileObject->FsContext;
2283 ccb = FileObject->FsContext2;
2284 fileref = ccb ? ccb->fileref : NULL;
2285
2286 TRACE("cleanup called for FileObject %p\n", FileObject);
2287 TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb, file_desc(FileObject), fcb->refcount, fcb->open_count);
2288
2289 IoRemoveShareAccess(FileObject, &fcb->share_access);
2290
2291 FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
2292
2293 oc = InterlockedDecrement(&fcb->open_count);
2294 #ifdef DEBUG_FCB_REFCOUNTS
2295 ERR("fcb %p: open_count now %i\n", fcb, oc);
2296 #endif
2297
2298 if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2299 fileref->delete_on_close = TRUE;
2300
2301 if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
2302 fileref->delete_on_close = FALSE;
2303
2304 if (Vcb->locked && Vcb->locked_fileobj == FileObject) {
2305 TRACE("unlocking volume\n");
2306 do_unlock_volume(Vcb);
2307 FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
2308 }
2309
2310 if (oc == 0) {
2311 if (!Vcb->removing) {
2312 LIST_ENTRY rollback;
2313
2314 InitializeListHead(&rollback);
2315
2316 if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2317 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED);
2318
2319 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
2320
2321 Status = delete_fileref(fileref, FileObject, Irp, &rollback);
2322 if (!NT_SUCCESS(Status)) {
2323 ERR("delete_fileref returned %08x\n", Status);
2324 do_rollback(Vcb, &rollback);
2325 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2326 goto exit;
2327 }
2328
2329 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2330 clear_rollback(&rollback);
2331 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
2332 IO_STATUS_BLOCK iosb;
2333 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2334
2335 if (!NT_SUCCESS(iosb.Status)) {
2336 ERR("CcFlushCache returned %08x\n", iosb.Status);
2337 }
2338
2339 if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2340 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
2341 ExReleaseResourceLite(fcb->Header.PagingIoResource);
2342 }
2343
2344 CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
2345
2346 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2347 FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2348 }
2349 }
2350
2351 if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2352 CcUninitializeCacheMap(FileObject, NULL, NULL);
2353 }
2354
2355 FileObject->Flags |= FO_CLEANUP_COMPLETE;
2356 }
2357
2358 Status = STATUS_SUCCESS;
2359
2360 exit:
2361 Irp->IoStatus.Status = Status;
2362 Irp->IoStatus.Information = 0;
2363
2364 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2365
2366 exit2:
2367 if (top_level)
2368 IoSetTopLevelIrp(NULL);
2369
2370 FsRtlExitFileSystem();
2371
2372 return Status;
2373 }
2374
2375 ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa, PIRP Irp) {
2376 ULONG att;
2377 char* eaval;
2378 UINT16 ealen;
2379
2380 // ii can be NULL
2381
2382 if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) {
2383 if (ealen > 2) {
2384 if (eaval[0] == '0' && eaval[1] == 'x') {
2385 int i;
2386 ULONG dosnum = 0;
2387
2388 for (i = 2; i < ealen; i++) {
2389 dosnum *= 0x10;
2390
2391 if (eaval[i] >= '0' && eaval[i] <= '9')
2392 dosnum |= eaval[i] - '0';
2393 else if (eaval[i] >= 'a' && eaval[i] <= 'f')
2394 dosnum |= eaval[i] + 10 - 'a';
2395 else if (eaval[i] >= 'A' && eaval[i] <= 'F')
2396 dosnum |= eaval[i] + 10 - 'a';
2397 }
2398
2399 TRACE("DOSATTRIB: %08x\n", dosnum);
2400
2401 ExFreePool(eaval);
2402
2403 if (type == BTRFS_TYPE_DIRECTORY)
2404 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2405 else if (type == BTRFS_TYPE_SYMLINK)
2406 dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2407
2408 return dosnum;
2409 }
2410 }
2411
2412 ExFreePool(eaval);
2413 }
2414
2415 switch (type) {
2416 case BTRFS_TYPE_DIRECTORY:
2417 att = FILE_ATTRIBUTE_DIRECTORY;
2418 break;
2419
2420 case BTRFS_TYPE_SYMLINK:
2421 att = FILE_ATTRIBUTE_REPARSE_POINT;
2422 break;
2423
2424 default:
2425 att = 0;
2426 break;
2427 }
2428
2429 if (dotfile) {
2430 att |= FILE_ATTRIBUTE_HIDDEN;
2431 }
2432
2433 att |= FILE_ATTRIBUTE_ARCHIVE;
2434
2435 // FIXME - get READONLY from ii->st_mode
2436 // FIXME - return SYSTEM for block/char devices?
2437
2438 if (att == 0)
2439 att = FILE_ATTRIBUTE_NORMAL;
2440
2441 return att;
2442 }
2443
2444 static NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer, BOOL override) {
2445 IO_STATUS_BLOCK* IoStatus;
2446 LARGE_INTEGER Offset;
2447 PIRP Irp;
2448 PIO_STACK_LOCATION IrpSp;
2449 NTSTATUS Status;
2450 read_context* context;
2451
2452 num_reads++;
2453
2454 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
2455 if (!context) {
2456 ERR("out of memory\n");
2457 return STATUS_INSUFFICIENT_RESOURCES;
2458 }
2459
2460 RtlZeroMemory(context, sizeof(read_context));
2461 KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
2462
2463 IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
2464 if (!IoStatus) {
2465 ERR("out of memory\n");
2466 ExFreePool(context);
2467 return STATUS_INSUFFICIENT_RESOURCES;
2468 }
2469
2470 Offset.QuadPart = StartingOffset;
2471
2472 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2473 Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
2474
2475 if (!Irp) {
2476 ERR("IoAllocateIrp failed\n");
2477 Status = STATUS_INSUFFICIENT_RESOURCES;
2478 goto exit;
2479 }
2480
2481 Irp->Flags |= IRP_NOCACHE;
2482 IrpSp = IoGetNextIrpStackLocation(Irp);
2483 IrpSp->MajorFunction = IRP_MJ_READ;
2484
2485 if (override)
2486 IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2487
2488 if (DeviceObject->Flags & DO_BUFFERED_IO) {
2489 FIXME("FIXME - buffered IO\n");
2490 } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2491 // TRACE("direct IO\n");
2492
2493 Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
2494 if (!Irp->MdlAddress) {
2495 ERR("IoAllocateMdl failed\n");
2496 Status = STATUS_INSUFFICIENT_RESOURCES;
2497 // IoFreeIrp(Irp);
2498 goto exit;
2499 // } else {
2500 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2501 }
2502
2503 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
2504 } else {
2505 // TRACE("neither buffered nor direct IO\n");
2506 Irp->UserBuffer = Buffer;
2507 }
2508
2509 IrpSp->Parameters.Read.Length = Length;
2510 IrpSp->Parameters.Read.ByteOffset = Offset;
2511
2512 Irp->UserIosb = IoStatus;
2513 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2514
2515 Irp->UserEvent = &context->Event;
2516
2517 // IoQueueThreadIrp(Irp);
2518
2519 IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
2520
2521 Status = IoCallDriver(DeviceObject, Irp);
2522
2523 if (Status == STATUS_PENDING) {
2524 KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
2525 Status = context->iosb.Status;
2526 }
2527
2528 if (DeviceObject->Flags & DO_DIRECT_IO) {
2529 MmUnlockPages(Irp->MdlAddress);
2530 IoFreeMdl(Irp->MdlAddress);
2531 }
2532
2533 exit:
2534 IoFreeIrp(Irp);
2535
2536 ExFreePool(IoStatus);
2537 ExFreePool(context);
2538
2539 return Status;
2540 }
2541
2542 static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device, UINT64 length) {
2543 NTSTATUS Status;
2544 superblock* sb;
2545 unsigned int i, to_read;
2546 UINT32 crc32;
2547
2548 to_read = sector_align(sizeof(superblock), device->SectorSize);
2549
2550 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
2551 if (!sb) {
2552 ERR("out of memory\n");
2553 return STATUS_INSUFFICIENT_RESOURCES;
2554 }
2555
2556 i = 0;
2557
2558 while (superblock_addrs[i] > 0) {
2559 if (i > 0 && superblock_addrs[i] + sizeof(superblock) > length)
2560 break;
2561
2562 Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
2563 if (!NT_SUCCESS(Status)) {
2564 ERR("Failed to read superblock %u: %08x\n", i, Status);
2565 ExFreePool(sb);
2566 return Status;
2567 }
2568
2569 // FIXME - check checksum before accepting?
2570
2571 TRACE("got superblock %u!\n", i);
2572
2573 if (i == 0 || sb->generation > Vcb->superblock.generation)
2574 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2575
2576 i++;
2577 }
2578
2579 ExFreePool(sb);
2580
2581 crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
2582 crc32 = ~crc32;
2583 TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
2584
2585 if (crc32 != *((UINT32*)Vcb->superblock.checksum))
2586 return STATUS_INTERNAL_ERROR; // FIXME - correct error?
2587
2588 TRACE("label is %s\n", Vcb->superblock.label);
2589 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2590
2591 return STATUS_SUCCESS;
2592 }
2593
2594 NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer, ULONG InputBufferSize,
2595 PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb)
2596 {
2597 PIRP Irp;
2598 KEVENT Event;
2599 NTSTATUS Status;
2600 PIO_STACK_LOCATION Stack;
2601 IO_STATUS_BLOCK IoStatus;
2602
2603 KeInitializeEvent(&Event, NotificationEvent, FALSE);
2604
2605 Irp = IoBuildDeviceIoControlRequest(ControlCode,
2606 DeviceObject,
2607 InputBuffer,
2608 InputBufferSize,
2609 OutputBuffer,
2610 OutputBufferSize,
2611 FALSE,
2612 &Event,
2613 &IoStatus);
2614
2615 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2616
2617 if (Override) {
2618 Stack = IoGetNextIrpStackLocation(Irp);
2619 Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2620 }
2621
2622 Status = IoCallDriver(DeviceObject, Irp);
2623
2624 if (Status == STATUS_PENDING) {
2625 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2626 Status = IoStatus.Status;
2627 }
2628
2629 if (iosb)
2630 *iosb = IoStatus;
2631
2632 return Status;
2633 }
2634
2635 static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
2636 root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
2637 if (!r) {
2638 ERR("out of memory\n");
2639 return STATUS_INSUFFICIENT_RESOURCES;
2640 }
2641
2642 r->id = id;
2643 r->path.Buffer = NULL;
2644 r->treeholder.address = addr;
2645 r->treeholder.tree = NULL;
2646 InitializeListHead(&r->fcbs);
2647
2648 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
2649 if (!r->nonpaged) {
2650 ERR("out of memory\n");
2651 ExFreePool(r);
2652 return STATUS_INSUFFICIENT_RESOURCES;
2653 }
2654
2655 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
2656
2657 r->lastinode = 0;
2658
2659 if (tp) {
2660 RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
2661 if (tp->item->size < sizeof(ROOT_ITEM))
2662 RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
2663 }
2664
2665 InsertTailList(&Vcb->roots, &r->list_entry);
2666
2667 switch (r->id) {
2668 case BTRFS_ROOT_ROOT:
2669 Vcb->root_root = r;
2670 break;
2671
2672 case BTRFS_ROOT_EXTENT:
2673 Vcb->extent_root = r;
2674 break;
2675
2676 case BTRFS_ROOT_CHUNK:
2677 Vcb->chunk_root = r;
2678 break;
2679
2680 case BTRFS_ROOT_DEVTREE:
2681 Vcb->dev_root = r;
2682 break;
2683
2684 case BTRFS_ROOT_CHECKSUM:
2685 Vcb->checksum_root = r;
2686 break;
2687
2688 case BTRFS_ROOT_UUID:
2689 Vcb->uuid_root = r;
2690 break;
2691 }
2692
2693 return STATUS_SUCCESS;
2694 }
2695
2696 static NTSTATUS STDCALL look_for_roots(device_extension* Vcb, PIRP Irp) {
2697 traverse_ptr tp, next_tp;
2698 KEY searchkey;
2699 BOOL b;
2700 NTSTATUS Status;
2701
2702 searchkey.obj_id = 0;
2703 searchkey.obj_type = 0;
2704 searchkey.offset = 0;
2705
2706 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
2707 if (!NT_SUCCESS(Status)) {
2708 ERR("error - find_tree returned %08x\n", Status);
2709 return Status;
2710 }
2711
2712 do {
2713 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2714
2715 if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
2716 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2717
2718 if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
2719 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));
2720 } else {
2721 TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
2722
2723 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
2724 if (!NT_SUCCESS(Status)) {
2725 ERR("add_root returned %08x\n", Status);
2726 return Status;
2727 }
2728 }
2729 }
2730
2731 b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
2732
2733 if (b)
2734 tp = next_tp;
2735 } while (b);
2736
2737 return STATUS_SUCCESS;
2738 }
2739
2740 static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev, PIRP Irp) {
2741 KEY searchkey;
2742 traverse_ptr tp, next_tp;
2743 BOOL b;
2744 UINT64 lastaddr;
2745 NTSTATUS Status;
2746
2747 InitializeListHead(&dev->space);
2748
2749 searchkey.obj_id = dev->devitem.dev_id;
2750 searchkey.obj_type = TYPE_DEV_EXTENT;
2751 searchkey.offset = 0;
2752
2753 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
2754 if (!NT_SUCCESS(Status)) {
2755 ERR("error - find_tree returned %08x\n", Status);
2756 return Status;
2757 }
2758
2759 lastaddr = 0;
2760
2761 do {
2762 if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
2763 if (tp.item->size >= sizeof(DEV_EXTENT)) {
2764 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
2765
2766 if (tp.item->key.offset > lastaddr) {
2767 Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
2768 if (!NT_SUCCESS(Status)) {
2769 ERR("add_space_entry returned %08x\n", Status);
2770 return Status;
2771 }
2772 }
2773
2774 lastaddr = tp.item->key.offset + de->length;
2775 } else {
2776 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));
2777 }
2778 }
2779
2780 b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
2781
2782 if (b) {
2783 tp = next_tp;
2784 if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
2785 break;
2786 }
2787 } while (b);
2788
2789 if (lastaddr < dev->devitem.num_bytes) {
2790 Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
2791 if (!NT_SUCCESS(Status)) {
2792 ERR("add_space_entry returned %08x\n", Status);
2793 return Status;
2794 }
2795 }
2796
2797 return STATUS_SUCCESS;
2798 }
2799
2800 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
2801 UINT64 i;
2802
2803 for (i = 0; i < Vcb->devices_loaded; i++) {
2804 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
2805 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],
2806 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]);
2807
2808 if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2809 TRACE("returning device %llx\n", i);
2810 return &Vcb->devices[i];
2811 }
2812 }
2813
2814 if (Vcb->devices_loaded < Vcb->superblock.num_devices && !IsListEmpty(&volumes)) {
2815 LIST_ENTRY* le = volumes.Flink;
2816
2817 while (le != &volumes) {
2818 volume* v = CONTAINING_RECORD(le, volume, list_entry);
2819
2820 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
2821 RtlCompareMemory(uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
2822 ) {
2823 NTSTATUS Status;
2824 PFILE_OBJECT FileObject;
2825 PDEVICE_OBJECT DeviceObject;
2826
2827 Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
2828 if (!NT_SUCCESS(Status)) {
2829 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
2830 return NULL;
2831 }
2832
2833 DeviceObject = FileObject->DeviceObject;
2834
2835 ObReferenceObject(DeviceObject);
2836 ObDereferenceObject(FileObject);
2837
2838 Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
2839 Vcb->devices[Vcb->devices_loaded].devitem.device_uuid = *uuid;
2840 Vcb->devices_loaded++;
2841
2842 return &Vcb->devices[Vcb->devices_loaded - 1];
2843 }
2844
2845 le = le->Flink;
2846 }
2847 }
2848
2849 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2850 uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
2851 uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
2852
2853 return NULL;
2854 }
2855
2856 static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
2857 NTSTATUS Status;
2858 STORAGE_HOTPLUG_INFO shi;
2859
2860 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
2861
2862 if (!NT_SUCCESS(Status)) {
2863 ERR("dev_ioctl returned %08x\n", Status);
2864 return FALSE;
2865 }
2866
2867 return shi.MediaRemovable != 0 ? TRUE : FALSE;
2868 }
2869
2870 static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
2871 NTSTATUS Status;
2872 ULONG cc;
2873 IO_STATUS_BLOCK iosb;
2874
2875 Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
2876
2877 if (!NT_SUCCESS(Status)) {
2878 ERR("dev_ioctl returned %08x\n", Status);
2879 return 0;
2880 }
2881
2882 if (iosb.Information < sizeof(ULONG)) {
2883 ERR("iosb.Information was too short\n");
2884 return 0;
2885 }
2886
2887 return cc;
2888 }
2889
2890 static void init_device(device_extension* Vcb, device* dev, BOOL get_length) {
2891 NTSTATUS Status;
2892 GET_LENGTH_INFORMATION gli;
2893
2894 dev->removable = is_device_removable(dev->devobj);
2895 dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
2896
2897 if (get_length) {
2898 Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
2899 &gli, sizeof(gli), TRUE, NULL);
2900 if (!NT_SUCCESS(Status)) {
2901 ERR("error reading length information: %08x\n", Status);
2902 }
2903
2904 dev->length = gli.Length.QuadPart;
2905 }
2906 }
2907
2908 static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb, PIRP Irp) {
2909 traverse_ptr tp, next_tp;
2910 KEY searchkey;
2911 BOOL b;
2912 chunk* c;
2913 UINT64 i;
2914 NTSTATUS Status;
2915
2916 searchkey.obj_id = 0;
2917 searchkey.obj_type = 0;
2918 searchkey.offset = 0;
2919
2920 Vcb->data_flags = 0;
2921
2922 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
2923 if (!NT_SUCCESS(Status)) {
2924 ERR("error - find_item returned %08x\n", Status);
2925 return Status;
2926 }
2927
2928 do {
2929 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2930
2931 if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
2932 if (tp.item->size < sizeof(DEV_ITEM)) {
2933 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));
2934 } else {
2935 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
2936 BOOL done = FALSE;
2937
2938 for (i = 0; i < Vcb->devices_loaded; i++) {
2939 if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
2940 RtlCopyMemory(&Vcb->devices[i].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
2941
2942 if (i > 0)
2943 init_device(Vcb, &Vcb->devices[i], TRUE);
2944
2945 done = TRUE;
2946 break;
2947 }
2948 }
2949
2950 if (!done) {
2951 if (!IsListEmpty(&volumes) && Vcb->devices_loaded < Vcb->superblock.num_devices) {
2952 LIST_ENTRY* le = volumes.Flink;
2953
2954 while (le != &volumes) {
2955 volume* v = CONTAINING_RECORD(le, volume, list_entry);
2956
2957 if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) &&
2958 RtlCompareMemory(&di->device_uuid, &v->devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)
2959 ) {
2960 PFILE_OBJECT FileObject;
2961 PDEVICE_OBJECT DeviceObject;
2962
2963 Status = IoGetDeviceObjectPointer(&v->devpath, FILE_READ_DATA | FILE_WRITE_DATA, &FileObject, &DeviceObject);
2964 if (!NT_SUCCESS(Status)) {
2965 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v->devpath.Length / sizeof(WCHAR), v->devpath.Buffer, Status);
2966 return Status;
2967 }
2968
2969 DeviceObject = FileObject->DeviceObject;
2970
2971 ObReferenceObject(DeviceObject);
2972 ObDereferenceObject(FileObject);
2973
2974 Vcb->devices[Vcb->devices_loaded].devobj = DeviceObject;
2975 RtlCopyMemory(&Vcb->devices[Vcb->devices_loaded].devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
2976 init_device(Vcb, &Vcb->devices[i], FALSE);
2977 Vcb->devices[i].length = v->length;
2978 Vcb->devices_loaded++;
2979
2980 done = TRUE;
2981 break;
2982 }
2983
2984 le = le->Flink;
2985 }
2986
2987 if (!done) {
2988 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,
2989 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],
2990 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]);
2991 }
2992 } else
2993 ERR("unexpected device %llx found\n", tp.item->key.offset);
2994 }
2995 }
2996 } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
2997 if (tp.item->size < sizeof(CHUNK_ITEM)) {
2998 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));
2999 } else {
3000 c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
3001
3002 if (!c) {
3003 ERR("out of memory\n");
3004 return STATUS_INSUFFICIENT_RESOURCES;
3005 }
3006
3007 c->size = tp.item->size;
3008 c->offset = tp.item->key.offset;
3009 c->used = c->oldused = 0;
3010 c->cache = NULL;
3011 c->created = FALSE;
3012
3013 c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
3014
3015 if (!c->chunk_item) {
3016 ERR("out of memory\n");
3017 ExFreePool(c);
3018 return STATUS_INSUFFICIENT_RESOURCES;
3019 }
3020
3021 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
3022
3023 if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
3024 Vcb->data_flags = c->chunk_item->type;
3025
3026 if (c->chunk_item->num_stripes > 0) {
3027 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3028
3029 c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
3030
3031 if (!c->devices) {
3032 ERR("out of memory\n");
3033 ExFreePool(c);
3034 ExFreePool(c->chunk_item);
3035 return STATUS_INSUFFICIENT_RESOURCES;
3036 }
3037
3038 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3039 c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3040 TRACE("device %llu = %p\n", i, c->devices[i]);
3041 }
3042 } else
3043 c->devices = NULL;
3044
3045 ExInitializeResourceLite(&c->lock);
3046 ExInitializeResourceLite(&c->changed_extents_lock);
3047
3048 InitializeListHead(&c->space);
3049 InitializeListHead(&c->space_size);
3050 InitializeListHead(&c->deleting);
3051 InitializeListHead(&c->changed_extents);
3052
3053 InsertTailList(&Vcb->chunks, &c->list_entry);
3054
3055 c->list_entry_changed.Flink = NULL;
3056 }
3057 }
3058
3059 b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
3060
3061 if (b)
3062 tp = next_tp;
3063 } while (b);
3064
3065 Vcb->log_to_phys_loaded = TRUE;
3066
3067 if (Vcb->data_flags == 0)
3068 Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
3069
3070 return STATUS_SUCCESS;
3071 }
3072
3073 void protect_superblocks(device_extension* Vcb, chunk* c) {
3074 UINT16 i = 0, j;
3075 UINT64 off_start, off_end;
3076
3077 // The Linux driver also protects all the space before the first superblock.
3078 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3079 // evidently Linux assumes the chunk at 0 is always SINGLE.
3080 if (c->offset < superblock_addrs[0])
3081 space_list_subtract(Vcb, c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
3082
3083 while (superblock_addrs[i] != 0) {
3084 CHUNK_ITEM* ci = c->chunk_item;
3085 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3086
3087 if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
3088 for (j = 0; j < ci->num_stripes; j++) {
3089 ULONG sub_stripes = max(ci->sub_stripes, 1);
3090
3091 if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3092 #ifdef _DEBUG
3093 UINT64 startoff;
3094 UINT16 startoffstripe;
3095 #endif
3096
3097 TRACE("cut out superblock in chunk %llx\n", c->offset);
3098
3099 off_start = superblock_addrs[i] - cis[j].offset;
3100 off_start -= off_start % ci->stripe_length;
3101 off_start *= ci->num_stripes / sub_stripes;
3102 off_start += (j / sub_stripes) * ci->stripe_length;
3103
3104 off_end = off_start + ci->stripe_length;
3105
3106 #ifdef _DEBUG
3107 get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
3108 TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
3109 TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]);
3110 #endif
3111
3112 space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3113 }
3114 }
3115 } else { // SINGLE, DUPLICATE, RAID1
3116 for (j = 0; j < ci->num_stripes; j++) {
3117 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3118 TRACE("cut out superblock in chunk %llx\n", c->offset);
3119
3120 // The Linux driver protects the whole stripe in which the superblock lives
3121
3122 off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3123 off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3124
3125 space_list_subtract(Vcb, c, FALSE, c->offset + off_start, off_end - off_start, NULL);
3126 }
3127 }
3128 }
3129
3130 i++;
3131 }
3132 }
3133
3134 static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb, PIRP Irp) {
3135 LIST_ENTRY* le = Vcb->chunks.Flink;
3136 chunk* c;
3137 KEY searchkey;
3138 traverse_ptr tp;
3139 BLOCK_GROUP_ITEM* bgi;
3140 NTSTATUS Status;
3141
3142 // c00000,c0,800000
3143 // block_group_item size=7f0000 chunktreeid=100 flags=1
3144
3145 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3146
3147 while (le != &Vcb->chunks) {
3148 c = CONTAINING_RECORD(le, chunk, list_entry);
3149
3150 searchkey.obj_id = c->offset;
3151 searchkey.offset = c->chunk_item->size;
3152
3153 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
3154 if (!NT_SUCCESS(Status)) {
3155 ERR("error - find_item returned %08x\n", Status);
3156 return Status;
3157 }
3158
3159 if (!keycmp(&searchkey, &tp.item->key)) {
3160 if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
3161 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
3162
3163 c->used = c->oldused = bgi->used;
3164
3165 TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
3166 } else {
3167 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3168 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));
3169 }
3170 }
3171
3172 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3173 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3174 //
3175 // return (addr - c->offset) + cis->offset;
3176 // }
3177
3178 // FIXME - make sure we free occasionally after doing one of these, or we
3179 // might use up a lot of memory with a big disk.
3180
3181 Status = load_free_space_cache(Vcb, c, Irp);
3182 if (!NT_SUCCESS(Status)) {
3183 ERR("load_free_space_cache returned %08x\n", Status);
3184 return Status;
3185 }
3186
3187 protect_superblocks(Vcb, c);
3188
3189 le = le->Flink;
3190 }
3191
3192 return STATUS_SUCCESS;
3193 }
3194
3195 // static void STDCALL root_test(device_extension* Vcb) {
3196 // root* r;
3197 // KEY searchkey;
3198 // traverse_ptr tp, next_tp;
3199 // BOOL b;
3200 //
3201 // r = Vcb->roots;
3202 // while (r) {
3203 // if (r->id == 0x102)
3204 // break;
3205 // r = r->next;
3206 // }
3207 //
3208 // if (!r) {
3209 // ERR("Could not find root tree.\n");
3210 // return;
3211 // }
3212 //
3213 // searchkey.obj_id = 0x1b6;
3214 // searchkey.obj_type = 0xb;
3215 // searchkey.offset = 0;
3216 //
3217 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3218 // ERR("Could not find first item.\n");
3219 // return;
3220 // }
3221 //
3222 // b = TRUE;
3223 // do {
3224 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3225 //
3226 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3227 //
3228 // if (b) {
3229 // free_traverse_ptr(&tp);
3230 // tp = next_tp;
3231 // }
3232 // } while (b);
3233 //
3234 // free_traverse_ptr(&tp);
3235 // }
3236
3237 static NTSTATUS load_sys_chunks(device_extension* Vcb) {
3238 KEY key;
3239 ULONG n = Vcb->superblock.n;
3240
3241 while (n > 0) {
3242 if (n > sizeof(KEY)) {
3243 RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
3244 n -= sizeof(KEY);
3245 } else
3246 return STATUS_SUCCESS;
3247
3248 TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
3249
3250 if (key.obj_type == TYPE_CHUNK_ITEM) {
3251 CHUNK_ITEM* ci;
3252 ULONG cisize;
3253 sys_chunk* sc;
3254
3255 if (n < sizeof(CHUNK_ITEM))
3256 return STATUS_SUCCESS;
3257
3258 ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
3259 cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
3260
3261 if (n < cisize)
3262 return STATUS_SUCCESS;
3263
3264 sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
3265
3266 if (!sc) {
3267 ERR("out of memory\n");
3268 return STATUS_INSUFFICIENT_RESOURCES;
3269 }
3270
3271 sc->key = key;
3272 sc->size = cisize;
3273 sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
3274
3275 if (!sc->data) {
3276 ERR("out of memory\n");
3277 return STATUS_INSUFFICIENT_RESOURCES;
3278 }
3279
3280 RtlCopyMemory(sc->data, ci, sc->size);
3281 InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
3282
3283 n -= cisize;
3284 } else {
3285 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
3286 return STATUS_INTERNAL_ERROR;
3287 }
3288 }
3289
3290 return STATUS_SUCCESS;
3291 }
3292
3293 static root* find_default_subvol(device_extension* Vcb, PIRP Irp) {
3294 LIST_ENTRY* le;
3295
3296 static char fn[] = "default";
3297 static UINT32 crc32 = 0x8dbfc2d2;
3298
3299 if (Vcb->options.subvol_id != 0) {
3300 le = Vcb->roots.Flink;
3301 while (le != &Vcb->roots) {
3302 root* r = CONTAINING_RECORD(le, root, list_entry);
3303
3304 if (r->id == Vcb->options.subvol_id)
3305 return r;
3306
3307 le = le->Flink;
3308 }
3309 }
3310
3311 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
3312 NTSTATUS Status;
3313 KEY searchkey;
3314 traverse_ptr tp;
3315 DIR_ITEM* di;
3316
3317 searchkey.obj_id = Vcb->superblock.root_dir_objectid;
3318 searchkey.obj_type = TYPE_DIR_ITEM;
3319 searchkey.offset = crc32;
3320
3321 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
3322 if (!NT_SUCCESS(Status)) {
3323 ERR("error - find_item returned %08x\n", Status);
3324 goto end;
3325 }
3326
3327 if (keycmp(&tp.item->key, &searchkey)) {
3328 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3329 goto end;
3330 }
3331
3332 if (tp.item->size < sizeof(DIR_ITEM)) {
3333 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
3334 goto end;
3335 }
3336
3337 di = (DIR_ITEM*)tp.item->data;
3338
3339 if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
3340 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);
3341 goto end;
3342 }
3343
3344 if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
3345 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3346 goto end;
3347 }
3348
3349 if (di->key.obj_type != TYPE_ROOT_ITEM) {
3350 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
3351 goto end;
3352 }
3353
3354 le = Vcb->roots.Flink;
3355 while (le != &Vcb->roots) {
3356 root* r = CONTAINING_RECORD(le, root, list_entry);
3357
3358 if (r->id == di->key.obj_id)
3359 return r;
3360
3361 le = le->Flink;
3362 }
3363
3364 ERR("could not find root %llx, using default instead\n", di->key.obj_id);
3365 }
3366
3367 end:
3368 le = Vcb->roots.Flink;
3369 while (le != &Vcb->roots) {
3370 root* r = CONTAINING_RECORD(le, root, list_entry);
3371
3372 if (r->id == BTRFS_ROOT_FSTREE)
3373 return r;
3374
3375 le = le->Flink;
3376 }
3377
3378 return NULL;
3379 }
3380
3381 static NTSTATUS create_worker_threads(PDEVICE_OBJECT DeviceObject) {
3382 device_extension* Vcb = DeviceObject->DeviceExtension;
3383 ULONG i;
3384 NTSTATUS Status;
3385