ReactOS 0.4.16-dev-13-ge2fc578
btrfs.c
Go to the documentation of this file.
1/* Copyright (c) Mark Harmstone 2016-17
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#include "xxhash.h"
24#include "crc32c.h"
25#ifndef __REACTOS__
26#ifndef _MSC_VER
27#include <cpuid.h>
28#else
29#include <intrin.h>
30#endif
31#endif // __REACTOS__
32#include <ntddscsi.h>
33#include "btrfs.h"
34#include <ata.h>
35
36#ifndef _MSC_VER
37#include <initguid.h>
38#include <ntddstor.h>
39#undef INITGUID
40#endif
41
42#include <ntdddisk.h>
43#include <ntddvol.h>
44
45#ifdef _MSC_VER
46#include <initguid.h>
47#include <ntddstor.h>
48#undef INITGUID
49#endif
50
51#include <ntstrsafe.h>
52
53#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
54 BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
55 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
56 BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD | BTRFS_INCOMPAT_FLAGS_METADATA_UUID | BTRFS_INCOMPAT_FLAGS_RAID1C34)
57#define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID | \
58 BTRFS_COMPAT_RO_FLAGS_VERITY)
59
60static const WCHAR device_name[] = {'\\','B','t','r','f','s',0};
61static const WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
62
63DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
64
88bool log_started = false;
101bool diskacc = false;
105bool finished_probing = false;
107bool degraded_wait = true;
109bool shutting_down = false;
112extern uint64_t boot_subvol;
113
114#ifdef _DEBUG
115PFILE_OBJECT comfo = NULL;
116PDEVICE_OBJECT comdo = NULL;
117HANDLE log_handle = NULL;
118ERESOURCE log_lock;
119HANDLE serial_thread_handle = NULL;
120
121static void init_serial(bool first_time);
122#endif
123
125static void __stdcall do_xor_basic(uint8_t* buf1, uint8_t* buf2, uint32_t len);
126
128
129typedef struct {
133
134// no longer in Windows headers??
136
137#ifdef _DEBUG
138_Function_class_(IO_COMPLETION_ROUTINE)
139static NTSTATUS __stdcall dbg_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
140 read_context* context = conptr;
141
143
144 context->iosb = Irp->IoStatus;
145 KeSetEvent(&context->Event, 0, false);
146
148}
149
150#define DEBUG_MESSAGE_LEN 1024
151
152#ifdef DEBUG_LONG_MESSAGES
153void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) {
154#else
155void _debug_message(_In_ const char* func, _In_ char* s, ...) {
156#endif
160 PIRP Irp;
161 va_list ap;
162 char *buf2, *buf;
165
166 buf2 = ExAllocatePoolWithTag(NonPagedPool, DEBUG_MESSAGE_LEN, ALLOC_TAG);
167
168 if (!buf2) {
169 DbgPrint("Couldn't allocate buffer in debug_message\n");
170 return;
171 }
172
173#ifdef DEBUG_LONG_MESSAGES
174 sprintf(buf2, "%p:%s:%s:%u:", (void*)PsGetCurrentThread(), func, file, line);
175#else
176 sprintf(buf2, "%p:%s:", (void*)PsGetCurrentThread(), func);
177#endif
178 buf = &buf2[strlen(buf2)];
179
180 va_start(ap, s);
181
182 RtlStringCbVPrintfA(buf, DEBUG_MESSAGE_LEN - strlen(buf2), s, ap);
183
184 ExAcquireResourceSharedLite(&log_lock, true);
185
186 if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
187 DbgPrint(buf2);
188 } else if (log_device.Length > 0) {
189 if (!comdo) {
190 DbgPrint(buf2);
191 goto exit2;
192 }
193
194 length = (uint32_t)strlen(buf2);
195
196 offset.u.LowPart = 0;
197 offset.u.HighPart = 0;
198
200
202
203 Irp = IoAllocateIrp(comdo->StackSize, false);
204
205 if (!Irp) {
206 DbgPrint("IoAllocateIrp failed\n");
207 goto exit2;
208 }
209
212 IrpSp->FileObject = comfo;
213
214 if (comdo->Flags & DO_BUFFERED_IO) {
215 Irp->AssociatedIrp.SystemBuffer = buf2;
216
217 Irp->Flags = IRP_BUFFERED_IO;
218 } else if (comdo->Flags & DO_DIRECT_IO) {
219 Irp->MdlAddress = IoAllocateMdl(buf2, length, false, false, NULL);
220 if (!Irp->MdlAddress) {
221 DbgPrint("IoAllocateMdl failed\n");
222 goto exit;
223 }
224
225 MmBuildMdlForNonPagedPool(Irp->MdlAddress);
226 } else {
227 Irp->UserBuffer = buf2;
228 }
229
230 IrpSp->Parameters.Write.Length = length;
231 IrpSp->Parameters.Write.ByteOffset = offset;
232
233 Irp->UserIosb = &context.iosb;
234
235 Irp->UserEvent = &context.Event;
236
237 IoSetCompletionRoutine(Irp, dbg_completion, &context, true, true, true);
238
239 Status = IoCallDriver(comdo, Irp);
240
241 if (Status == STATUS_PENDING) {
243 Status = context.iosb.Status;
244 }
245
246 if (comdo->Flags & DO_DIRECT_IO)
247 IoFreeMdl(Irp->MdlAddress);
248
249 if (!NT_SUCCESS(Status)) {
250 DbgPrint("failed to write to COM1 - error %08lx\n", Status);
251 goto exit;
252 }
253
254exit:
255 IoFreeIrp(Irp);
256 } else if (log_handle != NULL) {
258
259 length = (uint32_t)strlen(buf2);
260
261 Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
262
263 if (!NT_SUCCESS(Status)) {
264 DbgPrint("failed to write to file - error %08lx\n", Status);
265 }
266 }
267
268exit2:
269 ExReleaseResourceLite(&log_lock);
270
271 va_end(ap);
272
273 if (buf2)
274 ExFreePool(buf2);
275}
276#endif
277
279 if (!IoGetTopLevelIrp()) {
281 return true;
282 }
283
284 return false;
285}
286
287static void __stdcall do_xor_basic(uint8_t* buf1, uint8_t* buf2, uint32_t len) {
288 uint32_t j;
289
290#if defined(_ARM_) || defined(_ARM64_)
291 uint64x2_t x1, x2;
292
293 if (((uintptr_t)buf1 & 0xf) == 0 && ((uintptr_t)buf2 & 0xf) == 0) {
294 while (len >= 16) {
295 x1 = vld1q_u64((const uint64_t*)buf1);
296 x2 = vld1q_u64((const uint64_t*)buf2);
297 x1 = veorq_u64(x1, x2);
298 vst1q_u64((uint64_t*)buf1, x1);
299
300 buf1 += 16;
301 buf2 += 16;
302 len -= 16;
303 }
304 }
305#endif
306
307#if defined(_AMD64_) || defined(_ARM64_)
308 while (len > 8) {
309 *(uint64_t*)buf1 ^= *(uint64_t*)buf2;
310 buf1 += 8;
311 buf2 += 8;
312 len -= 8;
313 }
314#endif
315
316 while (len > 4) {
317 *(uint32_t*)buf1 ^= *(uint32_t*)buf2;
318 buf1 += 4;
319 buf2 += 4;
320 len -= 4;
321 }
322
323 for (j = 0; j < len; j++) {
324 *buf1 ^= *buf2;
325 buf1++;
326 buf2++;
327 }
328}
329
330_Function_class_(DRIVER_UNLOAD)
332 UNICODE_STRING dosdevice_nameW;
333
334 TRACE("(%p)\n", DriverObject);
335
336 dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
337 dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) - sizeof(WCHAR);
338
339 IoDeleteSymbolicLink(&dosdevice_nameW);
340 IoDeleteDevice(DriverObject->DeviceObject);
341
342 while (!IsListEmpty(&uid_map_list)) {
344 uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
345
346 ExFreePool(um->sid);
347
348 ExFreePool(um);
349 }
350
351 while (!IsListEmpty(&gid_map_list)) {
353
354 ExFreePool(gm->sid);
355 ExFreePool(gm);
356 }
357
358 // FIXME - free volumes and their devpaths
359
360#ifdef _DEBUG
361 if (comfo)
362 ObDereferenceObject(comfo);
363
364 if (log_handle)
365 ZwClose(log_handle);
366#endif
367
370
371 if (log_device.Buffer)
373
374 if (log_file.Buffer)
376
379
380#ifdef _DEBUG
381 ExDeleteResourceLite(&log_lock);
382#endif
384}
385
387 KEY searchkey;
388 traverse_ptr tp, prev_tp;
390
391 // get last entry
392 searchkey.obj_id = 0xffffffffffffffff;
393 searchkey.obj_type = 0xff;
394 searchkey.offset = 0xffffffffffffffff;
395
396 Status = find_item(Vcb, r, &tp, &searchkey, false, Irp);
397 if (!NT_SUCCESS(Status)) {
398 ERR("error - find_item returned %08lx\n", Status);
399 return false;
400 }
401
403 r->lastinode = tp.item->key.obj_id;
404 TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
405 return true;
406 }
407
408 while (find_prev_item(Vcb, &tp, &prev_tp, Irp)) {
409 tp = prev_tp;
410
411 TRACE("moving on to %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
412
414 r->lastinode = tp.item->key.obj_id;
415 TRACE("last inode for tree %I64x is %I64x\n", r->id, r->lastinode);
416 return true;
417 }
418 }
419
420 r->lastinode = SUBVOL_ROOT_INODE;
421
422 WARN("no INODE_ITEMs in tree %I64x\n", r->id);
423
424 return true;
425}
426
428static bool extract_xattr(_In_reads_bytes_(size) void* item, _In_ USHORT size, _In_z_ char* name, _Out_ uint8_t** data, _Out_ uint16_t* datalen) {
429 DIR_ITEM* xa = (DIR_ITEM*)item;
430 USHORT xasize;
431
432 while (true) {
433 if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
434 WARN("DIR_ITEM is truncated\n");
435 return false;
436 }
437
438 if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
439 TRACE("found xattr %s\n", name);
440
441 *datalen = xa->m;
442
443 if (xa->m > 0) {
445 if (!*data) {
446 ERR("out of memory\n");
447 return false;
448 }
449
450 RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
451 } else
452 *data = NULL;
453
454 return true;
455 }
456
457 xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
458
459 if (size > xasize) {
460 size -= xasize;
461 xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
462 } else
463 break;
464 }
465
466 TRACE("xattr %s not found\n", name);
467
468 return false;
469}
470
471_Success_(return)
472bool get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* subvol, _In_ uint64_t inode, _In_z_ char* name, _In_ uint32_t crc32,
474 KEY searchkey;
477
478 TRACE("(%p, %I64x, %I64x, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
479
480 searchkey.obj_id = inode;
481 searchkey.obj_type = TYPE_XATTR_ITEM;
482 searchkey.offset = crc32;
483
484 Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
485 if (!NT_SUCCESS(Status)) {
486 ERR("error - find_item returned %08lx\n", Status);
487 return false;
488 }
489
490 if (keycmp(tp.item->key, searchkey)) {
491 TRACE("could not find item (%I64x,%x,%I64x)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
492 return false;
493 }
494
495 if (tp.item->size < sizeof(DIR_ITEM)) {
496 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
497 return false;
498 }
499
500 return extract_xattr(tp.item->data, tp.item->size, name, data, datalen);
501}
502
508 device_extension* Vcb = DeviceObject->DeviceExtension;
509 bool top_level;
510
512
513 TRACE("close\n");
514
515 top_level = is_top_level(Irp);
516
518 TRACE("Closing file system\n");
520 goto end;
521 } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
523 goto end;
524 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
526 goto end;
527 }
528
530
531 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
532
534
535end:
536 Irp->IoStatus.Status = Status;
537 Irp->IoStatus.Information = 0;
538
540
541 if (top_level)
543
544 TRACE("returning %08lx\n", Status);
545
547
548 return Status;
549}
550
553static NTSTATUS __stdcall drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
557 fcb* fcb = FileObject->FsContext;
558 device_extension* Vcb = DeviceObject->DeviceExtension;
559 bool top_level;
560
562
563 TRACE("flush buffers\n");
564
565 top_level = is_top_level(Irp);
566
567 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
569 goto end;
570 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
572 goto end;
573 }
574
575 if (!fcb) {
576 ERR("fcb was NULL\n");
578 goto end;
579 }
580
581 if (fcb == Vcb->volume_fcb) {
583 goto end;
584 }
585
587
588 Irp->IoStatus.Information = 0;
589
590 fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
591
593 Irp->IoStatus.Status = Status;
594
595 if (fcb->type != BTRFS_TYPE_DIRECTORY) {
596 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &Irp->IoStatus);
597
598 if (fcb->Header.PagingIoResource) {
599 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
600 ExReleaseResourceLite(fcb->Header.PagingIoResource);
601 }
602
603 Status = Irp->IoStatus.Status;
604 }
605
606end:
608
609 TRACE("returning %08lx\n", Status);
610
611 if (top_level)
613
615
616 return Status;
617}
618
620 uint64_t nfactor, dfactor, sectors_used;
621
622 if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) {
623 nfactor = 1;
624 dfactor = 2;
625 } else if (Vcb->data_flags & BLOCK_FLAG_RAID5) {
626 nfactor = Vcb->superblock.num_devices - 1;
627 dfactor = Vcb->superblock.num_devices;
628 } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
629 nfactor = Vcb->superblock.num_devices - 2;
630 dfactor = Vcb->superblock.num_devices;
631 } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C3) {
632 nfactor = 1;
633 dfactor = 3;
634 } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C4) {
635 nfactor = 1;
636 dfactor = 4;
637 } else {
638 nfactor = 1;
639 dfactor = 1;
640 }
641
642 sectors_used = (Vcb->superblock.bytes_used >> Vcb->sector_shift) * nfactor / dfactor;
643
644 *totalsize = (Vcb->superblock.total_bytes >> Vcb->sector_shift) * nfactor / dfactor;
645 *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
646}
647
648#ifndef __REACTOS__
649// simplified version of FsRtlAreNamesEqual, which can be a bottleneck!
650static bool compare_strings(const UNICODE_STRING* us1, const UNICODE_STRING* us2) {
651 if (us1->Length != us2->Length)
652 return false;
653
654 WCHAR* s1 = us1->Buffer;
655 WCHAR* s2 = us2->Buffer;
656
657 for (unsigned int i = 0; i < us1->Length; i++) {
658 WCHAR c1 = *s1;
659 WCHAR c2 = *s2;
660
661 if (c1 != c2) {
662 if (c1 >= 'a' && c1 <= 'z')
663 c1 = c1 - 'a' + 'A';
664
665 if (c2 >= 'a' && c2 <= 'z')
666 c2 = c2 - 'a' + 'A';
667
668 if (c1 != c2)
669 return false;
670 }
671
672 s1++;
673 s2++;
674 }
675
676 return true;
677}
678
679#define INIT_UNICODE_STRING(var, val) UNICODE_STRING us##var; us##var.Buffer = (WCHAR*)val; us##var.Length = us##var.MaximumLength = sizeof(val) - sizeof(WCHAR);
680
681// This function exists because we have to lie about our FS type in certain situations.
682// MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
683// it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
684// The command mklink refuses to create hard links on anything other than NTFS, so we have to
685// blacklist cmd.exe too.
686
687static bool lie_about_fs_type() {
690 PPEB peb;
691 LIST_ENTRY* le;
692 ULONG retlen;
693#ifdef _AMD64_
694 ULONG_PTR wow64info;
695#endif
696
697 INIT_UNICODE_STRING(mpr, L"MPR.DLL");
698 INIT_UNICODE_STRING(cmd, L"CMD.EXE");
699 INIT_UNICODE_STRING(fsutil, L"FSUTIL.EXE");
700 INIT_UNICODE_STRING(storsvc, L"STORSVC.DLL");
701
702 /* Not doing a Volkswagen, honest! Some IFS tests won't run if not recognized FS. */
703 INIT_UNICODE_STRING(ifstest, L"IFSTEST.EXE");
704
705 if (!PsGetCurrentProcess())
706 return false;
707
708#ifdef _AMD64_
710
711 if (NT_SUCCESS(Status) && wow64info != 0)
712 return true;
713#endif
714
716
717 if (!NT_SUCCESS(Status)) {
718 ERR("ZwQueryInformationProcess returned %08lx\n", Status);
719 return false;
720 }
721
722 if (!pbi.PebBaseAddress)
723 return false;
724
725 peb = pbi.PebBaseAddress;
726
727 if (!peb->Ldr)
728 return false;
729
731 while (le != &peb->Ldr->InMemoryOrderModuleList) {
733 bool blacklist = false;
734
735 if (entry->FullDllName.Length >= usmpr.Length) {
737
738 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usmpr.Length) / sizeof(WCHAR)];
739 name.Length = name.MaximumLength = usmpr.Length;
740
741 blacklist = compare_strings(&name, &usmpr);
742 }
743
744 if (!blacklist && entry->FullDllName.Length >= uscmd.Length) {
746
747 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - uscmd.Length) / sizeof(WCHAR)];
748 name.Length = name.MaximumLength = uscmd.Length;
749
750 blacklist = compare_strings(&name, &uscmd);
751 }
752
753 if (!blacklist && entry->FullDllName.Length >= usfsutil.Length) {
755
756 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usfsutil.Length) / sizeof(WCHAR)];
757 name.Length = name.MaximumLength = usfsutil.Length;
758
759 blacklist = compare_strings(&name, &usfsutil);
760 }
761
762 if (!blacklist && entry->FullDllName.Length >= usstorsvc.Length) {
764
765 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usstorsvc.Length) / sizeof(WCHAR)];
766 name.Length = name.MaximumLength = usstorsvc.Length;
767
768 blacklist = compare_strings(&name, &usstorsvc);
769 }
770
771 if (!blacklist && entry->FullDllName.Length >= usifstest.Length) {
773
774 name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usifstest.Length) / sizeof(WCHAR)];
775 name.Length = name.MaximumLength = usifstest.Length;
776
777 blacklist = compare_strings(&name, &usifstest);
778 }
779
780 if (blacklist) {
781 void** frames;
782 ULONG i, num_frames;
783
784 frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
785 if (!frames) {
786 ERR("out of memory\n");
787 return false;
788 }
789
790 num_frames = RtlWalkFrameChain(frames, 256, 1);
791
792 for (i = 0; i < num_frames; i++) {
793 // entry->Reserved3[1] appears to be the image size
794 if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
795 ExFreePool(frames);
796 return true;
797 }
798 }
799
800 ExFreePool(frames);
801 }
802
803 le = le->Flink;
804 }
805
806 return false;
807}
808#endif // __REACTOS__
809
810// version of RtlUTF8ToUnicodeN for Vista and below
811NTSTATUS utf8_to_utf16(WCHAR* dest, ULONG dest_max, ULONG* dest_len, char* src, ULONG src_len) {
813 uint8_t* in = (uint8_t*)src;
815 ULONG needed = 0, left = dest_max / sizeof(uint16_t);
816
817 for (ULONG i = 0; i < src_len; i++) {
818 uint32_t cp;
819
820 if (!(in[i] & 0x80))
821 cp = in[i];
822 else if ((in[i] & 0xe0) == 0xc0) {
823 if (i == src_len - 1 || (in[i+1] & 0xc0) != 0x80) {
824 cp = 0xfffd;
826 } else {
827 cp = ((in[i] & 0x1f) << 6) | (in[i+1] & 0x3f);
828 i++;
829 }
830 } else if ((in[i] & 0xf0) == 0xe0) {
831 if (i >= src_len - 2 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80) {
832 cp = 0xfffd;
834 } else {
835 cp = ((in[i] & 0xf) << 12) | ((in[i+1] & 0x3f) << 6) | (in[i+2] & 0x3f);
836 i += 2;
837 }
838 } else if ((in[i] & 0xf8) == 0xf0) {
839 if (i >= src_len - 3 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80 || (in[i+3] & 0xc0) != 0x80) {
840 cp = 0xfffd;
842 } else {
843 cp = ((in[i] & 0x7) << 18) | ((in[i+1] & 0x3f) << 12) | ((in[i+2] & 0x3f) << 6) | (in[i+3] & 0x3f);
844 i += 3;
845 }
846 } else {
847 cp = 0xfffd;
849 }
850
851 if (cp > 0x10ffff) {
852 cp = 0xfffd;
854 }
855
856 if (dest) {
857 if (cp <= 0xffff) {
858 if (left < 1)
860
861 *out = (uint16_t)cp;
862 out++;
863
864 left--;
865 } else {
866 if (left < 2)
868
869 cp -= 0x10000;
870
871 *out = 0xd800 | ((cp & 0xffc00) >> 10);
872 out++;
873
874 *out = 0xdc00 | (cp & 0x3ff);
875 out++;
876
877 left -= 2;
878 }
879 }
880
881 if (cp <= 0xffff)
882 needed += sizeof(uint16_t);
883 else
884 needed += 2 * sizeof(uint16_t);
885 }
886
887 if (dest_len)
888 *dest_len = needed;
889
890 return Status;
891}
892
893// version of RtlUnicodeToUTF8N for Vista and below
894NTSTATUS utf16_to_utf8(char* dest, ULONG dest_max, ULONG* dest_len, WCHAR* src, ULONG src_len) {
896 uint16_t* in = (uint16_t*)src;
897 uint8_t* out = (uint8_t*)dest;
898 ULONG in_len = src_len / sizeof(uint16_t);
899 ULONG needed = 0, left = dest_max;
900
901 for (ULONG i = 0; i < in_len; i++) {
902 uint32_t cp = *in;
903 in++;
904
905 if ((cp & 0xfc00) == 0xd800) {
906 if (i == in_len - 1 || (*in & 0xfc00) != 0xdc00) {
907 cp = 0xfffd;
909 } else {
910 cp = (cp & 0x3ff) << 10;
911 cp |= *in & 0x3ff;
912 cp += 0x10000;
913
914 in++;
915 i++;
916 }
917 } else if ((cp & 0xfc00) == 0xdc00) {
918 cp = 0xfffd;
920 }
921
922 if (cp > 0x10ffff) {
923 cp = 0xfffd;
925 }
926
927 if (dest) {
928 if (cp < 0x80) {
929 if (left < 1)
931
932 *out = (uint8_t)cp;
933 out++;
934
935 left--;
936 } else if (cp < 0x800) {
937 if (left < 2)
939
940 *out = 0xc0 | ((cp & 0x7c0) >> 6);
941 out++;
942
943 *out = 0x80 | (cp & 0x3f);
944 out++;
945
946 left -= 2;
947 } else if (cp < 0x10000) {
948 if (left < 3)
950
951 *out = 0xe0 | ((cp & 0xf000) >> 12);
952 out++;
953
954 *out = 0x80 | ((cp & 0xfc0) >> 6);
955 out++;
956
957 *out = 0x80 | (cp & 0x3f);
958 out++;
959
960 left -= 3;
961 } else {
962 if (left < 4)
964
965 *out = 0xf0 | ((cp & 0x1c0000) >> 18);
966 out++;
967
968 *out = 0x80 | ((cp & 0x3f000) >> 12);
969 out++;
970
971 *out = 0x80 | ((cp & 0xfc0) >> 6);
972 out++;
973
974 *out = 0x80 | (cp & 0x3f);
975 out++;
976
977 left -= 4;
978 }
979 }
980
981 if (cp < 0x80)
982 needed++;
983 else if (cp < 0x800)
984 needed += 2;
985 else if (cp < 0x10000)
986 needed += 3;
987 else
988 needed += 4;
989 }
990
991 if (dest_len)
992 *dest_len = needed;
993
994 return Status;
995}
996
999static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
1002 ULONG BytesCopied = 0;
1003 device_extension* Vcb = DeviceObject->DeviceExtension;
1004 bool top_level;
1005
1007
1008 TRACE("query volume information\n");
1009 top_level = is_top_level(Irp);
1010
1011 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1013 goto end;
1014 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1016 goto end;
1017 }
1018
1020
1022
1023 switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
1025 {
1026 FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1027 bool overflow = false;
1028#ifndef __REACTOS__
1029 static const WCHAR ntfs[] = L"NTFS";
1030#endif
1031 static const WCHAR btrfs[] = L"Btrfs";
1032 const WCHAR* fs_name;
1033 ULONG fs_name_len, orig_fs_name_len;
1034
1035#ifndef __REACTOS__
1036 if (Irp->RequestorMode == UserMode && lie_about_fs_type()) {
1037 fs_name = ntfs;
1038 orig_fs_name_len = fs_name_len = sizeof(ntfs) - sizeof(WCHAR);
1039 } else {
1040 fs_name = btrfs;
1041 orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
1042 }
1043#else
1044 fs_name = btrfs;
1045 orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
1046#endif
1047
1048 TRACE("FileFsAttributeInformation\n");
1049
1050 if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
1051 if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
1052 fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
1053 else
1054 fs_name_len = 0;
1055
1056 overflow = true;
1057 }
1058
1064 if (Vcb->readonly)
1065 data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
1066
1067 // should also be FILE_FILE_COMPRESSION when supported
1068 data->MaximumComponentNameLength = 255; // FIXME - check
1069 data->FileSystemNameLength = orig_fs_name_len;
1070 RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
1071
1072 BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
1074 break;
1075 }
1076
1078 {
1079 FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
1080
1081 TRACE("FileFsDeviceInformation\n");
1082
1084
1085 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1086 ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
1087 ExReleaseResourceLite(&Vcb->tree_lock);
1088
1089 if (Vcb->readonly)
1091 else
1092 ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
1093
1096
1097 break;
1098 }
1099
1101 {
1102 FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1103
1104 TRACE("FileFsFullSizeInformation\n");
1105
1108 ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1109 ffsi->BytesPerSector = 512;
1110
1113
1114 break;
1115 }
1116
1118 {
1119 FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
1120
1121 TRACE("FileFsObjectIdInformation\n");
1122
1123 RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
1124 RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
1125
1128
1129 break;
1130 }
1131
1133 {
1134 FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
1135
1136 TRACE("FileFsSizeInformation\n");
1137
1139 ffsi->SectorsPerAllocationUnit = Vcb->superblock.sector_size / 512;
1140 ffsi->BytesPerSector = 512;
1141
1144
1145 break;
1146 }
1147
1149 {
1150 FILE_FS_VOLUME_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1152 bool overflow = false;
1153 ULONG label_len, orig_label_len;
1154
1155 TRACE("FileFsVolumeInformation\n");
1156 TRACE("max length = %lu\n", IrpSp->Parameters.QueryVolume.Length);
1157
1158 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
1159
1160 Status = utf8_to_utf16(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1161 if (!NT_SUCCESS(Status)) {
1162 ERR("utf8_to_utf16 returned %08lx\n", Status);
1163 ExReleaseResourceLite(&Vcb->tree_lock);
1164 break;
1165 }
1166
1167 orig_label_len = label_len;
1168
1169 if (IrpSp->Parameters.QueryVolume.Length < offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + label_len) {
1171 label_len = IrpSp->Parameters.QueryVolume.Length - offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel);
1172 else
1173 label_len = 0;
1174
1175 overflow = true;
1176 }
1177
1178 TRACE("label_len = %lu\n", label_len);
1179
1181
1182 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];
1183 ffvi.VolumeLabelLength = orig_label_len;
1184
1186
1187 if (label_len > 0) {
1188 ULONG bytecount;
1189
1190 Status = utf8_to_utf16(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
1192 ERR("utf8_to_utf16 returned %08lx\n", Status);
1193 ExReleaseResourceLite(&Vcb->tree_lock);
1194 break;
1195 }
1196
1197 TRACE("label = %.*S\n", (int)(label_len / sizeof(WCHAR)), data->VolumeLabel);
1198 }
1199
1200 ExReleaseResourceLite(&Vcb->tree_lock);
1201
1202 BytesCopied = offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + label_len;
1204 break;
1205 }
1206
1207#ifndef __REACTOS__
1208#ifdef _MSC_VER // not in mingw yet
1209 case FileFsSectorSizeInformation:
1210 {
1211 FILE_FS_SECTOR_SIZE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
1212
1213 data->LogicalBytesPerSector = Vcb->superblock.sector_size;
1214 data->PhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1215 data->PhysicalBytesPerSectorForPerformance = Vcb->superblock.sector_size;
1216 data->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
1217 data->ByteOffsetForSectorAlignment = 0;
1218 data->ByteOffsetForPartitionAlignment = 0;
1219
1220 data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
1221
1222 if (Vcb->trim && !Vcb->options.no_trim)
1223 data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
1224
1225 BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
1227
1228 break;
1229 }
1230#endif
1231#endif /* __REACTOS__ */
1232
1233 default:
1235 WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
1236 break;
1237 }
1238
1240 Irp->IoStatus.Information = 0;
1241 else
1242 Irp->IoStatus.Information = BytesCopied;
1243
1244end:
1245 Irp->IoStatus.Status = Status;
1246
1248
1249 if (top_level)
1251
1252 TRACE("query volume information returning %08lx\n", Status);
1253
1255
1256 return Status;
1257}
1258
1259_Function_class_(IO_COMPLETION_ROUTINE)
1260static NTSTATUS __stdcall read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
1261 read_context* context = conptr;
1262
1264
1265 context->iosb = Irp->IoStatus;
1266 KeSetEvent(&context->Event, 0, false);
1267
1269}
1270
1272 _Out_ root** rootptr, _In_ bool no_tree, _In_ uint64_t offset, _In_opt_ PIRP Irp) {
1274 root* r;
1275 ROOT_ITEM* ri;
1277
1279 if (!r) {
1280 ERR("out of memory\n");
1282 }
1283
1285 if (!r->nonpaged) {
1286 ERR("out of memory\n");
1287 ExFreePool(r);
1289 }
1290
1292 if (!ri) {
1293 ERR("out of memory\n");
1294
1295 ExFreePool(r->nonpaged);
1296 ExFreePool(r);
1298 }
1299
1300 r->id = id;
1301 r->treeholder.address = 0;
1302 r->treeholder.generation = Vcb->superblock.generation;
1303 r->treeholder.tree = NULL;
1304 r->lastinode = 0;
1305 r->dirty = false;
1306 r->received = false;
1307 r->reserved = NULL;
1308 r->parent = 0;
1309 r->send_ops = 0;
1310 RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
1311 r->root_item.num_references = 1;
1312 r->fcbs_version = 0;
1313 r->checked_for_orphans = true;
1314 r->dropped = false;
1315 InitializeListHead(&r->fcbs);
1316 RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
1317
1318 RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
1319
1320 // We ask here for a traverse_ptr to the item we're inserting, so we can
1321 // copy some of the tree's variables
1322
1323 Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
1324 if (!NT_SUCCESS(Status)) {
1325 ERR("insert_tree_item returned %08lx\n", Status);
1326 ExFreePool(ri);
1327 ExFreePool(r->nonpaged);
1328 ExFreePool(r);
1329 return Status;
1330 }
1331
1332 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
1333
1334 InsertTailList(&Vcb->roots, &r->list_entry);
1335
1336 if (!no_tree) {
1338 if (!t) {
1339 ERR("out of memory\n");
1340
1342
1343 ExFreePool(r->nonpaged);
1344 ExFreePool(r);
1345 ExFreePool(ri);
1347 }
1348
1349 t->nonpaged = NULL;
1350
1351 t->is_unique = true;
1352 t->uniqueness_determined = true;
1353 t->buf = NULL;
1354
1355 r->treeholder.tree = t;
1356
1357 RtlZeroMemory(&t->header, sizeof(tree_header));
1358 t->header.fs_uuid = tp.tree->header.fs_uuid;
1359 t->header.address = 0;
1360 t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
1361 t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
1362 t->header.generation = Vcb->superblock.generation;
1363 t->header.tree_id = id;
1364 t->header.num_items = 0;
1365 t->header.level = 0;
1366
1367 t->has_address = false;
1368 t->size = 0;
1369 t->Vcb = Vcb;
1370 t->parent = NULL;
1371 t->paritem = NULL;
1372 t->root = r;
1373
1374 InitializeListHead(&t->itemlist);
1375
1376 t->new_address = 0;
1377 t->has_new_address = false;
1378 t->updated_extents = false;
1379
1380 InsertTailList(&Vcb->trees, &t->list_entry);
1381 t->list_entry_hash.Flink = NULL;
1382
1383 t->write = true;
1384 Vcb->need_write = true;
1385 }
1386
1387 *rootptr = r;
1388
1389 return STATUS_SUCCESS;
1390}
1391
1393 ULONG utf8len;
1395 ULONG vollen, i;
1396
1397 TRACE("label = %.*S\n", (int)(ffli->VolumeLabelLength / sizeof(WCHAR)), ffli->VolumeLabel);
1398
1399 vollen = ffli->VolumeLabelLength;
1400
1401 for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
1402 if (ffli->VolumeLabel[i] == 0) {
1403 vollen = i * sizeof(WCHAR);
1404 break;
1405 } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
1407 goto end;
1408 }
1409 }
1410
1411 if (vollen == 0) {
1412 utf8len = 0;
1413 } else {
1414 Status = utf16_to_utf8(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
1415 if (!NT_SUCCESS(Status))
1416 goto end;
1417
1418 if (utf8len > MAX_LABEL_SIZE) {
1420 goto end;
1421 }
1422 }
1423
1424 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
1425
1426 if (utf8len > 0) {
1427 Status = utf16_to_utf8((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
1428 if (!NT_SUCCESS(Status))
1429 goto release;
1430 } else
1432
1433 if (utf8len < MAX_LABEL_SIZE)
1434 RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
1435
1436 Vcb->need_write = true;
1437
1438release:
1439 ExReleaseResourceLite(&Vcb->tree_lock);
1440
1441end:
1442 TRACE("returning %08lx\n", Status);
1443
1444 return Status;
1445}
1446
1449static NTSTATUS __stdcall drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
1451 device_extension* Vcb = DeviceObject->DeviceExtension;
1453 bool top_level;
1454
1456
1457 TRACE("set volume information\n");
1458
1459 top_level = is_top_level(Irp);
1460
1461 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
1463 goto end;
1464 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
1466 goto end;
1467 }
1468
1470
1471 if (Vcb->readonly) {
1473 goto end;
1474 }
1475
1476 if (Vcb->removing || Vcb->locked) {
1478 goto end;
1479 }
1480
1481 switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
1483 FIXME("STUB: FileFsControlInformation\n");
1484 break;
1485
1487 TRACE("FileFsLabelInformation\n");
1488
1489 Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
1490 break;
1491
1493 FIXME("STUB: FileFsObjectIdInformation\n");
1494 break;
1495
1496 default:
1497 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
1498 break;
1499 }
1500
1501end:
1502 Irp->IoStatus.Status = Status;
1503 Irp->IoStatus.Information = 0;
1504
1505 TRACE("returning %08lx\n", Status);
1506
1508
1509 if (top_level)
1511
1513
1514 return Status;
1515}
1516
1520 ULONG reqlen;
1521 USHORT name_offset;
1522 fcb* fcb = fileref->fcb;
1523
1524 fn.Length = fn.MaximumLength = 0;
1525 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
1527 ERR("fileref_get_filename returned %08lx\n", Status);
1528 return;
1529 }
1530
1531 if (reqlen > 0xffff) {
1532 WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
1533 return;
1534 }
1535
1536 fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
1537 if (!fn.Buffer) {
1538 ERR("out of memory\n");
1539 return;
1540 }
1541
1542 fn.MaximumLength = (USHORT)reqlen;
1543 fn.Length = 0;
1544
1545 Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
1546 if (!NT_SUCCESS(Status)) {
1547 ERR("fileref_get_filename returned %08lx\n", Status);
1548 ExFreePool(fn.Buffer);
1549 return;
1550 }
1551
1552 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
1553 (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1554 ExFreePool(fn.Buffer);
1555}
1556
1558 fcb* fcb = fileref->fcb;
1559 LIST_ENTRY* le;
1561
1562 // no point looking for hardlinks if st_nlink == 1
1563 if (fileref->fcb->inode_item.st_nlink == 1) {
1564 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
1565 send_notification_fileref(fileref, filter_match, action, stream);
1566 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
1567 return;
1568 }
1569
1570 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
1571
1572 le = fcb->hardlinks.Flink;
1573 while (le != &fcb->hardlinks) {
1575 file_ref* parfr;
1576
1578
1579 if (!NT_SUCCESS(Status))
1580 ERR("open_fileref_by_inode returned %08lx\n", Status);
1581 else if (!parfr->deleted) {
1583 ULONG pathlen;
1584
1585 fn.Length = fn.MaximumLength = 0;
1586 Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
1588 ERR("fileref_get_filename returned %08lx\n", Status);
1589 free_fileref(parfr);
1590 break;
1591 }
1592
1593 if (parfr != fcb->Vcb->root_fileref)
1594 pathlen += sizeof(WCHAR);
1595
1596 if (pathlen + hl->name.Length > 0xffff) {
1597 WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
1598 free_fileref(parfr);
1599 break;
1600 }
1601
1602 fn.MaximumLength = (USHORT)(pathlen + hl->name.Length);
1603 fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
1604 if (!fn.Buffer) {
1605 ERR("out of memory\n");
1606 free_fileref(parfr);
1607 break;
1608 }
1609
1610 Status = fileref_get_filename(parfr, &fn, NULL, NULL);
1611 if (!NT_SUCCESS(Status)) {
1612 ERR("fileref_get_filename returned %08lx\n", Status);
1613 free_fileref(parfr);
1614 ExFreePool(fn.Buffer);
1615 break;
1616 }
1617
1618 if (parfr != fcb->Vcb->root_fileref) {
1619 fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
1620 fn.Length += sizeof(WCHAR);
1621 }
1622
1623 RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
1624 fn.Length += hl->name.Length;
1625
1626 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, (USHORT)pathlen,
1627 (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
1628
1629 ExFreePool(fn.Buffer);
1630
1631 free_fileref(parfr);
1632 }
1633
1634 le = le->Flink;
1635 }
1636
1637 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
1638}
1639
1640typedef struct {
1647
1648_Function_class_(IO_WORKITEM_ROUTINE)
1649static void __stdcall notification_work_item(PDEVICE_OBJECT DeviceObject, PVOID con) {
1650 notification_fcb* nf = con;
1651
1653
1654 ExAcquireResourceSharedLite(&nf->fileref->fcb->Vcb->tree_lock, TRUE); // protect us from fileref being reaped
1655
1657
1658 free_fileref(nf->fileref);
1659
1660 ExReleaseResourceLite(&nf->fileref->fcb->Vcb->tree_lock);
1661
1663
1664 ExFreePool(nf);
1665}
1666
1668 notification_fcb* nf;
1669 PIO_WORKITEM work_item;
1670
1672 if (!nf) {
1673 ERR("out of memory\n");
1674 return;
1675 }
1676
1677 work_item = IoAllocateWorkItem(master_devobj);
1678 if (!work_item) {
1679 ERR("out of memory\n");
1680 ExFreePool(nf);
1681 return;
1682 }
1683
1684 InterlockedIncrement(&fileref->refcount);
1685
1686 nf->fileref = fileref;
1687 nf->filter_match = filter_match;
1688 nf->action = action;
1689 nf->stream = stream;
1690 nf->work_item = work_item;
1691
1692 IoQueueWorkItem(work_item, notification_work_item, DelayedWorkQueue, nf);
1693}
1694
1696 if (!fcb->dirty) {
1697#ifdef DEBUG_FCB_REFCOUNTS
1698 LONG rc;
1699#endif
1700 fcb->dirty = true;
1701
1702#ifdef DEBUG_FCB_REFCOUNTS
1704 WARN("fcb %p: refcount now %i\n", fcb, rc);
1705#else
1707#endif
1708
1709 ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, true);
1710 InsertTailList(&fcb->Vcb->dirty_fcbs, &fcb->list_entry_dirty);
1711 ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
1712 }
1713
1714 fcb->Vcb->need_write = true;
1715}
1716
1718 if (!fileref->dirty) {
1719 fileref->dirty = true;
1721
1722 ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, true);
1723 InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &fileref->list_entry_dirty);
1724 ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
1725 }
1726
1727 fileref->fcb->Vcb->need_write = true;
1728}
1729
1730#ifdef DEBUG_FCB_REFCOUNTS
1731void _free_fcb(_Inout_ fcb* fcb, _In_ const char* func) {
1733#else
1736#endif
1737
1738#ifdef DEBUG_FCB_REFCOUNTS
1739 ERR("fcb %p (%s): refcount now %i (subvol %I64x, inode %I64x)\n", fcb, func, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
1740#endif
1741}
1742
1744 uint8_t c = fcb->hash >> 24;
1745
1746 if (fcb->subvol && fcb->subvol->fcbs_ptrs[c] == &fcb->list_entry) {
1747 if (fcb->list_entry.Flink != &fcb->subvol->fcbs && (CONTAINING_RECORD(fcb->list_entry.Flink, struct _fcb, list_entry)->hash >> 24) == c)
1748 fcb->subvol->fcbs_ptrs[c] = fcb->list_entry.Flink;
1749 else
1750 fcb->subvol->fcbs_ptrs[c] = NULL;
1751 }
1752
1753 if (fcb->list_entry.Flink) {
1755
1756 if (fcb->subvol && fcb->subvol->dropped && IsListEmpty(&fcb->subvol->fcbs)) {
1757 ExDeleteResourceLite(&fcb->subvol->nonpaged->load_tree_lock);
1758 ExFreePool(fcb->subvol->nonpaged);
1760 }
1761 }
1762
1765
1766 ExDeleteResourceLite(&fcb->nonpaged->resource);
1767 ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
1768 ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
1769
1770 ExFreeToNPagedLookasideList(&fcb->Vcb->fcb_np_lookaside, fcb->nonpaged);
1771
1772 if (fcb->sd)
1773 ExFreePool(fcb->sd);
1774
1775 if (fcb->adsxattr.Buffer)
1777
1780
1781 if (fcb->ea_xattr.Buffer)
1783
1784 if (fcb->adsdata.Buffer)
1786
1787 while (!IsListEmpty(&fcb->extents)) {
1790
1791 if (ext->csum)
1792 ExFreePool(ext->csum);
1793
1794 ExFreePool(ext);
1795 }
1796
1797 while (!IsListEmpty(&fcb->hardlinks)) {
1800
1801 if (hl->name.Buffer)
1802 ExFreePool(hl->name.Buffer);
1803
1804 if (hl->utf8.Buffer)
1805 ExFreePool(hl->utf8.Buffer);
1806
1807 ExFreePool(hl);
1808 }
1809
1810 while (!IsListEmpty(&fcb->xattrs)) {
1812
1813 ExFreePool(xa);
1814 }
1815
1816 while (!IsListEmpty(&fcb->dir_children_index)) {
1818 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
1819
1820 ExFreePool(dc->utf8.Buffer);
1821 ExFreePool(dc->name.Buffer);
1822 ExFreePool(dc->name_uc.Buffer);
1823 ExFreePool(dc);
1824 }
1825
1826 if (fcb->hash_ptrs)
1828
1829 if (fcb->hash_ptrs_uc)
1831
1834
1835 if (fcb->pool_type == NonPagedPool)
1836 ExFreePool(fcb);
1837 else
1838 ExFreeToPagedLookasideList(&fcb->Vcb->fcb_lookaside, fcb);
1839}
1840
1842 LIST_ENTRY* le;
1843
1844 le = Vcb->all_fcbs.Flink;
1845 while (le != &Vcb->all_fcbs) {
1846 fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all);
1847 LIST_ENTRY* le2 = le->Flink;
1848
1849 if (fcb->refcount == 0)
1850 reap_fcb(fcb);
1851
1852 le = le2;
1853 }
1854}
1855
1857#if defined(_DEBUG) || defined(DEBUG_FCB_REFCOUNTS)
1858 LONG rc = InterlockedDecrement(&fr->refcount);
1859
1860#ifdef DEBUG_FCB_REFCOUNTS
1861 ERR("fileref %p: refcount now %i\n", fr, rc);
1862#endif
1863
1864#ifdef _DEBUG
1865 if (rc < 0) {
1866 ERR("fileref %p: refcount now %li\n", fr, rc);
1867 int3;
1868 }
1869#endif
1870#else
1871 InterlockedDecrement(&fr->refcount);
1872#endif
1873}
1874
1876 // FIXME - do we need a file_ref lock?
1877
1878 // FIXME - do delete if needed
1879
1880 // FIXME - throw error if children not empty
1881
1882 if (fr->fcb->fileref == fr)
1883 fr->fcb->fileref = NULL;
1884
1885 if (fr->dc) {
1886 if (fr->fcb->ads)
1887 fr->dc->size = fr->fcb->adsdata.Length;
1888
1889 fr->dc->fileref = NULL;
1890 }
1891
1892 if (fr->list_entry.Flink)
1894
1895 if (fr->parent)
1896 free_fileref(fr->parent);
1897
1898 free_fcb(fr->fcb);
1899
1900 if (fr->oldutf8.Buffer)
1902
1903 ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
1904}
1905
1907 LIST_ENTRY* le;
1908
1909 // FIXME - recursion is a bad idea in kernel mode
1910
1911 le = fr->children.Flink;
1912 while (le != &fr->children) {
1914 LIST_ENTRY* le2 = le->Flink;
1915
1917
1918 le = le2;
1919 }
1920
1921 if (fr->refcount == 0)
1922 reap_fileref(Vcb, fr);
1923}
1924
1926 fcb* fcb;
1927 ccb* ccb;
1928 file_ref* fileref = NULL;
1929 LONG open_files;
1930
1931 UNUSED(Irp);
1932
1933 TRACE("FileObject = %p\n", FileObject);
1934
1935 fcb = FileObject->FsContext;
1936 if (!fcb) {
1937 TRACE("FCB was NULL, returning success\n");
1938 return STATUS_SUCCESS;
1939 }
1940
1941 open_files = InterlockedDecrement(&fcb->Vcb->open_files);
1942
1943 ccb = FileObject->FsContext2;
1944
1945 TRACE("close called for fcb %p)\n", fcb);
1946
1947 // FIXME - make sure notification gets sent if file is being deleted
1948
1949 if (ccb) {
1950 if (ccb->query_string.Buffer)
1952
1953 if (ccb->filename.Buffer)
1955
1956 // FIXME - use refcounts for fileref
1957 fileref = ccb->fileref;
1958
1959 if (fcb->Vcb->running_sends > 0) {
1960 bool send_cancelled = false;
1961
1962 ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1963
1964 if (ccb->send) {
1965 ccb->send->cancelling = true;
1966 send_cancelled = true;
1967 KeSetEvent(&ccb->send->cleared_event, 0, false);
1968 }
1969
1970 ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1971
1972 if (send_cancelled) {
1973 while (ccb->send) {
1974 ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, true);
1975 ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
1976 }
1977 }
1978 }
1979
1980 ExFreePool(ccb);
1981 }
1982
1984
1985 if (open_files == 0 && fcb->Vcb->removing) {
1986 uninit(fcb->Vcb);
1987 return STATUS_SUCCESS;
1988 }
1989
1990 if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
1991 return STATUS_SUCCESS;
1992
1993 if (fileref)
1994 free_fileref(fileref);
1995 else
1996 free_fcb(fcb);
1997
1998 return STATUS_SUCCESS;
1999}
2000
2002 uint64_t i;
2003 KIRQL irql;
2005 LIST_ENTRY* le;
2007
2008 if (!Vcb->removing) {
2009 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
2010 Vcb->removing = true;
2011 ExReleaseResourceLite(&Vcb->tree_lock);
2012 }
2013
2014 if (Vcb->vde && Vcb->vde->mounted_device == Vcb->devobj)
2015 Vcb->vde->mounted_device = NULL;
2016
2018 Vcb->Vpb->Flags &= ~VPB_MOUNTED;
2019 Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
2020 Vcb->Vpb->DeviceObject = NULL;
2022
2023 // FIXME - needs global_loading_lock to be held
2024 if (Vcb->list_entry.Flink)
2025 RemoveEntryList(&Vcb->list_entry);
2026
2027 if (Vcb->balance.thread) {
2028 Vcb->balance.paused = false;
2029 Vcb->balance.stopping = true;
2030 KeSetEvent(&Vcb->balance.event, 0, false);
2031 KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, false, NULL);
2032 }
2033
2034 if (Vcb->scrub.thread) {
2035 Vcb->scrub.paused = false;
2036 Vcb->scrub.stopping = true;
2037 KeSetEvent(&Vcb->scrub.event, 0, false);
2038 KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, false, NULL);
2039 }
2040
2041 if (Vcb->running_sends != 0) {
2042 bool send_cancelled = false;
2043
2044 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
2045
2046 le = Vcb->send_ops.Flink;
2047 while (le != &Vcb->send_ops) {
2049
2050 if (!send->cancelling) {
2051 send->cancelling = true;
2052 send_cancelled = true;
2053 send->ccb = NULL;
2054 KeSetEvent(&send->cleared_event, 0, false);
2055 }
2056
2057 le = le->Flink;
2058 }
2059
2060 ExReleaseResourceLite(&Vcb->send_load_lock);
2061
2062 if (send_cancelled) {
2063 while (Vcb->running_sends != 0) {
2064 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
2065 ExReleaseResourceLite(&Vcb->send_load_lock);
2066 }
2067 }
2068 }
2069
2070 Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
2072 WARN("registry_mark_volume_unmounted returned %08lx\n", Status);
2073
2074 for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2075 Vcb->calcthreads.threads[i].quit = true;
2076 }
2077
2078 KeSetEvent(&Vcb->calcthreads.event, 0, false);
2079
2080 for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
2081 KeWaitForSingleObject(&Vcb->calcthreads.threads[i].finished, Executive, KernelMode, false, NULL);
2082
2083 ZwClose(Vcb->calcthreads.threads[i].handle);
2084 }
2085
2086 ExFreePool(Vcb->calcthreads.threads);
2087
2088 time.QuadPart = 0;
2089 KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
2090 KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, false, NULL);
2091
2092 reap_fcb(Vcb->volume_fcb);
2093 reap_fcb(Vcb->dummy_fcb);
2094
2095 if (Vcb->root_file)
2096 ObDereferenceObject(Vcb->root_file);
2097
2098 le = Vcb->chunks.Flink;
2099 while (le != &Vcb->chunks) {
2101
2102 if (c->cache) {
2103 reap_fcb(c->cache);
2104 c->cache = NULL;
2105 }
2106
2107 le = le->Flink;
2108 }
2109
2110 while (!IsListEmpty(&Vcb->all_fcbs)) {
2111 fcb* fcb = CONTAINING_RECORD(Vcb->all_fcbs.Flink, struct _fcb, list_entry_all);
2112
2113 reap_fcb(fcb);
2114 }
2115
2116 while (!IsListEmpty(&Vcb->sys_chunks)) {
2118
2119 if (sc->data)
2120 ExFreePool(sc->data);
2121
2122 ExFreePool(sc);
2123 }
2124
2125 while (!IsListEmpty(&Vcb->roots)) {
2127
2128 ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
2129 ExFreePool(r->nonpaged);
2130 ExFreePool(r);
2131 }
2132
2133 while (!IsListEmpty(&Vcb->chunks)) {
2135
2136 while (!IsListEmpty(&c->space)) {
2137 LIST_ENTRY* le2 = RemoveHeadList(&c->space);
2139
2140 ExFreePool(s);
2141 }
2142
2143 while (!IsListEmpty(&c->deleting)) {
2144 LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
2146
2147 ExFreePool(s);
2148 }
2149
2150 if (c->devices)
2151 ExFreePool(c->devices);
2152
2153 if (c->cache)
2154 reap_fcb(c->cache);
2155
2156 ExDeleteResourceLite(&c->range_locks_lock);
2157 ExDeleteResourceLite(&c->partial_stripes_lock);
2158 ExDeleteResourceLite(&c->lock);
2159 ExDeleteResourceLite(&c->changed_extents_lock);
2160
2161 ExFreePool(c->chunk_item);
2162 ExFreePool(c);
2163 }
2164
2165 while (!IsListEmpty(&Vcb->devices)) {
2167
2168 while (!IsListEmpty(&dev->space)) {
2169 LIST_ENTRY* le2 = RemoveHeadList(&dev->space);
2171
2172 ExFreePool(s);
2173 }
2174
2175 ExFreePool(dev);
2176 }
2177
2178 ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, true);
2179 while (!IsListEmpty(&Vcb->scrub.errors)) {
2181
2182 ExFreePool(err);
2183 }
2184 ExReleaseResourceLite(&Vcb->scrub.stats_lock);
2185
2186 ExDeleteResourceLite(&Vcb->fcb_lock);
2187 ExDeleteResourceLite(&Vcb->fileref_lock);
2188 ExDeleteResourceLite(&Vcb->load_lock);
2189 ExDeleteResourceLite(&Vcb->tree_lock);
2190 ExDeleteResourceLite(&Vcb->chunk_lock);
2191 ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
2192 ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
2193 ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
2194 ExDeleteResourceLite(&Vcb->scrub.stats_lock);
2195 ExDeleteResourceLite(&Vcb->send_load_lock);
2196
2197 ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
2198 ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
2199 ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
2200 ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
2201 ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
2202 ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
2203 ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
2204 ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
2205
2206 ZwClose(Vcb->flush_thread_handle);
2207
2208 if (Vcb->devobj->AttachedDevice)
2209 IoDetachDevice(Vcb->devobj);
2210
2211 IoDeleteDevice(Vcb->devobj);
2212}
2213
2216 LIST_ENTRY* le;
2217
2218 // excise extents
2219
2220 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
2221 Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
2222 if (!NT_SUCCESS(Status)) {
2223 ERR("excise_extents returned %08lx\n", Status);
2224 return Status;
2225 }
2226 }
2227
2228 fileref->fcb->Header.AllocationSize.QuadPart = 0;
2229 fileref->fcb->Header.FileSize.QuadPart = 0;
2230 fileref->fcb->Header.ValidDataLength.QuadPart = 0;
2231
2232 if (FileObject) {
2233 CC_FILE_SIZES ccfs;
2234
2235 ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
2236 ccfs.FileSize = fileref->fcb->Header.FileSize;
2237 ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
2238
2240
2241 _SEH2_TRY {
2242 CcSetFileSizes(FileObject, &ccfs);
2245 } _SEH2_END;
2246
2247 if (!NT_SUCCESS(Status)) {
2248 ERR("CcSetFileSizes threw exception %08lx\n", Status);
2249 return Status;
2250 }
2251 }
2252
2253 fileref->fcb->deleted = true;
2254
2255 le = fileref->children.Flink;
2256 while (le != &fileref->children) {
2258
2259 if (fr2->fcb->ads) {
2260 fr2->fcb->deleted = true;
2261 mark_fcb_dirty(fr2->fcb);
2262 }
2263
2264 le = le->Flink;
2265 }
2266
2267 return STATUS_SUCCESS;
2268}
2269
2271 LARGE_INTEGER newlength, time;
2274 ULONG utf8len = 0;
2275
2278
2279 ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, true);
2280
2281 if (fileref->deleted) {
2282 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2283 return STATUS_SUCCESS;
2284 }
2285
2286 if (fileref->fcb->subvol->send_ops > 0) {
2287 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2288 return STATUS_ACCESS_DENIED;
2289 }
2290
2291 fileref->deleted = true;
2292 mark_fileref_dirty(fileref);
2293
2294 // delete INODE_ITEM (0x1)
2295
2296 TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
2297
2298 if (!fileref->fcb->ads) {
2299 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
2300 LIST_ENTRY* le;
2301
2302 mark_fcb_dirty(fileref->fcb);
2303
2304 fileref->fcb->inode_item_changed = true;
2305
2306 if (fileref->fcb->inode_item.st_nlink > 1 || make_orphan) {
2307 fileref->fcb->inode_item.st_nlink--;
2308 fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2309 fileref->fcb->inode_item.sequence++;
2310 fileref->fcb->inode_item.st_ctime = now;
2311 } else {
2313 if (!NT_SUCCESS(Status)) {
2314 ERR("delete_fileref_fcb returned %08lx\n", Status);
2315 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2316 return Status;
2317 }
2318 }
2319
2320 if (fileref->dc) {
2321 le = fileref->fcb->hardlinks.Flink;
2322 while (le != &fileref->fcb->hardlinks) {
2324
2325 if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
2327
2328 if (hl->name.Buffer)
2329 ExFreePool(hl->name.Buffer);
2330
2331 if (hl->utf8.Buffer)
2332 ExFreePool(hl->utf8.Buffer);
2333
2334 ExFreePool(hl);
2335 break;
2336 }
2337
2338 le = le->Flink;
2339 }
2340 }
2341 } else if (fileref->fcb->subvol->parent == fileref->parent->fcb->subvol->id) { // valid subvolume
2342 if (fileref->fcb->subvol->root_item.num_references > 1) {
2343 fileref->fcb->subvol->root_item.num_references--;
2344
2345 mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
2346 } else {
2347 LIST_ENTRY* le;
2348
2349 // FIXME - we need a lock here
2350
2351 RemoveEntryList(&fileref->fcb->subvol->list_entry);
2352
2353 InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
2354
2355 le = fileref->children.Flink;
2356 while (le != &fileref->children) {
2358
2359 if (fr2->fcb->ads) {
2360 fr2->fcb->deleted = true;
2361 mark_fcb_dirty(fr2->fcb);
2362 }
2363
2364 le = le->Flink;
2365 }
2366 }
2367 }
2368 } else {
2369 fileref->fcb->deleted = true;
2370 mark_fcb_dirty(fileref->fcb);
2371 }
2372
2373 // remove dir_child from parent
2374
2375 if (fileref->dc) {
2376 TRACE("delete file %.*S\n", (int)(fileref->dc->name.Length / sizeof(WCHAR)), fileref->dc->name.Buffer);
2377
2378 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
2379 RemoveEntryList(&fileref->dc->list_entry_index);
2380
2381 if (!fileref->fcb->ads)
2382 remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
2383
2384 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
2385
2386 if (!fileref->oldutf8.Buffer)
2387 fileref->oldutf8 = fileref->dc->utf8;
2388 else
2389 ExFreePool(fileref->dc->utf8.Buffer);
2390
2391 utf8len = fileref->dc->utf8.Length;
2392
2393 fileref->oldindex = fileref->dc->index;
2394
2395 ExFreePool(fileref->dc->name.Buffer);
2396 ExFreePool(fileref->dc->name_uc.Buffer);
2397 ExFreePool(fileref->dc);
2398
2399 fileref->dc = NULL;
2400 }
2401
2402 // update INODE_ITEM of parent
2403
2404 ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, true);
2405
2406 fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
2407 fileref->parent->fcb->inode_item.sequence++;
2408 fileref->parent->fcb->inode_item.st_ctime = now;
2409
2410 if (!fileref->fcb->ads) {
2411 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2412 fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
2413 TRACE("fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
2414 fileref->parent->fcb->inode_item.st_mtime = now;
2415 }
2416
2417 fileref->parent->fcb->inode_item_changed = true;
2418 ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
2419
2420 if (!fileref->fcb->ads && fileref->parent->dc)
2422
2423 mark_fcb_dirty(fileref->parent->fcb);
2424
2425 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
2426 fileref->fcb->subvol->root_item.ctime = now;
2427
2428 newlength.QuadPart = 0;
2429
2430 if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
2431 TRACE("CcUninitializeCacheMap failed\n");
2432
2433 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2434
2435 return STATUS_SUCCESS;
2436}
2437
2444 device_extension* Vcb = DeviceObject->DeviceExtension;
2445 fcb* fcb = FileObject->FsContext;
2446 bool top_level;
2447
2449
2450 TRACE("cleanup\n");
2451
2452 top_level = is_top_level(Irp);
2453
2454 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
2455 Irp->IoStatus.Information = 0;
2457 goto exit;
2458 } else if (DeviceObject == master_devobj) {
2459 TRACE("closing file system\n");
2461 goto exit;
2462 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
2464 goto exit;
2465 }
2466
2467 if (FileObject->Flags & FO_CLEANUP_COMPLETE) {
2468 TRACE("FileObject %p already cleaned up\n", FileObject);
2470 goto exit;
2471 }
2472
2473 if (!fcb) {
2474 ERR("fcb was NULL\n");
2476 goto exit;
2477 }
2478
2480
2481 // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
2482 // messages belonging to other devices.
2483
2484 if (FileObject && FileObject->FsContext) {
2485 ccb* ccb;
2486 file_ref* fileref;
2487 bool locked = true;
2488
2489 ccb = FileObject->FsContext2;
2490 fileref = ccb ? ccb->fileref : NULL;
2491
2492 TRACE("cleanup called for FileObject %p\n", FileObject);
2493 TRACE("fileref %p, refcount = %li, open_count = %li\n", fileref, fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
2494
2495 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
2496
2497 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
2498
2500
2502
2503 if (ccb)
2504 FsRtlNotifyCleanup(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, ccb);
2505
2506 if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
2507 fileref->delete_on_close = true;
2508
2509 if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && fcb != fcb->Vcb->dummy_fcb)
2510 fileref->delete_on_close = false;
2511
2512 if (fcb->Vcb->locked && fcb->Vcb->locked_fileobj == FileObject) {
2513 TRACE("unlocking volume\n");
2516 }
2517
2518 if (ccb && ccb->reserving) {
2519 fcb->subvol->reserved = NULL;
2520 ccb->reserving = false;
2521 // FIXME - flush all of subvol's fcbs
2522 }
2523
2524 if (fileref) {
2525 LONG oc = InterlockedDecrement(&fileref->open_count);
2526#ifdef DEBUG_FCB_REFCOUNTS
2527 ERR("fileref %p: open_count now %i\n", fileref, oc);
2528#endif
2529
2530 if (oc == 0 || (fileref->delete_on_close && fileref->posix_delete)) {
2531 if (!fcb->Vcb->removing) {
2532 if (oc == 0 && fileref->fcb->inode_item.st_nlink == 0 && fileref != fcb->Vcb->root_fileref &&
2533 fcb != fcb->Vcb->volume_fcb && !fcb->ads) { // last handle closed on POSIX-deleted file
2535
2537
2539 if (!NT_SUCCESS(Status)) {
2540 ERR("delete_fileref_fcb returned %08lx\n", Status);
2542 ExReleaseResourceLite(fileref->fcb->Header.Resource);
2543 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2544 goto exit;
2545 }
2546
2548
2549 mark_fcb_dirty(fileref->fcb);
2550 } else if (fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
2552
2554
2555 if (!fileref->fcb->ads || fileref->dc) {
2556 if (fileref->fcb->ads) {
2558 FILE_ACTION_REMOVED, &fileref->dc->name);
2559 } else
2561 }
2562
2563 ExReleaseResourceLite(fcb->Header.Resource);
2564 locked = false;
2565
2566 // fileref_lock needs to be acquired before fcb->Header.Resource
2567 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true);
2568
2569 Status = delete_fileref(fileref, FileObject, oc > 0 && fileref->posix_delete, Irp, &rollback);
2570 if (!NT_SUCCESS(Status)) {
2571 ERR("delete_fileref returned %08lx\n", Status);
2573 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2574 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2575 goto exit;
2576 }
2577
2578 ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
2579
2581 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && FileObject->SectionObjectPointer->DataSectionObject) {
2583
2584 if (locked) {
2585 ExReleaseResourceLite(fcb->Header.Resource);
2586 locked = false;
2587 }
2588
2589 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
2590
2591 if (!NT_SUCCESS(iosb.Status))
2592 ERR("CcFlushCache returned %08lx\n", iosb.Status);
2593
2594 if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
2595 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
2596 ExReleaseResourceLite(fcb->Header.PagingIoResource);
2597 }
2598
2599 CcPurgeCacheSection(FileObject->SectionObjectPointer, NULL, 0, false);
2600
2601 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x)\n",
2602 FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
2603 }
2604 }
2605
2606 if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
2608 }
2609 }
2610
2611 if (locked)
2612 ExReleaseResourceLite(fcb->Header.Resource);
2613
2614 ExReleaseResourceLite(&fcb->Vcb->tree_lock);
2615
2617 }
2618
2620
2621exit:
2622 TRACE("returning %08lx\n", Status);
2623
2624 Irp->IoStatus.Status = Status;
2625 Irp->IoStatus.Information = 0;
2626
2628
2629 if (top_level)
2631
2633
2634 return Status;
2635}
2636
2637_Success_(return)
2638bool get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ uint16_t len, _Out_ ULONG* atts) {
2639 if (len > 2 && val[0] == '0' && val[1] == 'x') {
2640 int i;
2641 ULONG dosnum = 0;
2642
2643 for (i = 2; i < len; i++) {
2644 dosnum *= 0x10;
2645
2646 if (val[i] >= '0' && val[i] <= '9')
2647 dosnum |= val[i] - '0';
2648 else if (val[i] >= 'a' && val[i] <= 'f')
2649 dosnum |= val[i] + 10 - 'a';
2650 else if (val[i] >= 'A' && val[i] <= 'F')
2651 dosnum |= val[i] + 10 - 'a';
2652 }
2653
2654 TRACE("DOSATTRIB: %08lx\n", dosnum);
2655
2656 *atts = dosnum;
2657
2658 return true;
2659 }
2660
2661 return false;
2662}
2663
2665 _In_ uint8_t type, _In_ bool dotfile, _In_ bool ignore_xa, _In_opt_ PIRP Irp) {
2666 ULONG att;
2667 char* eaval;
2668 uint16_t ealen;
2669
2670 if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (uint8_t**)&eaval, &ealen, Irp)) {
2671 ULONG dosnum = 0;
2672
2673 if (get_file_attributes_from_xattr(eaval, ealen, &dosnum)) {
2674 ExFreePool(eaval);
2675
2677 dosnum |= FILE_ATTRIBUTE_DIRECTORY;
2678 else if (type == BTRFS_TYPE_SYMLINK)
2680
2682 dosnum &= ~FILE_ATTRIBUTE_DIRECTORY;
2683
2684 if (inode == SUBVOL_ROOT_INODE) {
2685 if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2686 dosnum |= FILE_ATTRIBUTE_READONLY;
2687 else
2688 dosnum &= ~FILE_ATTRIBUTE_READONLY;
2689 }
2690
2691 return dosnum;
2692 }
2693
2694 ExFreePool(eaval);
2695 }
2696
2697 switch (type) {
2700 break;
2701
2702 case BTRFS_TYPE_SYMLINK:
2704 break;
2705
2706 default:
2707 att = 0;
2708 break;
2709 }
2710
2711 if (dotfile || (r->id == BTRFS_ROOT_FSTREE && inode == SUBVOL_ROOT_INODE))
2712 att |= FILE_ATTRIBUTE_HIDDEN;
2713
2715
2716 if (inode == SUBVOL_ROOT_INODE) {
2717 if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
2719 else
2720 att &= ~FILE_ATTRIBUTE_READONLY;
2721 }
2722
2723 // FIXME - get READONLY from ii->st_mode
2724 // FIXME - return SYSTEM for block/char devices?
2725
2726 if (att == 0)
2728
2729 return att;
2730}
2731
2733 _Out_writes_bytes_(Length) PUCHAR Buffer, _In_ bool override) {
2736 PIRP Irp;
2740
2741 num_reads++;
2742
2745
2746 Offset.QuadPart = (LONGLONG)StartingOffset;
2747
2748 Irp = IoAllocateIrp(DeviceObject->StackSize, false);
2749
2750 if (!Irp) {
2751 ERR("IoAllocateIrp failed\n");
2753 }
2754
2755 Irp->Flags |= IRP_NOCACHE;
2759
2760 if (override)
2762
2763 if (DeviceObject->Flags & DO_BUFFERED_IO) {
2764 Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
2765 if (!Irp->AssociatedIrp.SystemBuffer) {
2766 ERR("out of memory\n");
2768 goto exit;
2769 }
2770
2772
2773 Irp->UserBuffer = Buffer;
2774 } else if (DeviceObject->Flags & DO_DIRECT_IO) {
2775 Irp->MdlAddress = IoAllocateMdl(Buffer, Length, false, false, NULL);
2776 if (!Irp->MdlAddress) {
2777 ERR("IoAllocateMdl failed\n");
2779 goto exit;
2780 }
2781
2783
2784 _SEH2_TRY {
2788 } _SEH2_END;
2789
2790 if (!NT_SUCCESS(Status)) {
2791 ERR("MmProbeAndLockPages threw exception %08lx\n", Status);
2792 IoFreeMdl(Irp->MdlAddress);
2793 goto exit;
2794 }
2795 } else
2796 Irp->UserBuffer = Buffer;
2797
2798 IrpSp->Parameters.Read.Length = Length;
2799 IrpSp->Parameters.Read.ByteOffset = Offset;
2800
2801 Irp->UserIosb = &IoStatus;
2802
2803 Irp->UserEvent = &context.Event;
2804
2805 IoSetCompletionRoutine(Irp, read_completion, &context, true, true, true);
2806
2808
2809 if (Status == STATUS_PENDING) {
2811 Status = context.iosb.Status;
2812 }
2813
2814 if (DeviceObject->Flags & DO_DIRECT_IO) {
2815 MmUnlockPages(Irp->MdlAddress);
2816 IoFreeMdl(Irp->MdlAddress);
2817 }
2818
2819exit:
2820 IoFreeIrp(Irp);
2821
2822 return Status;
2823}
2824
2826 switch (sb->csum_type) {
2827 case CSUM_TYPE_CRC32C: {
2828 uint32_t crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
2829
2830 if (crc32 == *((uint32_t*)sb->checksum))
2831 return true;
2832
2833 WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
2834
2835 break;
2836 }
2837
2838 case CSUM_TYPE_XXHASH: {
2839 uint64_t hash = XXH64(&sb->uuid, sizeof(superblock) - sizeof(sb->checksum), 0);
2840
2841 if (hash == *((uint64_t*)sb->checksum))
2842 return true;
2843
2844 WARN("superblock hash was %I64x, expected %I64x\n", hash, *((uint64_t*)sb->checksum));
2845
2846 break;
2847 }
2848
2849 case CSUM_TYPE_SHA256: {
2851
2852 calc_sha256(hash, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
2853
2855 return true;
2856
2857 WARN("superblock hash was invalid\n");
2858
2859 break;
2860 }
2861
2862 case CSUM_TYPE_BLAKE2: {
2864
2865 blake2b(hash, sizeof(hash), &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
2866
2868 return true;
2869
2870 WARN("superblock hash was invalid\n");
2871
2872 break;
2873 }
2874
2875 default:
2876 WARN("unrecognized csum type %x\n", sb->csum_type);
2877 }
2878
2879 return false;
2880}
2881
2884 superblock* sb;
2885 ULONG i, to_read;
2886 uint8_t valid_superblocks;
2887
2888 to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
2889
2891 if (!sb) {
2892 ERR("out of memory\n");
2894 }
2895
2896 if (superblock_addrs[0] + to_read > length) {
2897 WARN("device was too short to have any superblock\n");
2898 ExFreePool(sb);
2900 }
2901
2902 i = 0;
2903 valid_superblocks = 0;
2904
2905 while (superblock_addrs[i] > 0) {
2906 if (i > 0 && superblock_addrs[i] + to_read > length)
2907 break;
2908
2910 if (!NT_SUCCESS(Status)) {
2911 ERR("Failed to read superblock %lu: %08lx\n", i, Status);
2912 ExFreePool(sb);
2913 return Status;
2914 }
2915
2916 if (sb->magic != BTRFS_MAGIC) {
2917 if (i == 0) {
2918 TRACE("not a BTRFS volume\n");
2919 ExFreePool(sb);
2921 }
2922 } else {
2923 TRACE("got superblock %lu!\n", i);
2924
2925 if (sb->sector_size == 0)
2926 WARN("superblock sector size was 0\n");
2927 else if (sb->sector_size & (sb->sector_size - 1))
2928 WARN("superblock sector size was not power of 2\n");
2929 else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
2930 WARN("invalid node size %x\n", sb->node_size);
2931 else if ((sb->node_size % sb->sector_size) != 0)
2932 WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
2933 else if (check_superblock_checksum(sb) && (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation)) {
2934 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
2935 valid_superblocks++;
2936 }
2937 }
2938
2939 i++;
2940 }
2941
2942 ExFreePool(sb);
2943
2944 if (valid_superblocks == 0) {
2945 ERR("could not find any valid superblocks\n");
2946 return STATUS_INTERNAL_ERROR;
2947 }
2948
2949 TRACE("label is %s\n", Vcb->superblock.label);
2950
2951 return STATUS_SUCCESS;
2952}
2953
2955 _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ bool Override, _Out_opt_ IO_STATUS_BLOCK* iosb) {
2956 PIRP Irp;
2957 KEVENT Event;
2961
2963
2967 InputBufferSize,
2969 OutputBufferSize,
2970 false,
2971 &Event,
2972 &IoStatus);
2973
2975
2976 if (Override) {
2979 }
2980
2982
2983 if (Status == STATUS_PENDING) {
2985 Status = IoStatus.Status;
2986 }
2987
2988 if (iosb)
2989 *iosb = IoStatus;
2990
2991 return Status;
2992}
2993
2998 if (!r) {
2999 ERR("out of memory\n");
3001 }
3002
3003 r->id = id;
3004 r->dirty = false;
3005 r->received = false;
3006 r->reserved = NULL;
3007 r->treeholder.address = addr;
3008 r->treeholder.tree = NULL;
3009 r->treeholder.generation = generation;
3010 r->parent = 0;
3011 r->send_ops = 0;
3012 r->fcbs_version = 0;
3013 r->checked_for_orphans = false;
3014 r->dropped = false;
3016 RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
3017
3019 if (!r->nonpaged) {
3020 ERR("out of memory\n");
3021 ExFreePool(r);
3023 }
3024
3025 ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
3026
3027 r->lastinode = 0;
3028
3029 if (tp) {
3030 RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
3031 if (tp->item->size < sizeof(ROOT_ITEM))
3032 RtlZeroMemory(((uint8_t*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
3033 } else
3034 RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
3035
3036 if (!Vcb->readonly && (r->id == BTRFS_ROOT_ROOT || r->id == BTRFS_ROOT_FSTREE || (r->id >= 0x100 && !(r->id & 0xf000000000000000)))) { // FS tree root
3037 // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
3039
3040 if (r->id == BTRFS_ROOT_ROOT && r->lastinode < 0x100)
3041 r->lastinode = 0x100;
3042 }
3043
3044 InsertTailList(&Vcb->roots, &r->list_entry);
3045
3046 switch (r->id) {
3047 case BTRFS_ROOT_ROOT:
3048 Vcb->root_root = r;
3049 break;
3050
3051 case BTRFS_ROOT_EXTENT:
3052 Vcb->extent_root = r;
3053 break;
3054
3055 case BTRFS_ROOT_CHUNK:
3056 Vcb->chunk_root = r;
3057 break;
3058
3059 case BTRFS_ROOT_DEVTREE:
3060 Vcb->dev_root = r;
3061 break;
3062
3064 Vcb->checksum_root = r;
3065 break;
3066
3067 case BTRFS_ROOT_UUID:
3068 Vcb->uuid_root = r;
3069 break;
3070
3072 Vcb->space_root = r;
3073 break;
3074
3076 Vcb->data_reloc_root = r;
3077 break;
3078 }
3079
3081}
3082
3084 traverse_ptr tp, next_tp;
3085 KEY searchkey;
3086 bool b;
3088
3089 searchkey.obj_id = 0;
3090 searchkey.obj_type = 0;
3091 searchkey.offset = 0;
3092
3093 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
3094 if (!NT_SUCCESS(Status)) {
3095 ERR("error - find_item returned %08lx\n", Status);
3096 return Status;
3097 }
3098
3099 do {
3100 TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3101
3102 if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
3103 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
3104
3105 if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
3106 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_ITEM, byte_limit));
3107 } else {
3108 TRACE("root %I64x - address %I64x\n", tp.item->key.obj_id, ri->block_number);
3109
3111 if (!NT_SUCCESS(Status)) {
3112 ERR("add_root returned %08lx\n", Status);
3113 return Status;
3114 }
3115 }
3116 } else if (tp.item->key.obj_type == TYPE_ROOT_BACKREF && !IsListEmpty(&Vcb->roots)) {
3117 root* lastroot = CONTAINING_RECORD(Vcb->roots.Blink, root, list_entry);
3118
3119 if (lastroot->id == tp.item->key.obj_id)
3120 lastroot->parent = tp.item->key.offset;
3121 }
3122
3123 b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3124
3125 if (b)
3126 tp = next_tp;
3127 } while (b);
3128
3129 if (!Vcb->readonly && !Vcb->data_reloc_root) {
3131 INODE_ITEM* ii;
3132 uint16_t irlen;
3133 INODE_REF* ir;
3136
3137 WARN("data reloc root doesn't exist, creating it\n");
3138
3140
3141 if (!NT_SUCCESS(Status)) {
3142 ERR("create_root returned %08lx\n", Status);
3143 return Status;
3144 }
3145
3146 reloc_root->root_item.inode.generation = 1;
3147 reloc_root->root_item.inode.st_size = 3;
3148 reloc_root->root_item.inode.st_blocks = Vcb->superblock.node_size;
3149 reloc_root->root_item.inode.st_nlink = 1;
3150 reloc_root->root_item.inode.st_mode = 040755;
3151 reloc_root->root_item.inode.flags = 0x80000000;
3152 reloc_root->root_item.inode.flags_ro = 0xffffffff;
3153 reloc_root->root_item.objid = SUBVOL_ROOT_INODE;
3154 reloc_root->root_item.bytes_used = Vcb->superblock.node_size;
3155
3157 if (!ii) {
3158 ERR("out of memory\n");
3160 }
3161
3164
3165 RtlZeroMemory(ii, sizeof(INODE_ITEM));
3166 ii->generation = Vcb->superblock.generation;
3167 ii->st_blocks = Vcb->superblock.node_size;
3168 ii->st_nlink = 1;
3169 ii->st_mode = 040755;
3170 ii->st_atime = now;
3171 ii->st_ctime = now;
3172 ii->st_mtime = now;
3173
3175 if (!NT_SUCCESS(Status)) {
3176 ERR("insert_tree_item returned %08lx\n", Status);
3177 ExFreePool(ii);
3178 return Status;
3179 }
3180
3181 irlen = (uint16_t)offsetof(INODE_REF, name[0]) + 2;
3183 if (!ir) {
3184 ERR("out of memory\n");
3186 }
3187
3188 ir->index = 0;
3189 ir->n = 2;
3190 ir->name[0] = '.';
3191 ir->name[1] = '.';
3192
3194 if (!NT_SUCCESS(Status)) {
3195 ERR("insert_tree_item returned %08lx\n", Status);
3196 ExFreePool(ir);
3197 return Status;
3198 }
3199
3200 Vcb->data_reloc_root = reloc_root;
3201 Vcb->need_write = true;
3202 }
3203
3204 return STATUS_SUCCESS;
3205}
3206
3208 KEY searchkey;
3209 traverse_ptr tp, next_tp;
3210 bool b;
3211 uint64_t lastaddr;
3213
3214 InitializeListHead(&dev->space);
3215
3216 searchkey.obj_id = 0;
3217 searchkey.obj_type = TYPE_DEV_STATS;
3218 searchkey.offset = dev->devitem.dev_id;
3219
3220 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3221 if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
3222 RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(uint64_t) * 5, tp.item->size));
3223
3224 searchkey.obj_id = dev->devitem.dev_id;
3225 searchkey.obj_type = TYPE_DEV_EXTENT;
3226 searchkey.offset = 0;
3227
3228 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
3229 if (!NT_SUCCESS(Status)) {
3230 ERR("error - find_item returned %08lx\n", Status);
3231 return Status;
3232 }
3233
3234 lastaddr = 0;
3235
3236 do {
3237 if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
3238 if (tp.item->size >= sizeof(DEV_EXTENT)) {
3239 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
3240
3241 if (tp.item->key.offset > lastaddr) {
3242 Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
3243 if (!NT_SUCCESS(Status)) {
3244 ERR("add_space_entry returned %08lx\n", Status);
3245 return Status;
3246 }
3247 }
3248
3249 lastaddr = tp.item->key.offset + de->length;
3250 } else {
3251 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
3252 }
3253 }
3254
3255 b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
3256
3257 if (b) {
3258 tp = next_tp;
3259 if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
3260 break;
3261 }
3262 } while (b);
3263
3264 if (lastaddr < dev->devitem.num_bytes) {
3265 Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
3266 if (!NT_SUCCESS(Status)) {
3267 ERR("add_space_entry returned %08lx\n", Status);
3268 return Status;
3269 }
3270 }
3271
3272 // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
3273
3274 space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
3275
3276 return STATUS_SUCCESS;
3277}
3278
3280 LIST_ENTRY* le;
3281
3282 le = Vcb->devices.Flink;
3283
3284 while (le != &Vcb->devices) {
3286
3287 if (dev2->devitem.dev_id > dev->devitem.dev_id) {
3288 InsertHeadList(le->Blink, &dev->list_entry);
3289 return;
3290 }
3291
3292 le = le->Flink;
3293 }
3294
3295 InsertTailList(&Vcb->devices, &dev->list_entry);
3296}
3297
3301 pdo_device_extension* pdode;
3302 LIST_ENTRY* le;
3303
3304 le = Vcb->devices.Flink;
3305 while (le != &Vcb->devices) {
3307
3308 TRACE("device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev->devitem.dev_id,
3309 dev->devitem.device_uuid.uuid[0], dev->devitem.device_uuid.uuid[1], dev->devitem.device_uuid.uuid[2], dev->devitem.device_uuid.uuid[3], dev->devitem.device_uuid.uuid[4], dev->devitem.device_uuid.uuid[5], dev->devitem.device_uuid.uuid[6], dev->devitem.device_uuid.uuid[7],
3310 dev->devitem.device_uuid.uuid[8], dev->devitem.device_uuid.uuid[9], dev->devitem.device_uuid.uuid[10], dev->devitem.device_uuid.uuid[11], dev->devitem.device_uuid.uuid[12], dev->devitem.device_uuid.uuid[13], dev->devitem.device_uuid.uuid[14], dev->devitem.device_uuid.uuid[15]);
3311
3312 if (RtlCompareMemory(&dev->devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3313 TRACE("returning device %I64x\n", dev->devitem.dev_id);
3314 return dev;
3315 }
3316
3317 le = le->Flink;
3318 }
3319
3320 vde = Vcb->vde;
3321
3322 if (!vde)
3323 goto end;
3324
3325 pdode = vde->pdode;
3326
3328
3329 if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3330 le = pdode->children.Flink;
3331
3332 while (le != &pdode->children) {
3334
3335 if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3336 device* dev;
3337
3339 if (!dev) {
3341 ERR("out of memory\n");
3342 return NULL;
3343 }
3344
3345 RtlZeroMemory(dev, sizeof(device));
3346 dev->devobj = vc->devobj;
3347 dev->fileobj = vc->fileobj;
3348 dev->devitem.device_uuid = *uuid;
3349 dev->devitem.dev_id = vc->devid;
3350 dev->devitem.num_bytes = vc->size;
3351 dev->seeding = vc->seeding;
3352 dev->readonly = dev->seeding;
3353 dev->reloc = false;
3354 dev->removable = false;
3355 dev->disk_num = vc->disk_num;
3356 dev->part_num = vc->part_num;
3357 dev->num_trim_entries = 0;
3358 InitializeListHead(&dev->trim_list);
3359
3361 Vcb->devices_loaded++;
3362
3364
3365 return dev;
3366 }
3367
3368 le = le->Flink;
3369 }
3370 }
3371
3373
3374end:
3375 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
3376 uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
3377 uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
3378
3379 return NULL;
3380}
3381
3385
3386 Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), true, NULL);
3387
3388 if (!NT_SUCCESS(Status)) {
3389 ERR("dev_ioctl returned %08lx\n", Status);
3390 return false;
3391 }
3392
3393 return shi.MediaRemovable != 0 ? true : false;
3394}
3395
3398 ULONG cc;
3400
3401 Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
3402
3403 if (!NT_SUCCESS(Status)) {
3404 ERR("dev_ioctl returned %08lx\n", Status);
3405 return 0;
3406 }
3407
3408 if (iosb.Information < sizeof(ULONG)) {
3409 ERR("iosb.Information was too short\n");
3410 return 0;
3411 }
3412
3413 return cc;
3414}
3415
3418 ULONG aptelen;
3419 ATA_PASS_THROUGH_EX* apte;
3422
3423 dev->removable = is_device_removable(dev->devobj);
3424 dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
3425
3426 if (get_nums) {
3428
3430 &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
3431
3432 if (!NT_SUCCESS(Status)) {
3433 WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status);
3434 dev->disk_num = 0xffffffff;
3435 dev->part_num = 0xffffffff;
3436 } else {
3437 dev->disk_num = sdn.DeviceNumber;
3438 dev->part_num = sdn.PartitionNumber;
3439 }
3440 }
3441
3442 dev->trim = false;
3443 dev->readonly = dev->seeding;
3444 dev->reloc = false;
3445 dev->num_trim_entries = 0;
3446 dev->stats_changed = false;
3447 InitializeListHead(&dev->trim_list);
3448
3449 if (!dev->readonly) {
3451 NULL, 0, true, NULL);
3453 dev->readonly = true;
3454 }
3455
3456 aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
3458 if (!apte) {
3459 ERR("out of memory\n");
3460 return;
3461 }
3462
3463 RtlZeroMemory(apte, aptelen);
3464
3465 apte->Length = sizeof(ATA_PASS_THROUGH_EX);
3467 apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
3468 apte->TimeOutValue = 3;
3469 apte->DataBufferOffset = apte->Length;
3471
3472 Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
3473 apte, aptelen, true, NULL);
3474
3475 if (!NT_SUCCESS(Status))
3476 TRACE("IOCTL_ATA_PASS_THROUGH returned %08lx for IDENTIFY DEVICE\n", Status);
3477 else {
3479
3480 if (idd->CommandSetSupport.FlushCache) {
3481 dev->can_flush = true;
3482 TRACE("FLUSH CACHE supported\n");
3483 } else
3484 TRACE("FLUSH CACHE not supported\n");
3485 }
3486
3487 ExFreePool(apte);
3488
3489#ifdef DEBUG_TRIM_EMULATION
3490 dev->trim = true;
3491 Vcb->trim = true;
3492#else
3495 spq.AdditionalParameters[0] = 0;
3496
3498 &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), true, NULL);
3499
3500 if (NT_SUCCESS(Status)) {
3501 if (dtd.TrimEnabled) {
3502 dev->trim = true;
3503 Vcb->trim = true;
3504 TRACE("TRIM supported\n");
3505 } else
3506 TRACE("TRIM not supported\n");
3507 }
3508#endif
3509
3510 RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5);
3511}
3512
3514 traverse_ptr tp, next_tp;
3515 KEY searchkey;
3516 bool b;
3517 chunk* c;
3519
3520 searchkey.obj_id = 0;
3521 searchkey.obj_type = 0;
3522 searchkey.offset = 0;
3523
3524 Vcb->data_flags = 0;
3525 Vcb->metadata_flags = 0;
3526 Vcb->system_flags = 0;
3527
3528 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp);
3529 if (!NT_SUCCESS(Status)) {
3530 ERR("error - find_item returned %08lx\n", Status);
3531 return Status;
3532 }
3533
3534 do {
3535 TRACE("(%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3536
3537 if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
3538 if (tp.item->size < sizeof(DEV_ITEM)) {
3539 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
3540 } else {
3541 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
3542 LIST_ENTRY* le;
3543 bool done = false;
3544
3545 le = Vcb->devices.Flink;
3546 while (le != &Vcb->devices) {
3548
3549 if (dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3550 RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
3551
3552 if (le != Vcb->devices.Flink)
3553 init_device(Vcb, dev, true);
3554
3555 done = true;
3556 break;
3557 }
3558
3559 le = le->Flink;
3560 }
3561
3562 if (!done && Vcb->vde) {
3563 volume_device_extension* vde = Vcb->vde;
3564 pdo_device_extension* pdode = vde->pdode;
3565
3567
3568 if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
3569 le = pdode->children.Flink;
3570
3571 while (le != &pdode->children) {
3573
3574 if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
3575 device* dev;
3576
3578 if (!dev) {
3580 ERR("out of memory\n");
3582 }
3583
3584 RtlZeroMemory(dev, sizeof(device));
3585
3586 dev->devobj = vc->devobj;
3587 dev->fileobj = vc->fileobj;
3588 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
3589 dev->seeding = vc->seeding;
3590 init_device(Vcb, dev, false);
3591
3592 if (dev->devitem.num_bytes > vc->size) {
3593 WARN("device %I64x: DEV_ITEM says %I64x bytes, but Windows only reports %I64x\n", tp.item->key.offset,
3594 dev->devitem.num_bytes, vc->size);
3595
3596 dev->devitem.num_bytes = vc->size;
3597 }
3598
3599 dev->disk_num = vc->disk_num;
3600 dev->part_num = vc->part_num;
3602 Vcb->devices_loaded++;
3603
3604 done = true;
3605 break;
3606 }
3607
3608 le = le->Flink;
3609 }
3610
3611 if (!done) {
3612 if (!Vcb->options.allow_degraded) {
3613 ERR("volume not found: device %I64x, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset,
3614 di->device_uuid.uuid[0], di->device_uuid.uuid[1], di->device_uuid.uuid[2], di->device_uuid.uuid[3], di->device_uuid.uuid[4], di->device_uuid.uuid[5], di->device_uuid.uuid[6], di->device_uuid.uuid[7],
3615 di->device_uuid.uuid[8], di->device_uuid.uuid[9], di->device_uuid.uuid[10], di->device_uuid.uuid[11], di->device_uuid.uuid[12], di->device_uuid.uuid[13], di->device_uuid.uuid[14], di->device_uuid.uuid[15]);
3616 } else {
3617 device* dev;
3618
3620 if (!dev) {
3622 ERR("out of memory\n");
3624 }
3625
3626 RtlZeroMemory(dev, sizeof(device));
3627
3628 // Missing device, so we keep dev->devobj as NULL
3629 RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));