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