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