[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 // FIXME - stop async threads
2379
2380 free_fcb(Vcb->volume_fcb);
2381 free_fileref(Vcb->root_fileref);
2382
2383 // FIXME - free any open fcbs?
2384
2385 while (!IsListEmpty(&Vcb->roots)) {
2386 LIST_ENTRY* le = RemoveHeadList(&Vcb->roots);
2387 root* r = CONTAINING_RECORD(le, root, list_entry);
2388
2389 ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
2390 ExFreePool(r->nonpaged);
2391 ExFreePool(r);
2392 }
2393
2394 while (!IsListEmpty(&Vcb->chunks)) {
2395 LIST_ENTRY* le = RemoveHeadList(&Vcb->chunks);
2396 c = CONTAINING_RECORD(le, chunk, list_entry);
2397
2398 while (!IsListEmpty(&c->space)) {
2399 LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2400 s = CONTAINING_RECORD(le2, space, list_entry);
2401
2402 ExFreePool(s);
2403 }
2404
2405 if (c->devices)
2406 ExFreePool(c->devices);
2407
2408 ExFreePool(c->chunk_item);
2409 ExFreePool(c);
2410 }
2411
2412 for (i = 0; i < Vcb->superblock.num_devices; i++) {
2413 while (!IsListEmpty(&Vcb->devices[i].disk_holes)) {
2414 LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].disk_holes);
2415 disk_hole* dh = CONTAINING_RECORD(le, disk_hole, listentry);
2416
2417 ExFreePool(dh);
2418 }
2419 }
2420
2421 ExFreePool(Vcb->devices);
2422
2423 ExDeleteResourceLite(&Vcb->fcb_lock);
2424 ExDeleteResourceLite(&Vcb->load_lock);
2425 ExDeleteResourceLite(&Vcb->tree_lock);
2426
2427 ZwClose(Vcb->flush_thread_handle);
2428 }
2429
2430 static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
2431 NTSTATUS Status;
2432 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
2433 PFILE_OBJECT FileObject = IrpSp->FileObject;
2434 fcb* fcb;
2435 BOOL top_level;
2436
2437 TRACE("cleanup\n");
2438
2439 FsRtlEnterFileSystem();
2440
2441 top_level = is_top_level(Irp);
2442
2443 if (DeviceObject == devobj) {
2444 TRACE("closing file system\n");
2445 Status = STATUS_SUCCESS;
2446 goto exit;
2447 }
2448
2449 if (FileObject && FileObject->FsContext) {
2450 LONG oc;
2451 ccb* ccb;
2452 file_ref* fileref;
2453
2454 fcb = FileObject->FsContext;
2455 ccb = FileObject->FsContext2;
2456 fileref = ccb ? ccb->fileref : NULL;
2457
2458 TRACE("cleanup called for FileObject %p\n", FileObject);
2459 TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb, file_desc(FileObject), fcb->refcount, fcb->open_count);
2460
2461 IoRemoveShareAccess(FileObject, &fcb->share_access);
2462
2463 oc = InterlockedDecrement(&fcb->open_count);
2464 #ifdef DEBUG_FCB_REFCOUNTS
2465 ERR("fcb %p: open_count now %i\n", fcb, oc);
2466 #endif
2467
2468 if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2469 fileref->delete_on_close = TRUE;
2470
2471 if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0)
2472 fileref->delete_on_close = FALSE;
2473
2474 if (oc == 0) {
2475 if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2476 LIST_ENTRY rollback;
2477 InitializeListHead(&rollback);
2478
2479 acquire_tree_lock(fcb->Vcb, TRUE);
2480
2481 Status = delete_fileref(fileref, FileObject, &rollback);
2482
2483 if (NT_SUCCESS(Status)) {
2484 LARGE_INTEGER newlength;
2485
2486 if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject)
2487 CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
2488
2489 newlength.QuadPart = 0;
2490
2491 if (!CcUninitializeCacheMap(FileObject, &newlength, NULL)) {
2492 TRACE("CcUninitializeCacheMap failed\n");
2493 }
2494
2495 clear_rollback(&rollback);
2496 } else
2497 do_rollback(fcb->Vcb, &rollback);
2498
2499 release_tree_lock(fcb->Vcb, TRUE);
2500 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
2501 IO_STATUS_BLOCK iosb;
2502 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2503
2504 if (!NT_SUCCESS(iosb.Status)) {
2505 ERR("CcFlushCache returned %08x\n", iosb.Status);
2506 }
2507
2508 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
2509 ExReleaseResourceLite(fcb->Header.PagingIoResource);
2510
2511 CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
2512
2513 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2514 FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2515 }
2516
2517 if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2518 CcUninitializeCacheMap(FileObject, NULL, NULL);
2519 }
2520
2521 FileObject->Flags |= FO_CLEANUP_COMPLETE;
2522 }
2523
2524 Status = STATUS_SUCCESS;
2525
2526 exit:
2527 Irp->IoStatus.Status = Status;
2528 Irp->IoStatus.Information = 0;
2529
2530 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2531
2532 if (top_level)
2533 IoSetTopLevelIrp(NULL);
2534
2535 FsRtlExitFileSystem();
2536
2537 return Status;
2538 }
2539
2540 ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
2541 ULONG att;
2542 char* eaval;
2543 UINT16 ealen;
2544
2545 // ii can be NULL
2546
2547 if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
2548 if (ealen > 2) {
2549 if (eaval[0] == '0' && eaval[1] == 'x') {
2550 int i;
2551 ULONG dosnum = 0;
2552
2553 for (i = 2; i < ealen; i++) {
2554 dosnum *= 0x10;
2555
2556 if (eaval[i] >= '0' && eaval[i] <= '9')
2557 dosnum |= eaval[i] - '0';
2558 else if (eaval[i] >= 'a' && eaval[i] <= 'f')
2559 dosnum |= eaval[i] + 10 - 'a';
2560 else if (eaval[i] >= 'A' && eaval[i] <= 'F')
2561 dosnum |= eaval[i] + 10 - 'a';
2562 }
2563
2564 TRACE("DOSATTRIB: %08x\n", dosnum);
2565
2566 ExFreePool(eaval);
2567
2568 if (type == BTRFS_TYPE_DIRECTORY)
2569 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2570 else if (type == BTRFS_TYPE_SYMLINK)
2571 dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
2572
2573 return dosnum;
2574 }
2575 }
2576
2577 ExFreePool(eaval);
2578 }
2579
2580 switch (type) {
2581 case BTRFS_TYPE_DIRECTORY:
2582 att = FILE_ATTRIBUTE_DIRECTORY;
2583 break;
2584
2585 case BTRFS_TYPE_SYMLINK:
2586 att = FILE_ATTRIBUTE_REPARSE_POINT;
2587 break;
2588
2589 default:
2590 att = 0;
2591 break;
2592 }
2593
2594 if (dotfile) {
2595 att |= FILE_ATTRIBUTE_HIDDEN;
2596 }
2597
2598 att |= FILE_ATTRIBUTE_ARCHIVE;
2599
2600 // FIXME - get READONLY from ii->st_mode
2601 // FIXME - return SYSTEM for block/char devices?
2602
2603 if (att == 0)
2604 att = FILE_ATTRIBUTE_NORMAL;
2605
2606 return att;
2607 }
2608
2609 // static int STDCALL utf8icmp(char* a, char* b) {
2610 // return strcmp(a, b); // FIXME - don't treat as ASCII
2611 // }
2612
2613 NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
2614 IO_STATUS_BLOCK* IoStatus;
2615 LARGE_INTEGER Offset;
2616 PIRP Irp;
2617 PIO_STACK_LOCATION IrpSp;
2618 NTSTATUS Status;
2619 read_context* context;
2620
2621 num_reads++;
2622
2623 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
2624 if (!context) {
2625 ERR("out of memory\n");
2626 return STATUS_INSUFFICIENT_RESOURCES;
2627 }
2628
2629 RtlZeroMemory(context, sizeof(read_context));
2630 KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
2631
2632 IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
2633 if (!IoStatus) {
2634 ERR("out of memory\n");
2635 ExFreePool(context);
2636 return STATUS_INSUFFICIENT_RESOURCES;
2637 }
2638
2639 Offset.QuadPart = StartingOffset;
2640
2641 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2642 Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
2643
2644 if (!Irp) {
2645 ERR("IoAllocateIrp failed\n");
2646 Status = STATUS_INSUFFICIENT_RESOURCES;
2647 goto exit;
2648 }
2649
2650 IrpSp = IoGetNextIrpStackLocation(Irp);
2651 IrpSp->MajorFunction = IRP_MJ_READ;
2652
2653 if (DeviceObject->Flags & DO_BUFFERED_IO) {
2654 FIXME("FIXME - buffered IO\n");
2655 } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2656 // TRACE("direct IO\n");
2657
2658 Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
2659 if (!Irp->MdlAddress) {
2660 ERR("IoAllocateMdl failed\n");
2661 Status = STATUS_INSUFFICIENT_RESOURCES;
2662 // IoFreeIrp(Irp);
2663 goto exit;
2664 // } else {
2665 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2666 }
2667
2668 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
2669 } else {
2670 // TRACE("neither buffered nor direct IO\n");
2671 Irp->UserBuffer = Buffer;
2672 }
2673
2674 IrpSp->Parameters.Read.Length = Length;
2675 IrpSp->Parameters.Read.ByteOffset = Offset;
2676
2677 Irp->UserIosb = IoStatus;
2678 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2679
2680 Irp->UserEvent = &context->Event;
2681
2682 // IoQueueThreadIrp(Irp);
2683
2684 IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
2685
2686 // if (Override)
2687 // {
2688 // Stack = IoGetNextIrpStackLocation(Irp);
2689 // Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2690 // }
2691
2692 // TRACE("Calling IO Driver... with irp %p\n", Irp);
2693 Status = IoCallDriver(DeviceObject, Irp);
2694
2695 // TRACE("Waiting for IO Operation for %p\n", Irp);
2696 if (Status == STATUS_PENDING) {
2697 // TRACE("Operation pending\n");
2698 KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
2699 // TRACE("Getting IO Status... for %p\n", Irp);
2700 Status = context->iosb.Status;
2701 }
2702
2703 if (DeviceObject->Flags & DO_DIRECT_IO) {
2704 MmUnlockPages(Irp->MdlAddress);
2705 IoFreeMdl(Irp->MdlAddress);
2706 }
2707
2708 exit:
2709 IoFreeIrp(Irp);
2710
2711 ExFreePool(IoStatus);
2712 ExFreePool(context);
2713
2714 return Status;
2715 }
2716
2717 static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device) {
2718 NTSTATUS Status;
2719 superblock* sb;
2720 unsigned int i, to_read;
2721 UINT32 crc32;
2722
2723 to_read = sector_align(sizeof(superblock), device->SectorSize);
2724
2725 sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
2726 if (!sb) {
2727 ERR("out of memory\n");
2728 return STATUS_INSUFFICIENT_RESOURCES;
2729 }
2730
2731 i = 0;
2732
2733 while (superblock_addrs[i] > 0) {
2734 if (i > 0 && superblock_addrs[i] + sizeof(superblock) > Vcb->length)
2735 break;
2736
2737 Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
2738 if (!NT_SUCCESS(Status)) {
2739 ERR("Failed to read superblock %u: %08x\n", i, Status);
2740 ExFreePool(sb);
2741 return Status;
2742 }
2743
2744 TRACE("got superblock %u!\n", i);
2745
2746 if (i == 0 || sb->generation > Vcb->superblock.generation)
2747 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2748
2749 i++;
2750 }
2751
2752 ExFreePool(sb);
2753
2754 crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
2755 crc32 = ~crc32;
2756 TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
2757
2758 if (crc32 != *((UINT32*)Vcb->superblock.checksum))
2759 return STATUS_INTERNAL_ERROR; // FIXME - correct error?
2760
2761 TRACE("label is %s\n", Vcb->superblock.label);
2762 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2763
2764 return STATUS_SUCCESS;
2765 }
2766
2767 NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer, ULONG InputBufferSize,
2768 PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override, IO_STATUS_BLOCK* iosb)
2769 {
2770 PIRP Irp;
2771 KEVENT Event;
2772 NTSTATUS Status;
2773 PIO_STACK_LOCATION Stack;
2774 IO_STATUS_BLOCK IoStatus;
2775
2776 KeInitializeEvent(&Event, NotificationEvent, FALSE);
2777
2778 Irp = IoBuildDeviceIoControlRequest(ControlCode,
2779 DeviceObject,
2780 InputBuffer,
2781 InputBufferSize,
2782 OutputBuffer,
2783 OutputBufferSize,
2784 FALSE,
2785 &Event,
2786 &IoStatus);
2787
2788 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
2789
2790 if (Override) {
2791 Stack = IoGetNextIrpStackLocation(Irp);
2792 Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2793 }
2794
2795 Status = IoCallDriver(DeviceObject, Irp);
2796
2797 if (Status == STATUS_PENDING) {
2798 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
2799 Status = IoStatus.Status;
2800 }
2801
2802 if (iosb)
2803 *iosb = IoStatus;
2804
2805 return Status;
2806 }
2807
2808 // static void STDCALL find_chunk_root(device_extension* Vcb) {
2809 // UINT32 i;
2810 // KEY* key;
2811 //
2812 // i = 0;
2813 // while (i < Vcb->superblock.n) {
2814 // key = &Vcb->superblock.sys_chunk_array[i];
2815 // i += sizeof(KEY);
2816 // }
2817 //
2818 // // FIXME
2819 // }
2820
2821 // static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) {
2822 // if (!Vcb->log_to_phys) {
2823 // Vcb->log_to_phys = ltp;
2824 // ltp->next = NULL;
2825 // return;
2826 // }
2827 //
2828 // // FIXME - these should be ordered
2829 // ltp->next = Vcb->log_to_phys;
2830 // Vcb->log_to_phys = ltp;
2831 // }
2832
2833 static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
2834 root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
2835 if (!r) {
2836 ERR("out of memory\n");
2837 return STATUS_INSUFFICIENT_RESOURCES;
2838 }
2839
2840 r->id = id;
2841 r->path.Buffer = NULL;
2842 r->treeholder.address = addr;
2843 r->treeholder.tree = NULL;
2844 init_tree_holder(&r->treeholder);
2845 InitializeListHead(&r->fcbs);
2846
2847 r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
2848 if (!r->nonpaged) {
2849 ERR("out of memory\n");
2850 ExFreePool(r);
2851 return STATUS_INSUFFICIENT_RESOURCES;
2852 }
2853
2854 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
2855
2856 r->lastinode = 0;
2857
2858 if (tp) {
2859 RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
2860 if (tp->item->size < sizeof(ROOT_ITEM))
2861 RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
2862 }
2863
2864 InsertTailList(&Vcb->roots, &r->list_entry);
2865
2866 switch (r->id) {
2867 case BTRFS_ROOT_ROOT:
2868 Vcb->root_root = r;
2869 break;
2870
2871 case BTRFS_ROOT_EXTENT:
2872 Vcb->extent_root = r;
2873 break;
2874
2875 case BTRFS_ROOT_CHUNK:
2876 Vcb->chunk_root = r;
2877 break;
2878
2879 case BTRFS_ROOT_DEVTREE:
2880 Vcb->dev_root = r;
2881 break;
2882
2883 case BTRFS_ROOT_CHECKSUM:
2884 Vcb->checksum_root = r;
2885 break;
2886
2887 case BTRFS_ROOT_UUID:
2888 Vcb->uuid_root = r;
2889 break;
2890 }
2891
2892 return STATUS_SUCCESS;
2893 }
2894
2895 static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
2896 traverse_ptr tp, next_tp;
2897 KEY searchkey;
2898 BOOL b;
2899 NTSTATUS Status;
2900
2901 searchkey.obj_id = 0;
2902 searchkey.obj_type = 0;
2903 searchkey.offset = 0;
2904
2905 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
2906 if (!NT_SUCCESS(Status)) {
2907 ERR("error - find_tree returned %08x\n", Status);
2908 return Status;
2909 }
2910
2911 do {
2912 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2913
2914 if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
2915 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2916
2917 if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
2918 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));
2919 } else {
2920 TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
2921
2922 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
2923 if (!NT_SUCCESS(Status)) {
2924 ERR("add_root returned %08x\n", Status);
2925 return Status;
2926 }
2927 }
2928 }
2929
2930 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
2931
2932 if (b)
2933 tp = next_tp;
2934 } while (b);
2935
2936 return STATUS_SUCCESS;
2937 }
2938
2939 static NTSTATUS add_disk_hole(LIST_ENTRY* disk_holes, UINT64 address, UINT64 size) {
2940 disk_hole* dh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG);
2941
2942 if (!dh) {
2943 ERR("out of memory\n");
2944 return STATUS_INSUFFICIENT_RESOURCES;
2945 }
2946
2947 dh->address = address;
2948 dh->size = size;
2949 dh->provisional = FALSE;
2950
2951 InsertTailList(disk_holes, &dh->listentry);
2952
2953 return STATUS_SUCCESS;
2954 }
2955
2956 static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
2957 KEY searchkey;
2958 traverse_ptr tp, next_tp;
2959 BOOL b;
2960 UINT64 lastaddr;
2961 NTSTATUS Status;
2962
2963 InitializeListHead(&dev->disk_holes);
2964
2965 searchkey.obj_id = dev->devitem.dev_id;
2966 searchkey.obj_type = TYPE_DEV_EXTENT;
2967 searchkey.offset = 0;
2968
2969 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
2970 if (!NT_SUCCESS(Status)) {
2971 ERR("error - find_tree returned %08x\n", Status);
2972 return Status;
2973 }
2974
2975 lastaddr = 0;
2976
2977 do {
2978 if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
2979 if (tp.item->size >= sizeof(DEV_EXTENT)) {
2980 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
2981
2982 if (tp.item->key.offset > lastaddr) {
2983 Status = add_disk_hole(&dev->disk_holes, lastaddr, tp.item->key.offset - lastaddr);
2984 if (!NT_SUCCESS(Status)) {
2985 ERR("add_disk_hole returned %08x\n", Status);
2986 return Status;
2987 }
2988 }
2989
2990 lastaddr = tp.item->key.offset + de->length;
2991 } else {
2992 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));
2993 }
2994 }
2995
2996 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
2997
2998 if (b) {
2999 tp = next_tp;
3000 if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
3001 break;
3002 }
3003 } while (b);
3004
3005 if (lastaddr < dev->devitem.num_bytes) {
3006 Status = add_disk_hole(&dev->disk_holes, lastaddr, dev->devitem.num_bytes - lastaddr);
3007 if (!NT_SUCCESS(Status)) {
3008 ERR("add_disk_hole returned %08x\n", Status);
3009 return Status;
3010 }
3011 }
3012
3013 // FIXME - free disk_holes when unmounting
3014
3015 return STATUS_SUCCESS;
3016 }
3017
3018 device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
3019 UINT64 i;
3020
3021 for (i = 0; i < Vcb->superblock.num_devices; i++) {
3022 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
3023 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],
3024 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]);
3025
3026 if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3027 TRACE("returning device %llx\n", i);
3028 return &Vcb->devices[i];
3029 }
3030 }
3031
3032 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3033 uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
3034 uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
3035
3036 return NULL;
3037 }
3038
3039 static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
3040 traverse_ptr tp, next_tp;
3041 KEY searchkey;
3042 BOOL b;
3043 chunk* c;
3044 UINT64 i;
3045 NTSTATUS Status;
3046
3047 searchkey.obj_id = 0;
3048 searchkey.obj_type = 0;
3049 searchkey.offset = 0;
3050
3051 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
3052 if (!NT_SUCCESS(Status)) {
3053 ERR("error - find_item returned %08x\n", Status);
3054 return Status;
3055 }
3056
3057 do {
3058 TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3059
3060 if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM && tp.item->key.offset == 1) {
3061 // FIXME - this is a hack; make this work with multiple devices!
3062 if (tp.item->size > 0)
3063 RtlCopyMemory(&Vcb->devices[0].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
3064 } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
3065 if (tp.item->size < sizeof(CHUNK_ITEM)) {
3066 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));
3067 } else {
3068 c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
3069
3070 if (!c) {
3071 ERR("out of memory\n");
3072 return STATUS_INSUFFICIENT_RESOURCES;
3073 }
3074
3075 c->size = tp.item->size;
3076 c->offset = tp.item->key.offset;
3077 c->used = c->oldused = 0;
3078 c->space_changed = FALSE;
3079 c->cache_inode = 0;
3080 c->cache_size = 0;
3081
3082 c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
3083
3084 if (!c->chunk_item) {
3085 ERR("out of memory\n");
3086 return STATUS_INSUFFICIENT_RESOURCES;
3087 }
3088
3089 RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
3090
3091 if (c->chunk_item->num_stripes > 0) {
3092 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3093
3094 c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
3095
3096 if (!c->devices) {
3097 ERR("out of memory\n");
3098 return STATUS_INSUFFICIENT_RESOURCES;
3099 }
3100
3101 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3102 c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
3103 TRACE("device %llu = %p\n", i, c->devices[i]);
3104 }
3105 } else
3106 c->devices = NULL;
3107
3108 InitializeListHead(&c->space);
3109
3110 InsertTailList(&Vcb->chunks, &c->list_entry);
3111 }
3112 }
3113
3114 b = find_next_item(Vcb, &tp, &next_tp, FALSE);
3115
3116 if (b)
3117 tp = next_tp;
3118 } while (b);
3119
3120 Vcb->log_to_phys_loaded = TRUE;
3121
3122 return STATUS_SUCCESS;
3123 }
3124
3125 void protect_superblocks(device_extension* Vcb, chunk* c) {
3126 int i = 0, j;
3127 UINT64 off_start, off_end;
3128
3129 // FIXME - this will need modifying for RAID
3130
3131 while (superblock_addrs[i] != 0) {
3132 CHUNK_ITEM* ci = c->chunk_item;
3133 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
3134
3135 for (j = 0; j < ci->num_stripes; j++) {
3136 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
3137 TRACE("cut out superblock in chunk %llx\n", c->offset);
3138
3139 // The Linux driver protects the whole stripe in which the superblock lives
3140
3141 off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
3142 off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
3143
3144 add_to_space_list(c, c->offset + off_start, off_end - off_start, SPACE_TYPE_USED);
3145 }
3146 }
3147
3148 i++;
3149 }
3150 }
3151
3152 static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
3153 LIST_ENTRY* le = Vcb->chunks.Flink;
3154 chunk* c;
3155 KEY searchkey;
3156 traverse_ptr tp;
3157 BLOCK_GROUP_ITEM* bgi;
3158 NTSTATUS Status;
3159
3160 // c00000,c0,800000
3161 // block_group_item size=7f0000 chunktreeid=100 flags=1
3162
3163 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3164
3165 while (le != &Vcb->chunks) {
3166 c = CONTAINING_RECORD(le, chunk, list_entry);
3167
3168 searchkey.obj_id = c->offset;
3169 searchkey.offset = c->chunk_item->size;
3170
3171 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3172 if (!NT_SUCCESS(Status)) {
3173 ERR("error - find_item returned %08x\n", Status);
3174 return Status;
3175 }
3176
3177 if (!keycmp(&searchkey, &tp.item->key)) {
3178 if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
3179 bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
3180
3181 c->used = c->oldused = bgi->used;
3182
3183 TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
3184 } else {
3185 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3186 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));
3187 }
3188 }
3189
3190 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3191 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3192 //
3193 // return (addr - c->offset) + cis->offset;
3194 // }
3195
3196 // FIXME - make sure we free occasionally after doing one of these, or we
3197 // might use up a lot of memory with a big disk.
3198
3199 Status = load_free_space_cache(Vcb, c);
3200 if (!NT_SUCCESS(Status)) {
3201 ERR("load_free_space_cache returned %08x\n", Status);
3202 return Status;
3203 }
3204
3205 protect_superblocks(Vcb, c);
3206
3207 le = le->Flink;
3208 }
3209
3210 return STATUS_SUCCESS;
3211 }
3212
3213 // static void STDCALL root_test(device_extension* Vcb) {
3214 // root* r;
3215 // KEY searchkey;
3216 // traverse_ptr tp, next_tp;
3217 // BOOL b;
3218 //
3219 // r = Vcb->roots;
3220 // while (r) {
3221 // if (r->id == 0x102)
3222 // break;
3223 // r = r->next;
3224 // }
3225 //
3226 // if (!r) {
3227 // ERR("Could not find root tree.\n");
3228 // return;
3229 // }
3230 //
3231 // searchkey.obj_id = 0x1b6;
3232 // searchkey.obj_type = 0xb;
3233 // searchkey.offset = 0;
3234 //
3235 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3236 // ERR("Could not find first item.\n");
3237 // return;
3238 // }
3239 //
3240 // b = TRUE;
3241 // do {
3242 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3243 //
3244 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3245 //
3246 // if (b) {
3247 // free_traverse_ptr(&tp);
3248 // tp = next_tp;
3249 // }
3250 // } while (b);
3251 //
3252 // free_traverse_ptr(&tp);
3253 // }
3254
3255 static NTSTATUS load_sys_chunks(device_extension* Vcb) {
3256 KEY key;
3257 ULONG n = Vcb->superblock.n;
3258
3259 while (n > 0) {
3260 if (n > sizeof(KEY)) {
3261 RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
3262 n -= sizeof(KEY);
3263 } else
3264 return STATUS_SUCCESS;
3265
3266 TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
3267
3268 if (key.obj_type == TYPE_CHUNK_ITEM) {
3269 CHUNK_ITEM* ci;
3270 ULONG cisize;
3271 sys_chunk* sc;
3272
3273 if (n < sizeof(CHUNK_ITEM))
3274 return STATUS_SUCCESS;
3275
3276 ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
3277 cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
3278
3279 if (n < cisize)
3280 return STATUS_SUCCESS;
3281
3282 sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
3283
3284 if (!sc) {
3285 ERR("out of memory\n");
3286 return STATUS_INSUFFICIENT_RESOURCES;
3287 }
3288
3289 sc->key = key;
3290 sc->size = cisize;
3291 sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
3292
3293 if (!sc->data) {
3294 ERR("out of memory\n");
3295 return STATUS_INSUFFICIENT_RESOURCES;
3296 }
3297
3298 RtlCopyMemory(sc->data, ci, sc->size);
3299 InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
3300
3301 n -= cisize;
3302 } else {
3303 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
3304 return STATUS_INTERNAL_ERROR;
3305 }
3306 }
3307
3308 return STATUS_SUCCESS;
3309 }
3310
3311 static root* find_default_subvol(device_extension* Vcb) {
3312 root* subvol;
3313 UINT64 inode;
3314 UINT8 type;
3315 UNICODE_STRING filename;
3316 LIST_ENTRY* le;
3317
3318 static WCHAR fn[] = L"default";
3319 static UINT32 crc32 = 0x8dbfc2d2;
3320
3321 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
3322 filename.Buffer = fn;
3323 filename.Length = filename.MaximumLength = (USHORT)wcslen(fn) * sizeof(WCHAR);
3324
3325 if (!find_file_in_dir_with_crc32(Vcb, &filename, crc32, Vcb->root_root, Vcb->superblock.root_dir_objectid, &subvol, &inode, &type, NULL))
3326 WARN("couldn't find default subvol DIR_ITEM, using default tree\n");
3327 else
3328 return subvol;
3329 }
3330
3331 le = Vcb->roots.Flink;
3332 while (le != &Vcb->roots) {
3333 root* r = CONTAINING_RECORD(le, root, list_entry);
3334
3335 if (r->id == BTRFS_ROOT_FSTREE)
3336 return r;
3337
3338 le = le->Flink;
3339 }
3340
3341 return NULL;
3342 }
3343
3344 static BOOL is_device_removable(PDEVICE_OBJECT devobj) {
3345 NTSTATUS Status;
3346 STORAGE_HOTPLUG_INFO shi;
3347
3348 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
3349
3350 if (!NT_SUCCESS(Status)) {
3351 ERR("dev_ioctl returned %08x\n", Status);
3352 return FALSE;
3353 }
3354
3355 return shi.MediaRemovable != 0 ? TRUE : FALSE;
3356 }
3357
3358 static ULONG get_device_change_count(PDEVICE_OBJECT devobj) {
3359 NTSTATUS Status;
3360 ULONG cc;
3361 IO_STATUS_BLOCK iosb;
3362
3363 Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
3364
3365 if (!NT_SUCCESS(Status)) {
3366 ERR("dev_ioctl returned %08x\n", Status);