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