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