-/* Copyright (c) Mark Harmstone 2016
- *
+/* Copyright (c) Mark Harmstone 2016-17
+ *
* This file is part of WinBtrfs.
- *
+ *
* WinBtrfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published by
* the Free Software Foundation, either version 3 of the Licence, or
* (at your option) any later version.
- *
+ *
* WinBtrfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public Licence for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public Licence
* along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
#include <intrin.h>
#endif
#endif
+#include <ntddscsi.h>
#include "btrfs.h"
-#ifndef __REACTOS__
-#include <winioctl.h>
-#else
-#include <rtlfuncs.h>
+#include <ata.h>
+
+#ifndef _MSC_VER
+#include <initguid.h>
+#include <ntddstor.h>
+#undef INITGUID
#endif
-#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
- BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
-#define COMPAT_RO_SUPPORTED 0
+#include <ntdddisk.h>
+#include <ntddvol.h>
+
+#ifdef _MSC_VER
+#include <initguid.h>
+#include <ntddstor.h>
+#undef INITGUID
+#endif
+
+#define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
+ BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
+ BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
+#define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
static WCHAR device_name[] = {'\\','B','t','r','f','s',0};
static WCHAR dosdevice_name[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
+DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17, 0x9a, 0x7d, 0x1d);
+
PDRIVER_OBJECT drvobj;
-PDEVICE_OBJECT devobj;
+PDEVICE_OBJECT master_devobj;
#ifndef __REACTOS__
-BOOL have_sse42 = FALSE;
+BOOL have_sse42 = FALSE, have_sse2 = FALSE;
#endif
UINT64 num_reads = 0;
-LIST_ENTRY uid_map_list;
-LIST_ENTRY volumes;
+LIST_ENTRY uid_map_list, gid_map_list;
LIST_ENTRY VcbList;
ERESOURCE global_loading_lock;
UINT32 debug_log_level = 0;
+UINT32 mount_compress = 0;
+UINT32 mount_compress_force = 0;
+UINT32 mount_compress_type = 0;
+UINT32 mount_zlib_level = 3;
+UINT32 mount_flush_interval = 30;
+UINT32 mount_max_inline = 2048;
+UINT32 mount_skip_balance = 0;
+UINT32 mount_no_barrier = 0;
+UINT32 mount_no_trim = 0;
+UINT32 mount_clear_cache = 0;
+UINT32 mount_allow_degraded = 0;
+UINT32 mount_readonly = 0;
+UINT32 no_pnp = 0;
BOOL log_started = FALSE;
-UNICODE_STRING log_device, log_file;
+UNICODE_STRING log_device, log_file, registry_path;
+tPsUpdateDiskCounters fPsUpdateDiskCounters;
+tCcCopyReadEx fCcCopyReadEx;
+tCcCopyWriteEx fCcCopyWriteEx;
+tCcSetAdditionalCacheAttributesEx fCcSetAdditionalCacheAttributesEx;
+tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters;
+BOOL diskacc = FALSE;
+void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL;
+ERESOURCE pdo_list_lock, mapping_lock;
+LIST_ENTRY pdo_list;
+BOOL finished_probing = FALSE;
+HANDLE degraded_wait_handle = NULL, mountmgr_thread_handle = NULL;
+BOOL degraded_wait = TRUE;
+KEVENT mountmgr_thread_event;
+BOOL shutting_down = FALSE;
#ifdef _DEBUG
PFILE_OBJECT comfo = NULL;
PDEVICE_OBJECT comdo = NULL;
HANDLE log_handle = NULL;
+ERESOURCE log_lock;
+HANDLE serial_thread_handle = NULL;
+
+static void init_serial(BOOL first_time);
#endif
-static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject);
+static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp);
typedef struct {
KEVENT Event;
} read_context;
#ifdef _DEBUG
-static NTSTATUS STDCALL dbg_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+_Function_class_(IO_COMPLETION_ROUTINE)
+static NTSTATUS dbg_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
read_context* context = conptr;
-
-// DbgPrint("dbg_completion\n");
-
+
+ UNUSED(DeviceObject);
+
context->iosb = Irp->IoStatus;
KeSetEvent(&context->Event, 0, FALSE);
-
-// return STATUS_SUCCESS;
+
return STATUS_MORE_PROCESSING_REQUIRED;
}
#ifdef DEBUG_LONG_MESSAGES
-void STDCALL _debug_message(const char* func, UINT8 priority, const char* file, unsigned int line, char* s, ...) {
+void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) {
#else
-void STDCALL _debug_message(const char* func, UINT8 priority, char* s, ...) {
+void _debug_message(_In_ const char* func, _In_ char* s, ...) {
#endif
LARGE_INTEGER offset;
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status;
PIRP Irp;
va_list ap;
- char *buf2 = NULL, *buf;
- read_context* context = NULL;
+ char *buf2, *buf;
+ read_context context;
UINT32 length;
-
- if (log_started && priority > debug_log_level)
- return;
-
+
buf2 = ExAllocatePoolWithTag(NonPagedPool, 1024, ALLOC_TAG);
-
+
if (!buf2) {
DbgPrint("Couldn't allocate buffer in debug_message\n");
return;
}
-
+
#ifdef DEBUG_LONG_MESSAGES
- sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func, file, line);
+ sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThread(), func, file, line);
#else
- sprintf(buf2, "%p:%s:", PsGetCurrentThreadId(), func);
+ sprintf(buf2, "%p:%s:", PsGetCurrentThread(), func);
#endif
buf = &buf2[strlen(buf2)];
-
+
va_start(ap, s);
vsprintf(buf, s, ap);
-
+
+ ExAcquireResourceSharedLite(&log_lock, TRUE);
+
if (!log_started || (log_device.Length == 0 && log_file.Length == 0)) {
DbgPrint(buf2);
} else if (log_device.Length > 0) {
DbgPrint(buf2);
goto exit2;
}
-
+
length = (UINT32)strlen(buf2);
-
+
offset.u.LowPart = 0;
offset.u.HighPart = 0;
-
- context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
- if (!context) {
- DbgPrint("Couldn't allocate context in debug_message\n");
- return;
- }
-
- RtlZeroMemory(context, sizeof(read_context));
-
- KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
- // status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
-
+ RtlZeroMemory(&context, sizeof(read_context));
+
+ KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+
Irp = IoAllocateIrp(comdo->StackSize, FALSE);
-
+
if (!Irp) {
DbgPrint("IoAllocateIrp failed\n");
goto exit2;
}
-
+
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_WRITE;
-
+
if (comdo->Flags & DO_BUFFERED_IO) {
Irp->AssociatedIrp.SystemBuffer = buf2;
DbgPrint("IoAllocateMdl failed\n");
goto exit;
}
-
- MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+
+ MmBuildMdlForNonPagedPool(Irp->MdlAddress);
} else {
Irp->UserBuffer = buf2;
}
IrpSp->Parameters.Write.Length = length;
IrpSp->Parameters.Write.ByteOffset = offset;
-
- Irp->UserIosb = &context->iosb;
- Irp->UserEvent = &context->Event;
+ Irp->UserIosb = &context.iosb;
- IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE);
+ Irp->UserEvent = &context.Event;
+
+ IoSetCompletionRoutine(Irp, dbg_completion, &context, TRUE, TRUE, TRUE);
Status = IoCallDriver(comdo, Irp);
if (Status == STATUS_PENDING) {
- KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
- Status = context->iosb.Status;
+ KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+ Status = context.iosb.Status;
}
-
- if (comdo->Flags & DO_DIRECT_IO) {
- MmUnlockPages(Irp->MdlAddress);
+
+ if (comdo->Flags & DO_DIRECT_IO)
IoFreeMdl(Irp->MdlAddress);
- }
-
+
if (!NT_SUCCESS(Status)) {
DbgPrint("failed to write to COM1 - error %08x\n", Status);
goto exit;
}
-
+
exit:
IoFreeIrp(Irp);
} else if (log_handle != NULL) {
IO_STATUS_BLOCK iosb;
-
+
length = (UINT32)strlen(buf2);
-
+
Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
-
+
if (!NT_SUCCESS(Status)) {
DbgPrint("failed to write to file - error %08x\n", Status);
}
}
-
+
exit2:
+ ExReleaseResourceLite(&log_lock);
+
va_end(ap);
-
- if (context)
- ExFreePool(context);
-
+
if (buf2)
ExFreePool(buf2);
}
#endif
-ULONG sector_align( ULONG NumberToBeAligned, ULONG Alignment )
-{
- if( Alignment & ( Alignment - 1 ) )
- {
- //
- // Alignment not a power of 2
- // Just returning
- //
- return NumberToBeAligned;
- }
- if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
- {
- NumberToBeAligned = NumberToBeAligned + Alignment;
- NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
- }
- return NumberToBeAligned;
-}
-
-int keycmp(const KEY* key1, const KEY* key2) {
- if (key1->obj_id < key2->obj_id) {
- return -1;
- } else if (key1->obj_id > key2->obj_id) {
- return 1;
- }
-
- if (key1->obj_type < key2->obj_type) {
- return -1;
- } else if (key1->obj_type > key2->obj_type) {
- return 1;
- }
-
- if (key1->offset < key2->offset) {
- return -1;
- } else if (key1->offset > key2->offset) {
- return 1;
- }
-
- return 0;
-}
-
-BOOL is_top_level(PIRP Irp) {
+BOOL is_top_level(_In_ PIRP Irp) {
if (!IoGetTopLevelIrp()) {
IoSetTopLevelIrp(Irp);
return TRUE;
return FALSE;
}
-static void STDCALL DriverUnload(PDRIVER_OBJECT DriverObject) {
+_Function_class_(DRIVER_UNLOAD)
+#ifdef __REACTOS__
+static void NTAPI DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
+#else
+static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
+#endif
UNICODE_STRING dosdevice_nameW;
ERR("DriverUnload\n");
-
+
free_cache();
-
+
IoUnregisterFileSystem(DriverObject->DeviceObject);
-
+
+ if (notification_entry2)
+#ifdef __REACTOS__
+ IoUnregisterPlugPlayNotification(notification_entry2);
+#else
+ IoUnregisterPlugPlayNotificationEx(notification_entry2);
+#endif
+
+ if (notification_entry3)
+#ifdef __REACTOS__
+ IoUnregisterPlugPlayNotification(notification_entry3);
+#else
+ IoUnregisterPlugPlayNotificationEx(notification_entry3);
+#endif
+
+ if (notification_entry)
+#ifdef __REACTOS__
+ IoUnregisterPlugPlayNotification(notification_entry);
+#else
+ IoUnregisterPlugPlayNotificationEx(notification_entry);
+#endif
+
dosdevice_nameW.Buffer = dosdevice_name;
dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
IoDeleteSymbolicLink(&dosdevice_nameW);
IoDeleteDevice(DriverObject->DeviceObject);
-
+
while (!IsListEmpty(&uid_map_list)) {
LIST_ENTRY* le = RemoveHeadList(&uid_map_list);
uid_map* um = CONTAINING_RECORD(le, uid_map, listentry);
-
+
ExFreePool(um->sid);
ExFreePool(um);
}
-
+
+ while (!IsListEmpty(&gid_map_list)) {
+ gid_map* gm = CONTAINING_RECORD(RemoveHeadList(&gid_map_list), gid_map, listentry);
+
+ ExFreePool(gm->sid);
+ ExFreePool(gm);
+ }
+
// FIXME - free volumes and their devpaths
-
+
#ifdef _DEBUG
if (comfo)
ObDereferenceObject(comfo);
-
+
if (log_handle)
ZwClose(log_handle);
#endif
-
+
ExDeleteResourceLite(&global_loading_lock);
-
+ ExDeleteResourceLite(&pdo_list_lock);
+
if (log_device.Buffer)
ExFreePool(log_device.Buffer);
-
+
if (log_file.Buffer)
ExFreePool(log_file.Buffer);
+
+ if (registry_path.Buffer)
+ ExFreePool(registry_path.Buffer);
+
+#ifdef _DEBUG
+ ExDeleteResourceLite(&log_lock);
+#endif
+ ExDeleteResourceLite(&mapping_lock);
}
-BOOL STDCALL get_last_inode(device_extension* Vcb, root* r) {
+static BOOL get_last_inode(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_opt_ PIRP Irp) {
KEY searchkey;
traverse_ptr tp, prev_tp;
NTSTATUS Status;
-
+
// get last entry
searchkey.obj_id = 0xffffffffffffffff;
searchkey.obj_type = 0xff;
searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(Vcb, r, &tp, &searchkey, FALSE);
+
+ Status = find_item(Vcb, r, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return FALSE;
}
-
- while (find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
- free_traverse_ptr(&tp);
+
+ if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
+ r->lastinode = tp.item->key.obj_id;
+ TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
+ return TRUE;
+ }
+
+ while (find_prev_item(Vcb, &tp, &prev_tp, Irp)) {
tp = prev_tp;
-
+
TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
+
+ if (tp.item->key.obj_type == TYPE_INODE_ITEM || (tp.item->key.obj_type == TYPE_ROOT_ITEM && !(tp.item->key.obj_id & 0x8000000000000000))) {
r->lastinode = tp.item->key.obj_id;
- free_traverse_ptr(&tp);
TRACE("last inode for tree %llx is %llx\n", r->id, r->lastinode);
return TRUE;
}
}
-
- free_traverse_ptr(&tp);
-
+
r->lastinode = SUBVOL_ROOT_INODE;
-
+
WARN("no INODE_ITEMs in tree %llx\n", r->id);
-
+
return TRUE;
}
-BOOL STDCALL get_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8** data, UINT16* datalen) {
- KEY searchkey;
- traverse_ptr tp;
- DIR_ITEM* xa;
- ULONG size, xasize;
- NTSTATUS Status;
-
- TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_XATTR_ITEM;
- searchkey.offset = crc32;
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return FALSE;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- if (tp.item->size < sizeof(DIR_ITEM)) {
- 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));
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- xa = (DIR_ITEM*)tp.item->data;
- size = tp.item->size;
-
+_Success_(return)
+static BOOL extract_xattr(_In_reads_bytes_(size) void* item, _In_ USHORT size, _In_z_ char* name, _Out_ UINT8** data, _Out_ UINT16* datalen) {
+ DIR_ITEM* xa = (DIR_ITEM*)item;
+ USHORT xasize;
+
while (TRUE) {
if (size < sizeof(DIR_ITEM) || size < (sizeof(DIR_ITEM) - 1 + xa->m + xa->n)) {
- WARN("(%llx,%x,%llx) is truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- free_traverse_ptr(&tp);
+ WARN("DIR_ITEM is truncated\n");
return FALSE;
}
-
+
if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
- TRACE("found xattr %s in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
-
+ TRACE("found xattr %s\n", name);
+
*datalen = xa->m;
-
+
if (xa->m > 0) {
*data = ExAllocatePoolWithTag(PagedPool, xa->m, ALLOC_TAG);
if (!*data) {
ERR("out of memory\n");
- free_traverse_ptr(&tp);
return FALSE;
}
-
+
RtlCopyMemory(*data, &xa->name[xa->n], xa->m);
} else
*data = NULL;
-
- free_traverse_ptr(&tp);
+
return TRUE;
}
-
+
xasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
if (size > xasize) {
} else
break;
}
-
- TRACE("xattr %s not found in (%llx,%x,%llx)\n", name, searchkey.obj_id, searchkey.obj_type, searchkey.offset);
-
- free_traverse_ptr(&tp);
-
+
+ TRACE("xattr %s not found\n", name);
+
return FALSE;
}
-NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, LIST_ENTRY* rollback) {
+_Success_(return)
+BOOL get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* subvol, _In_ UINT64 inode, _In_z_ char* name, _In_ UINT32 crc32,
+ _Out_ UINT8** data, _Out_ UINT16* datalen, _In_opt_ PIRP Irp) {
KEY searchkey;
traverse_ptr tp;
- ULONG xasize;
- DIR_ITEM* xa;
NTSTATUS Status;
-
- TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_XATTR_ITEM;
- searchkey.offset = crc32;
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
-
- if (!keycmp(&tp.item->key, &searchkey)) { // key exists
- UINT8* newdata;
- ULONG size = tp.item->size;
-
- xa = (DIR_ITEM*)tp.item->data;
-
- if (tp.item->size < sizeof(DIR_ITEM)) {
- 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));
- } else {
- while (TRUE) {
- ULONG oldxasize;
-
- if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
-
- if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
- UINT64 pos;
-
- // replace
- newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
- if (!newdata) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- pos = (UINT8*)xa - tp.item->data;
- if (pos + oldxasize < tp.item->size) { // copy after changed xattr
- RtlCopyMemory(newdata + pos + xasize, tp.item->data + pos + oldxasize, tp.item->size - pos - oldxasize);
- }
-
- if (pos > 0) { // copy before changed xattr
- RtlCopyMemory(newdata, tp.item->data, pos);
- xa = (DIR_ITEM*)(newdata + pos);
- } else
- xa = (DIR_ITEM*)newdata;
-
- xa->key.obj_id = 0;
- xa->key.obj_type = 0;
- xa->key.offset = 0;
- xa->transid = Vcb->superblock.generation;
- xa->m = datalen;
- xa->n = (UINT16)strlen(name);
- xa->type = BTRFS_TYPE_EA;
- RtlCopyMemory(xa->name, name, strlen(name));
- RtlCopyMemory(xa->name + strlen(name), data, datalen);
-
- delete_tree_item(Vcb, &tp, rollback);
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, rollback);
-
- break;
- }
-
- if (xa->m + xa->n >= size) { // FIXME - test this works
- // not found, add to end of data
- newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
- if (!newdata) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(newdata, tp.item->data, tp.item->size);
-
- xa = (DIR_ITEM*)((UINT8*)newdata + tp.item->size);
- xa->key.obj_id = 0;
- xa->key.obj_type = 0;
- xa->key.offset = 0;
- xa->transid = Vcb->superblock.generation;
- xa->m = datalen;
- xa->n = (UINT16)strlen(name);
- xa->type = BTRFS_TYPE_EA;
- RtlCopyMemory(xa->name, name, strlen(name));
- RtlCopyMemory(xa->name + strlen(name), data, datalen);
-
- delete_tree_item(Vcb, &tp, rollback);
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, rollback);
-
- break;
- } else {
- xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
- size -= oldxasize;
- }
- }
- }
- } else {
- // add new DIR_ITEM struct
-
- xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
- if (!xa) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- xa->key.obj_id = 0;
- xa->key.obj_type = 0;
- xa->key.offset = 0;
- xa->transid = Vcb->superblock.generation;
- xa->m = datalen;
- xa->n = (UINT16)strlen(name);
- xa->type = BTRFS_TYPE_EA;
- RtlCopyMemory(xa->name, name, strlen(name));
- RtlCopyMemory(xa->name + strlen(name), data, datalen);
-
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, rollback);
- }
-
- free_traverse_ptr(&tp);
-
- return STATUS_SUCCESS;
-}
-BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp;
- DIR_ITEM* xa;
- NTSTATUS Status;
-
- TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32);
-
+ TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
+
searchkey.obj_id = inode;
searchkey.obj_type = TYPE_XATTR_ITEM;
searchkey.offset = crc32;
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+
+ Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return FALSE;
}
-
- if (!keycmp(&tp.item->key, &searchkey)) { // key exists
- ULONG size = tp.item->size;
-
- if (tp.item->size < sizeof(DIR_ITEM)) {
- 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));
-
- free_traverse_ptr(&tp);
- return FALSE;
- } else {
- xa = (DIR_ITEM*)tp.item->data;
-
- while (TRUE) {
- ULONG oldxasize;
-
- if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- free_traverse_ptr(&tp);
-
- return FALSE;
- }
-
- oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
-
- if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
- ULONG newsize;
- UINT8 *newdata, *dioff;
-
- newsize = tp.item->size - (sizeof(DIR_ITEM) - 1 + xa->n + xa->m);
-
- delete_tree_item(Vcb, &tp, rollback);
-
- if (newsize == 0) {
- TRACE("xattr %s deleted\n", name);
- free_traverse_ptr(&tp);
-
- return TRUE;
- }
-
- // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
- newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG);
- if (!newdata) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- if ((UINT8*)xa > tp.item->data) {
- RtlCopyMemory(newdata, tp.item->data, (UINT8*)xa - tp.item->data);
- dioff = newdata + ((UINT8*)xa - tp.item->data);
- } else {
- dioff = newdata;
- }
-
- if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size)
- RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data));
-
- insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, rollback);
-
- free_traverse_ptr(&tp);
-
- return TRUE;
- }
-
- if (xa->m + xa->n >= size) { // FIXME - test this works
- WARN("xattr %s not found\n", name);
- free_traverse_ptr(&tp);
- return FALSE;
- } else {
- xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
- size -= oldxasize;
- }
- }
- }
- } else {
- WARN("xattr %s not found\n", name);
- free_traverse_ptr(&tp);
-
+ if (keycmp(tp.item->key, searchkey)) {
+ TRACE("could not find item (%llx,%x,%llx)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
return FALSE;
}
-}
-NTSTATUS add_dir_item(device_extension* Vcb, root* subvol, UINT64 inode, UINT32 crc32, DIR_ITEM* di, ULONG disize, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp;
- UINT8* di2;
- NTSTATUS Status;
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_DIR_ITEM;
- searchkey.offset = crc32;
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (!keycmp(&tp.item->key, &searchkey)) {
- ULONG maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
-
- if (tp.item->size + disize > maxlen) {
- WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp.item->size, disize, maxlen);
- free_traverse_ptr(&tp);
- return STATUS_INTERNAL_ERROR;
- }
-
- di2 = ExAllocatePoolWithTag(PagedPool, tp.item->size + disize, ALLOC_TAG);
- if (!di2) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- if (tp.item->size > 0)
- RtlCopyMemory(di2, tp.item->data, tp.item->size);
-
- RtlCopyMemory(di2 + tp.item->size, di, disize);
-
- delete_tree_item(Vcb, &tp, rollback);
-
- insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di2, tp.item->size + disize, NULL, rollback);
-
- ExFreePool(di);
- } else {
- insert_tree_item(Vcb, subvol, inode, TYPE_DIR_ITEM, crc32, di, disize, NULL, rollback);
+ if (tp.item->size < sizeof(DIR_ITEM)) {
+ 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));
+ return FALSE;
}
- free_traverse_ptr(&tp);
-
- return STATUS_SUCCESS;
-}
-
-UINT64 find_next_dir_index(device_extension* Vcb, root* subvol, UINT64 inode) {
- KEY searchkey;
- traverse_ptr tp, prev_tp;
- UINT64 dirpos;
- NTSTATUS Status;
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_DIR_INDEX + 1;
- searchkey.offset = 0;
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return 0;
- }
-
- if (!keycmp(&searchkey, &tp.item->key)) {
- if (!find_prev_item(Vcb, &tp, &prev_tp, FALSE)) {
- free_traverse_ptr(&tp);
- tp = prev_tp;
-
- TRACE("moving back to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- }
- }
-
- if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_DIR_INDEX) {
- dirpos = tp.item->key.offset + 1;
- } else
- dirpos = 2;
-
- free_traverse_ptr(&tp);
-
- return dirpos;
+ return extract_xattr(tp.item->data, tp.item->size, name, data, datalen);
}
-static NTSTATUS STDCALL drv_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_CLOSE)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_close(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level;
- TRACE("close\n");
-
FsRtlEnterFileSystem();
+ TRACE("close\n");
+
top_level = is_top_level(Irp);
-
- if (DeviceObject == devobj) {
+
+ if (DeviceObject == master_devobj) {
TRACE("Closing file system\n");
Status = STATUS_SUCCESS;
- goto exit;
+ goto end;
+ } else if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_close(DeviceObject, Irp);
+ goto end;
+ } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
}
-
+
IrpSp = IoGetCurrentIrpStackLocation(Irp);
-
+
// FIXME - unmount if called for volume
// FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
-
- Status = close_file(DeviceObject->DeviceExtension, IrpSp->FileObject);
-exit:
+ Status = close_file(IrpSp->FileObject, Irp);
+
+end:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
-
+
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
-
- if (top_level)
+
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
- FsRtlExitFileSystem();
-
+
TRACE("returning %08x\n", Status);
+ FsRtlExitFileSystem();
+
return Status;
}
-static NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ PFILE_OBJECT FileObject = IrpSp->FileObject;
+ fcb* fcb = FileObject->FsContext;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level;
FsRtlEnterFileSystem();
+ TRACE("flush buffers\n");
+
top_level = is_top_level(Irp);
-
-// ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
-
- Status = write_file(DeviceObject, Irp);
-
- Irp->IoStatus.Status = Status;
- TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
-
- if (Status != STATUS_PENDING)
- IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
- if (top_level)
- IoSetTopLevelIrp(NULL);
-
- FsRtlExitFileSystem();
-
- TRACE("returning %08x\n", Status);
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_flush_buffers(DeviceObject, Irp);
+ goto end;
+ } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
- return Status;
-}
+ if (!fcb) {
+ ERR("fcb was NULL\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
-static NTSTATUS STDCALL drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
- NTSTATUS Status;
- BOOL top_level;
+ if (fcb == Vcb->volume_fcb) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
- FsRtlEnterFileSystem();
+ Irp->IoStatus.Information = 0;
- top_level = is_top_level(Irp);
-
- FIXME("STUB: query ea\n");
- Status = STATUS_NOT_IMPLEMENTED;
-
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+
+ Status = STATUS_SUCCESS;
Irp->IoStatus.Status = Status;
- Irp->IoStatus.Information = 0;
- IoCompleteRequest( Irp, IO_NO_INCREMENT );
-
- if (top_level)
- IoSetTopLevelIrp(NULL);
-
- FsRtlExitFileSystem();
+ if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+ CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
- return Status;
-}
+ if (fcb->Header.PagingIoResource) {
+ ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
+ ExReleaseResourceLite(fcb->Header.PagingIoResource);
+ }
-static NTSTATUS STDCALL drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
- NTSTATUS Status;
- device_extension* Vcb = DeviceObject->DeviceExtension;
- BOOL top_level;
+ Status = Irp->IoStatus.Status;
+ }
- FsRtlEnterFileSystem();
+end:
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
- top_level = is_top_level(Irp);
-
- FIXME("STUB: set ea\n");
- Status = STATUS_NOT_IMPLEMENTED;
-
- if (Vcb->readonly)
- Status = STATUS_MEDIA_WRITE_PROTECTED;
-
- // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
-
- Irp->IoStatus.Status = Status;
- Irp->IoStatus.Information = 0;
+ TRACE("returning %08x\n", Status);
- IoCompleteRequest( Irp, IO_NO_INCREMENT );
-
- if (top_level)
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
return Status;
}
-static NTSTATUS STDCALL drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+static void calculate_total_space(_In_ device_extension* Vcb, _Out_ UINT64* totalsize, _Out_ UINT64* freespace) {
+ UINT64 nfactor, dfactor, sectors_used;
+
+ if (Vcb->data_flags & BLOCK_FLAG_DUPLICATE || Vcb->data_flags & BLOCK_FLAG_RAID1 || Vcb->data_flags & BLOCK_FLAG_RAID10) {
+ nfactor = 1;
+ dfactor = 2;
+ } else if (Vcb->data_flags & BLOCK_FLAG_RAID5) {
+ nfactor = Vcb->superblock.num_devices - 1;
+ dfactor = Vcb->superblock.num_devices;
+ } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
+ nfactor = Vcb->superblock.num_devices - 2;
+ dfactor = Vcb->superblock.num_devices;
+ } else {
+ nfactor = 1;
+ dfactor = 1;
+ }
+
+ sectors_used = Vcb->superblock.bytes_used / Vcb->superblock.sector_size;
+
+ *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) * nfactor / dfactor;
+ *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
+}
+
+#ifndef __REACTOS__
+// This function exists because we have to lie about our FS type in certain situations.
+// MPR!MprGetConnection queries the FS type, and compares it to a whitelist. If it doesn't match,
+// it will return ERROR_NO_NET_OR_BAD_PATH, which prevents UAC from working.
+// The command mklink refuses to create hard links on anything other than NTFS, so we have to
+// blacklist cmd.exe too.
+
+static BOOL lie_about_fs_type() {
NTSTATUS Status;
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
- PFILE_OBJECT FileObject = IrpSp->FileObject;
- fcb* fcb = FileObject->FsContext;
- BOOL top_level;
+ PROCESS_BASIC_INFORMATION pbi;
+ PPEB peb;
+ LIST_ENTRY* le;
+ ULONG retlen;
- TRACE("flush buffers\n");
-
- FsRtlEnterFileSystem();
+ static WCHAR mpr[] = L"MPR.DLL";
+ static WCHAR cmd[] = L"CMD.EXE";
+ static WCHAR fsutil[] = L"FSUTIL.EXE";
+ UNICODE_STRING mprus, cmdus, fsutilus;
- top_level = is_top_level(Irp);
-
- Status = STATUS_SUCCESS;
- Irp->IoStatus.Status = Status;
- Irp->IoStatus.Information = 0;
-
- if (fcb->type != BTRFS_TYPE_DIRECTORY) {
- CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &Irp->IoStatus);
-
- if (fcb->Header.PagingIoResource) {
- ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
- ExReleaseResourceLite(fcb->Header.PagingIoResource);
+ mprus.Buffer = mpr;
+ mprus.Length = mprus.MaximumLength = (USHORT)(wcslen(mpr) * sizeof(WCHAR));
+ cmdus.Buffer = cmd;
+ cmdus.Length = cmdus.MaximumLength = (USHORT)(wcslen(cmd) * sizeof(WCHAR));
+ fsutilus.Buffer = fsutil;
+ fsutilus.Length = fsutilus.MaximumLength = (USHORT)(wcslen(fsutil) * sizeof(WCHAR));
+
+ if (!PsGetCurrentProcess())
+ return FALSE;
+
+ Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwQueryInformationProcess returned %08x\n", Status);
+ return FALSE;
+ }
+
+ if (!pbi.PebBaseAddress)
+ return FALSE;
+
+ peb = pbi.PebBaseAddress;
+
+ if (!peb->Ldr)
+ return FALSE;
+
+ le = peb->Ldr->InMemoryOrderModuleList.Flink;
+ while (le != &peb->Ldr->InMemoryOrderModuleList) {
+ LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(le, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+ BOOL blacklist = FALSE;
+
+ if (entry->FullDllName.Length >= mprus.Length) {
+ UNICODE_STRING name;
+
+ name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - mprus.Length) / sizeof(WCHAR)];
+ name.Length = name.MaximumLength = mprus.Length;
+
+ blacklist = FsRtlAreNamesEqual(&name, &mprus, TRUE, NULL);
}
-
- Status = Irp->IoStatus.Status;
+
+ if (!blacklist && entry->FullDllName.Length >= cmdus.Length) {
+ UNICODE_STRING name;
+
+ name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - cmdus.Length) / sizeof(WCHAR)];
+ name.Length = name.MaximumLength = cmdus.Length;
+
+ blacklist = FsRtlAreNamesEqual(&name, &cmdus, TRUE, NULL);
+ }
+
+ if (!blacklist && entry->FullDllName.Length >= fsutilus.Length) {
+ UNICODE_STRING name;
+
+ name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - fsutilus.Length) / sizeof(WCHAR)];
+ name.Length = name.MaximumLength = fsutilus.Length;
+
+ blacklist = FsRtlAreNamesEqual(&name, &fsutilus, TRUE, NULL);
+ }
+
+ if (blacklist) {
+ void** frames;
+ ULONG i, num_frames;
+
+ frames = ExAllocatePoolWithTag(PagedPool, 256 * sizeof(void*), ALLOC_TAG);
+ if (!frames) {
+ ERR("out of memory\n");
+ return FALSE;
+ }
+
+ num_frames = RtlWalkFrameChain(frames, 256, 1);
+
+ for (i = 0; i < num_frames; i++) {
+ // entry->Reserved3[1] appears to be the image size
+ if (frames[i] >= entry->DllBase && (ULONG_PTR)frames[i] <= (ULONG_PTR)entry->DllBase + (ULONG_PTR)entry->Reserved3[1]) {
+ ExFreePool(frames);
+ return TRUE;
+ }
+ }
+
+ ExFreePool(frames);
+ }
+
+ le = le->Flink;
}
-
- IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
- if (top_level)
- IoSetTopLevelIrp(NULL);
-
- FsRtlExitFileSystem();
- return Status;
+ return FALSE;
}
+#endif
-static NTSTATUS STDCALL drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_QUERY_VOLUME_INFORMATION)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_query_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status;
ULONG BytesCopied = 0;
device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level;
-
-#ifndef __REACTOS__
- // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
- // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
- // which prevents UAC from working.
- // FIXME - only lie if we detect that we're being called by mpr.dll
-
- WCHAR* fs_name = L"NTFS";
- ULONG fs_name_len = 4 * sizeof(WCHAR);
-#else
- WCHAR* fs_name = L"Btrfs";
- ULONG fs_name_len = 5 * sizeof(WCHAR);
-#endif
- TRACE("query volume information\n");
-
FsRtlEnterFileSystem();
+
+ TRACE("query volume information\n");
top_level = is_top_level(Irp);
-
+
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_query_volume_information(DeviceObject, Irp);
+ goto end;
+ } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
IrpSp = IoGetCurrentIrpStackLocation(Irp);
-
+
Status = STATUS_NOT_IMPLEMENTED;
-
+
switch (IrpSp->Parameters.QueryVolume.FsInformationClass) {
case FileFsAttributeInformation:
{
FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
BOOL overflow = FALSE;
+#ifndef __REACTOS__
+ WCHAR* fs_name = (Irp->RequestorMode == UserMode && lie_about_fs_type()) ? L"NTFS" : L"Btrfs";
+ ULONG fs_name_len = (ULONG)wcslen(fs_name) * sizeof(WCHAR);
+#else
+ WCHAR* fs_name = L"Btrfs";
+ ULONG fs_name_len = 5 * sizeof(WCHAR);
+#endif
ULONG orig_fs_name_len = fs_name_len;
-
+
TRACE("FileFsAttributeInformation\n");
-
+
if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len) {
if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR))
fs_name_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + sizeof(WCHAR);
else
fs_name_len = 0;
-
+
overflow = TRUE;
}
-
+
data->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH |
FILE_UNICODE_ON_DISK | FILE_NAMED_STREAMS | FILE_SUPPORTS_HARD_LINKS | FILE_PERSISTENT_ACLS |
- FILE_SUPPORTS_REPARSE_POINTS;
+ FILE_SUPPORTS_REPARSE_POINTS | FILE_SUPPORTS_SPARSE_FILES | FILE_SUPPORTS_OBJECT_IDS |
+ FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_BLOCK_REFCOUNTING;
if (Vcb->readonly)
data->FileSystemAttributes |= FILE_READ_ONLY_VOLUME;
-
+
// should also be FILE_FILE_COMPRESSION when supported
data->MaximumComponentNameLength = 255; // FIXME - check
data->FileSystemNameLength = orig_fs_name_len;
RtlCopyMemory(data->FileSystemName, fs_name, fs_name_len);
-
+
BytesCopied = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) - sizeof(WCHAR) + fs_name_len;
Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
break;
}
- case FileFsControlInformation:
- FIXME("STUB: FileFsControlInformation\n");
- break;
-
case FileFsDeviceInformation:
- FIXME("STUB: FileFsDeviceInformation\n");
- break;
+ {
+ FILE_FS_DEVICE_INFORMATION* ffdi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileFsDeviceInformation\n");
+
+ ffdi->DeviceType = FILE_DEVICE_DISK;
+
+ ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+ ffdi->Characteristics = Vcb->Vpb->RealDevice->Characteristics;
+ ExReleaseResourceLite(&Vcb->tree_lock);
+
+ if (Vcb->readonly)
+ ffdi->Characteristics |= FILE_READ_ONLY_DEVICE;
+ else
+ ffdi->Characteristics &= ~FILE_READ_ONLY_DEVICE;
+
+ BytesCopied = sizeof(FILE_FS_DEVICE_INFORMATION);
+ Status = STATUS_SUCCESS;
- case FileFsDriverPathInformation:
- FIXME("STUB: FileFsDriverPathInformation\n");
break;
+ }
case FileFsFullSizeInformation:
{
FILE_FS_FULL_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
- UINT64 totalsize, freespace;
-
+
TRACE("FileFsFullSizeInformation\n");
-
- // FIXME - calculate correctly for RAID
- totalsize = Vcb->superblock.total_bytes / Vcb->superblock.sector_size;
- freespace = (Vcb->superblock.total_bytes - Vcb->superblock.bytes_used) / Vcb->superblock.sector_size;
-
- ffsi->TotalAllocationUnits.QuadPart = totalsize;
- ffsi->ActualAvailableAllocationUnits.QuadPart = freespace;
+
+ calculate_total_space(Vcb, (UINT64*)&ffsi->TotalAllocationUnits.QuadPart, (UINT64*)&ffsi->ActualAvailableAllocationUnits.QuadPart);
ffsi->CallerAvailableAllocationUnits.QuadPart = ffsi->ActualAvailableAllocationUnits.QuadPart;
ffsi->SectorsPerAllocationUnit = 1;
ffsi->BytesPerSector = Vcb->superblock.sector_size;
-
+
BytesCopied = sizeof(FILE_FS_FULL_SIZE_INFORMATION);
Status = STATUS_SUCCESS;
-
+
break;
}
case FileFsObjectIdInformation:
- FIXME("STUB: FileFsObjectIdInformation\n");
+ {
+ FILE_FS_OBJECTID_INFORMATION* ffoi = Irp->AssociatedIrp.SystemBuffer;
+
+ TRACE("FileFsObjectIdInformation\n");
+
+ RtlCopyMemory(ffoi->ObjectId, &Vcb->superblock.uuid.uuid[0], sizeof(UCHAR) * 16);
+ RtlZeroMemory(ffoi->ExtendedInfo, sizeof(ffoi->ExtendedInfo));
+
+ BytesCopied = sizeof(FILE_FS_OBJECTID_INFORMATION);
+ Status = STATUS_SUCCESS;
+
break;
+ }
case FileFsSizeInformation:
{
FILE_FS_SIZE_INFORMATION* ffsi = Irp->AssociatedIrp.SystemBuffer;
- UINT64 totalsize, freespace;
-
+
TRACE("FileFsSizeInformation\n");
-
- // FIXME - calculate correctly for RAID
- // FIXME - is this returning the right free space?
- totalsize = Vcb->superblock.dev_item.num_bytes / Vcb->superblock.sector_size;
- freespace = (Vcb->superblock.dev_item.num_bytes - Vcb->superblock.dev_item.bytes_used) / Vcb->superblock.sector_size;
-
- ffsi->TotalAllocationUnits.QuadPart = totalsize;
- ffsi->AvailableAllocationUnits.QuadPart = freespace;
+
+ calculate_total_space(Vcb, (UINT64*)&ffsi->TotalAllocationUnits.QuadPart, (UINT64*)&ffsi->AvailableAllocationUnits.QuadPart);
ffsi->SectorsPerAllocationUnit = 1;
ffsi->BytesPerSector = Vcb->superblock.sector_size;
-
+
BytesCopied = sizeof(FILE_FS_SIZE_INFORMATION);
Status = STATUS_SUCCESS;
-
+
break;
}
FILE_FS_VOLUME_INFORMATION ffvi;
BOOL overflow = FALSE;
ULONG label_len, orig_label_len;
-
+
TRACE("FileFsVolumeInformation\n");
TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
-
- acquire_tree_lock(Vcb, FALSE);
-
-// orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
- RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+
+ ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
+
+ Status = RtlUTF8ToUnicodeN(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+ if (!NT_SUCCESS(Status)) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ ExReleaseResourceLite(&Vcb->tree_lock);
+ break;
+ }
+
orig_label_len = label_len;
-
+
if (IrpSp->Parameters.QueryVolume.Length < sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len) {
if (IrpSp->Parameters.QueryVolume.Length > sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR))
label_len = IrpSp->Parameters.QueryVolume.Length - sizeof(FILE_FS_VOLUME_INFORMATION) + sizeof(WCHAR);
else
label_len = 0;
-
+
overflow = TRUE;
}
-
+
TRACE("label_len = %u\n", label_len);
-
+
ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
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];
ffvi.VolumeLabelLength = orig_label_len;
ffvi.SupportsObjects = FALSE;
-
+
RtlCopyMemory(data, &ffvi, min(sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR), IrpSp->Parameters.QueryVolume.Length));
-
+
if (label_len > 0) {
ULONG bytecount;
-
-// RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
- RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+
+ Status = RtlUTF8ToUnicodeN(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
+ ERR("RtlUTF8ToUnicodeN returned %08x\n", Status);
+ ExReleaseResourceLite(&Vcb->tree_lock);
+ break;
+ }
+
TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
}
-
- release_tree_lock(Vcb, FALSE);
+
+ ExReleaseResourceLite(&Vcb->tree_lock);
BytesCopied = sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(WCHAR) + label_len;
Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
break;
}
+#ifndef __REACTOS__
+#ifdef _MSC_VER // not in mingw yet
+ case FileFsSectorSizeInformation:
+ {
+ FILE_FS_SECTOR_SIZE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
+
+ data->LogicalBytesPerSector = Vcb->superblock.sector_size;
+ data->PhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
+ data->PhysicalBytesPerSectorForPerformance = Vcb->superblock.sector_size;
+ data->FileSystemEffectivePhysicalBytesPerSectorForAtomicity = Vcb->superblock.sector_size;
+ data->ByteOffsetForSectorAlignment = 0;
+ data->ByteOffsetForPartitionAlignment = 0;
+
+ data->Flags = SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
+
+ if (Vcb->trim && !Vcb->options.no_trim)
+ data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
+
+ BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
+
+ break;
+ }
+#endif
+#endif /* __REACTOS__ */
+
default:
Status = STATUS_INVALID_PARAMETER;
- WARN("unknown FsInformatClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
+ WARN("unknown FsInformationClass %u\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
break;
}
-
-// if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
-// WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
-// BytesCopied = IrpSp->Parameters.QueryVolume.Length;
-// Status = STATUS_BUFFER_OVERFLOW;
-// }
- Irp->IoStatus.Status = Status;
-
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
Irp->IoStatus.Information = 0;
else
Irp->IoStatus.Information = BytesCopied;
-
+
+end:
+ Irp->IoStatus.Status = Status;
+
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
-
- if (top_level)
+
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
- FsRtlExitFileSystem();
-
+
TRACE("query volume information returning %08x\n", Status);
+ FsRtlExitFileSystem();
+
return Status;
}
-static NTSTATUS STDCALL read_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
+_Function_class_(IO_COMPLETION_ROUTINE)
+#ifdef __REACTOS__
+static NTSTATUS NTAPI read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
+#else
+static NTSTATUS read_completion(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ PVOID conptr) {
+#endif
read_context* context = conptr;
-
-// DbgPrint("read_completion\n");
-
+
+ UNUSED(DeviceObject);
+
context->iosb = Irp->IoStatus;
KeSetEvent(&context->Event, 0, FALSE);
-
-// return STATUS_SUCCESS;
+
return STATUS_MORE_PROCESSING_REQUIRED;
}
-// static void test_tree_deletion(device_extension* Vcb) {
-// KEY searchkey/*, endkey*/;
-// traverse_ptr tp, next_tp;
-// root* r;
-//
-// searchkey.obj_id = 0x100;
-// searchkey.obj_type = 0x54;
-// searchkey.offset = 0xca4ab2f5;
-//
-// // endkey.obj_id = 0x100;
-// // endkey.obj_type = 0x60;
-// // endkey.offset = 0x15a;
-//
-// r = Vcb->roots;
-// while (r && r->id != 0x102)
-// r = r->next;
-//
-// if (!r) {
-// ERR("error - could not find root\n");
-// return;
-// }
-//
-// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
-// ERR("error - could not find key\n");
-// return;
-// }
-//
-// while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
-// tp.item->ignore = TRUE;
-// add_to_tree_cache(tc, tp.tree);
-//
-// if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
-// free_traverse_ptr(&tp);
-// tp = next_tp;
-// } else
-// break;
-// }
-//
-// free_traverse_ptr(&tp);
-// }
-
-// static void test_tree_splitting(device_extension* Vcb) {
-// int i;
-//
-// for (i = 0; i < 1000; i++) {
-// char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
-//
-// insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
-// }
-// }
-
-static NTSTATUS STDCALL set_label(device_extension* Vcb, FILE_FS_LABEL_INFORMATION* ffli) {
- ULONG utf8len;
+NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ UINT64 id,
+ _Out_ root** rootptr, _In_ BOOL no_tree, _In_ UINT64 offset, _In_opt_ PIRP Irp) {
NTSTATUS Status;
-
- TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
-
- Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
- if (!NT_SUCCESS(Status))
- goto end;
-
- if (utf8len > MAX_LABEL_SIZE) {
- Status = STATUS_INVALID_VOLUME_LABEL;
- goto end;
- }
-
- // FIXME - check for '/' and '\\' and reject
-
- acquire_tree_lock(Vcb, TRUE);
-
-// utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
-
- Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE * sizeof(WCHAR), &utf8len, ffli->VolumeLabel, ffli->VolumeLabelLength);
- if (!NT_SUCCESS(Status))
- goto release;
-
- if (utf8len < MAX_LABEL_SIZE * sizeof(WCHAR))
- RtlZeroMemory(Vcb->superblock.label + utf8len, (MAX_LABEL_SIZE * sizeof(WCHAR)) - utf8len);
-
-// test_tree_deletion(Vcb); // TESTING
-// test_tree_splitting(Vcb);
-
- Status = consider_write(Vcb);
-
-release:
- release_tree_lock(Vcb, TRUE);
-
-end:
- TRACE("returning %08x\n", Status);
+ root* r;
+ tree* t = NULL;
+ ROOT_ITEM* ri;
+ traverse_ptr tp;
- return Status;
-}
+ r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
+ if (!r) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
-static NTSTATUS STDCALL drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- device_extension* Vcb = DeviceObject->DeviceExtension;
- NTSTATUS Status;
- BOOL top_level;
+ r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
+ if (!r->nonpaged) {
+ ERR("out of memory\n");
+ ExFreePool(r);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- TRACE("set volume information\n");
-
- FsRtlEnterFileSystem();
+ if (!no_tree) {
+ t = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
+ if (!t) {
+ ERR("out of memory\n");
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
- top_level = is_top_level(Irp);
-
- Status = STATUS_NOT_IMPLEMENTED;
-
- if (Vcb->readonly) {
- Status = STATUS_MEDIA_WRITE_PROTECTED;
- goto end;
+ t->is_unique = TRUE;
+ t->uniqueness_determined = TRUE;
+ t->buf = NULL;
}
-
- switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
- case FileFsControlInformation:
- FIXME("STUB: FileFsControlInformation\n");
- break;
- case FileFsLabelInformation:
- TRACE("FileFsLabelInformation\n");
-
- Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
- break;
+ ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
+ if (!ri) {
+ ERR("out of memory\n");
- case FileFsObjectIdInformation:
- FIXME("STUB: FileFsObjectIdInformation\n");
- break;
+ if (t)
+ ExFreePool(t);
- default:
- WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
- break;
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
+ return STATUS_INSUFFICIENT_RESOURCES;
}
-
-end:
- Irp->IoStatus.Status = Status;
- Irp->IoStatus.Information = 0;
- IoCompleteRequest( Irp, IO_NO_INCREMENT );
-
- if (top_level)
- IoSetTopLevelIrp(NULL);
-
- FsRtlExitFileSystem();
+ r->id = id;
+ r->treeholder.address = 0;
+ r->treeholder.generation = Vcb->superblock.generation;
+ r->treeholder.tree = t;
+ r->lastinode = 0;
+ r->dirty = FALSE;
+ r->received = FALSE;
+ r->reserved = NULL;
+ r->parent = 0;
+ r->send_ops = 0;
+ RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
+ r->root_item.num_references = 1;
+ InitializeListHead(&r->fcbs);
- return Status;
-}
+ RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM));
-NTSTATUS delete_dir_item(device_extension* Vcb, root* subvol, UINT64 parinode, UINT32 crc32, PANSI_STRING utf8, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp;
- NTSTATUS Status;
-
- searchkey.obj_id = parinode;
- searchkey.obj_type = TYPE_DIR_ITEM;
- searchkey.offset = crc32;
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
+ // We ask here for a traverse_ptr to the item we're inserting, so we can
+ // copy some of the tree's variables
+
+ Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
+ ERR("insert_tree_item returned %08x\n", Status);
+ ExFreePool(ri);
+
+ if (t)
+ ExFreePool(t);
+
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
return Status;
}
-
- if (!keycmp(&searchkey, &tp.item->key)) {
- if (tp.item->size < sizeof(DIR_ITEM)) {
- 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));
- } else {
- DIR_ITEM* di;
- LONG len;
-
- di = (DIR_ITEM*)tp.item->data;
- len = tp.item->size;
-
- do {
- if (di->n == utf8->Length && RtlCompareMemory(di->name, utf8->Buffer, di->n) == di->n) {
- ULONG newlen = tp.item->size - (sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m);
-
- delete_tree_item(Vcb, &tp, rollback);
-
- if (newlen == 0) {
- TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- } else {
- UINT8 *newdi = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *dioff;
-
- if (!newdi) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- if ((UINT8*)di > tp.item->data) {
- RtlCopyMemory(newdi, tp.item->data, (UINT8*)di - tp.item->data);
- dioff = newdi + ((UINT8*)di - tp.item->data);
- } else {
- dioff = newdi;
- }
-
- if ((UINT8*)&di->name[di->n + di->m] - tp.item->data < tp.item->size)
- RtlCopyMemory(dioff, &di->name[di->n + di->m], tp.item->size - ((UINT8*)&di->name[di->n + di->m] - tp.item->data));
-
- insert_tree_item(Vcb, subvol, parinode, TYPE_DIR_ITEM, crc32, newdi, newlen, NULL, rollback);
- }
-
- break;
- }
-
- len -= sizeof(DIR_ITEM) - sizeof(char) + di->n + di->m;
- di = (DIR_ITEM*)&di->name[di->n + di->m];
- } while (len > 0);
- }
- } else {
- WARN("could not find DIR_ITEM for crc32 %08x\n", crc32);
+
+ ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
+
+ InsertTailList(&Vcb->roots, &r->list_entry);
+
+ if (!no_tree) {
+ RtlZeroMemory(&t->header, sizeof(tree_header));
+ t->header.fs_uuid = tp.tree->header.fs_uuid;
+ t->header.address = 0;
+ t->header.flags = HEADER_FLAG_MIXED_BACKREF | 1; // 1 == "written"? Why does the Linux driver record this?
+ t->header.chunk_tree_uuid = tp.tree->header.chunk_tree_uuid;
+ t->header.generation = Vcb->superblock.generation;
+ t->header.tree_id = id;
+ t->header.num_items = 0;
+ t->header.level = 0;
+
+ t->has_address = FALSE;
+ t->size = 0;
+ t->Vcb = Vcb;
+ t->parent = NULL;
+ t->paritem = NULL;
+ t->root = r;
+
+ InitializeListHead(&t->itemlist);
+
+ t->new_address = 0;
+ t->has_new_address = FALSE;
+ t->updated_extents = FALSE;
+
+ InsertTailList(&Vcb->trees, &t->list_entry);
+ t->list_entry_hash.Flink = NULL;
+
+ t->write = TRUE;
+ Vcb->need_write = TRUE;
}
-
- free_traverse_ptr(&tp);
-
+
+ *rootptr = r;
+
return STATUS_SUCCESS;
}
-NTSTATUS delete_inode_ref(device_extension* Vcb, root* subvol, UINT64 inode, UINT64 parinode, PANSI_STRING utf8, UINT64* index, LIST_ENTRY* rollback) {
- KEY searchkey;
- traverse_ptr tp;
- BOOL changed = FALSE;
+static NTSTATUS set_label(_In_ device_extension* Vcb, _In_ FILE_FS_LABEL_INFORMATION* ffli) {
+ ULONG utf8len;
NTSTATUS Status;
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_INODE_REF;
- searchkey.offset = parinode;
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (!keycmp(&searchkey, &tp.item->key)) {
- if (tp.item->size < sizeof(INODE_REF)) {
- 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));
- } else {
- INODE_REF* ir;
- ULONG len;
-
- ir = (INODE_REF*)tp.item->data;
- len = tp.item->size;
-
- do {
- ULONG itemlen;
-
- if (len < sizeof(INODE_REF) || len < sizeof(INODE_REF) - 1 + ir->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- itemlen = sizeof(INODE_REF) - sizeof(char) + ir->n;
-
- if (ir->n == utf8->Length && RtlCompareMemory(ir->name, utf8->Buffer, ir->n) == ir->n) {
- ULONG newlen = tp.item->size - itemlen;
-
- delete_tree_item(Vcb, &tp, rollback);
- changed = TRUE;
-
- if (newlen == 0) {
- TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- } else {
- UINT8 *newir = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *iroff;
-
- if (!newir) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- if ((UINT8*)ir > tp.item->data) {
- RtlCopyMemory(newir, tp.item->data, (UINT8*)ir - tp.item->data);
- iroff = newir + ((UINT8*)ir - tp.item->data);
- } else {
- iroff = newir;
- }
-
- if ((UINT8*)&ir->name[ir->n] - tp.item->data < tp.item->size)
- RtlCopyMemory(iroff, &ir->name[ir->n], tp.item->size - ((UINT8*)&ir->name[ir->n] - tp.item->data));
-
- insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newir, newlen, NULL, rollback);
- }
-
- if (index)
- *index = ir->index;
-
- break;
- }
-
- if (len > itemlen) {
- len -= itemlen;
- ir = (INODE_REF*)&ir->name[ir->n];
- } else
- break;
- } while (len > 0);
-
- if (!changed) {
- WARN("found INODE_REF entry, but couldn't find filename\n");
- }
+ ULONG vollen, i;
+
+ TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
+
+ vollen = ffli->VolumeLabelLength;
+
+ for (i = 0; i < ffli->VolumeLabelLength / sizeof(WCHAR); i++) {
+ if (ffli->VolumeLabel[i] == 0) {
+ vollen = i * sizeof(WCHAR);
+ break;
+ } else if (ffli->VolumeLabel[i] == '/' || ffli->VolumeLabel[i] == '\\') {
+ Status = STATUS_INVALID_VOLUME_LABEL;
+ goto end;
}
- } else {
- WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey.obj_id, searchkey.offset);
}
-
- free_traverse_ptr(&tp);
-
- if (changed)
- return STATUS_SUCCESS;
-
- if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF))
- return STATUS_INTERNAL_ERROR;
-
- searchkey.obj_id = inode;
- searchkey.obj_type = TYPE_INODE_EXTREF;
- searchkey.offset = calc_crc32c((UINT32)parinode, (UINT8*)utf8->Buffer, utf8->Length);
-
- Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (!keycmp(&searchkey, &tp.item->key)) {
- if (tp.item->size < sizeof(INODE_EXTREF)) {
- 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));
- } else {
- INODE_EXTREF* ier;
- ULONG len;
-
- ier = (INODE_EXTREF*)tp.item->data;
- len = tp.item->size;
-
- do {
- ULONG itemlen;
-
- if (len < sizeof(INODE_EXTREF) || len < sizeof(INODE_EXTREF) - 1 + ier->n) {
- ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- break;
- }
-
- itemlen = sizeof(INODE_EXTREF) - sizeof(char) + ier->n;
-
- if (ier->dir == parinode && ier->n == utf8->Length && RtlCompareMemory(ier->name, utf8->Buffer, ier->n) == ier->n) {
- ULONG newlen = tp.item->size - itemlen;
-
- delete_tree_item(Vcb, &tp, rollback);
- changed = TRUE;
-
- if (newlen == 0) {
- TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
- } else {
- UINT8 *newier = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *ieroff;
-
- if (!newier) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- if ((UINT8*)ier > tp.item->data) {
- RtlCopyMemory(newier, tp.item->data, (UINT8*)ier - tp.item->data);
- ieroff = newier + ((UINT8*)ier - tp.item->data);
- } else {
- ieroff = newier;
- }
-
- if ((UINT8*)&ier->name[ier->n] - tp.item->data < tp.item->size)
- RtlCopyMemory(ieroff, &ier->name[ier->n], tp.item->size - ((UINT8*)&ier->name[ier->n] - tp.item->data));
-
- insert_tree_item(Vcb, subvol, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newier, newlen, NULL, rollback);
- }
-
- if (index)
- *index = ier->index;
-
- break;
- }
-
- if (len > itemlen) {
- len -= itemlen;
- ier = (INODE_EXTREF*)&ier->name[ier->n];
- } else
- break;
- } while (len > 0);
- }
+
+ if (vollen == 0) {
+ utf8len = 0;
} else {
- WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32)searchkey.offset);
+ Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, ffli->VolumeLabel, vollen);
+ if (!NT_SUCCESS(Status))
+ goto end;
+
+ if (utf8len > MAX_LABEL_SIZE) {
+ Status = STATUS_INVALID_VOLUME_LABEL;
+ goto end;
+ }
}
-
- free_traverse_ptr(&tp);
-
- return changed ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
+
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+
+ if (utf8len > 0) {
+ Status = RtlUnicodeToUTF8N((PCHAR)&Vcb->superblock.label, MAX_LABEL_SIZE, &utf8len, ffli->VolumeLabel, vollen);
+ if (!NT_SUCCESS(Status))
+ goto release;
+ } else
+ Status = STATUS_SUCCESS;
+
+ if (utf8len < MAX_LABEL_SIZE)
+ RtlZeroMemory(Vcb->superblock.label + utf8len, MAX_LABEL_SIZE - utf8len);
+
+ Vcb->need_write = TRUE;
+
+release:
+ ExReleaseResourceLite(&Vcb->tree_lock);
+
+end:
+ TRACE("returning %08x\n", Status);
+
+ return Status;
}
-NTSTATUS delete_fcb(fcb* fcb, PFILE_OBJECT FileObject, LIST_ENTRY* rollback) {
- ULONG bytecount;
+_Dispatch_type_(IRP_MJ_SET_VOLUME_INFORMATION)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_set_volume_information(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ device_extension* Vcb = DeviceObject->DeviceExtension;
NTSTATUS Status;
- char* utf8 = NULL;
- UINT32 crc32;
- KEY searchkey;
- traverse_ptr tp, tp2;
- UINT64 parinode, index;
- INODE_ITEM *ii, *dirii;
- LARGE_INTEGER time;
- BTRFS_TIME now;
- LIST_ENTRY changed_sector_list;
-#ifdef _DEBUG
- LARGE_INTEGER freq, time1, time2;
-#endif
-
- // FIXME - throw error if try to delete subvol root(?)
-
- // FIXME - delete all children if deleting directory
-
- if (fcb->deleted) {
- WARN("trying to delete already-deleted file\n");
- return STATUS_SUCCESS;
+ BOOL top_level;
+
+ FsRtlEnterFileSystem();
+
+ TRACE("set volume information\n");
+
+ top_level = is_top_level(Irp);
+
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_set_volume_information(DeviceObject, Irp);
+ goto end;
+ } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
}
-
- if (!fcb->par) {
- ERR("error - trying to delete root FCB\n");
- return STATUS_INTERNAL_ERROR;
+
+ Status = STATUS_NOT_IMPLEMENTED;
+
+ if (Vcb->readonly) {
+ Status = STATUS_MEDIA_WRITE_PROTECTED;
+ goto end;
}
-
-#ifdef _DEBUG
- time1 = KeQueryPerformanceCounter(&freq);
-#endif
-
- KeQuerySystemTime(&time);
- win_time_to_unix(time, &now);
-
- if (fcb->ads) {
- char* s;
- TRACE("deleting ADS\n");
-
- s = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.Length + 1, ALLOC_TAG);
- if (!s) {
- ERR("out of memory\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto exit;
- }
-
- RtlCopyMemory(s, fcb->adsxattr.Buffer, fcb->adsxattr.Length);
- s[fcb->adsxattr.Length] = 0;
-
- if (!delete_xattr(fcb->Vcb, fcb->par->subvol, fcb->par->inode, s, fcb->adshash, rollback)) {
- ERR("failed to delete xattr %s\n", s);
- }
-
- ExFreePool(s);
-
- fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
- fcb->par->inode_item.sequence++;
- fcb->par->inode_item.st_ctime = now;
-
- searchkey.obj_id = fcb->par->inode;
- searchkey.obj_type = TYPE_INODE_ITEM;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(fcb->Vcb, fcb->par->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto exit;
- }
-
- if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->par->inode, fcb->par->subvol->id);
- free_traverse_ptr(&tp);
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
- }
- ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
- if (!ii) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto exit;
- }
-
- RtlCopyMemory(ii, &fcb->par->inode_item, sizeof(INODE_ITEM));
- delete_tree_item(fcb->Vcb, &tp, rollback);
-
- insert_tree_item(fcb->Vcb, fcb->par->subvol, searchkey.obj_id, searchkey.obj_type, 0, ii, sizeof(INODE_ITEM), NULL, rollback);
-
- free_traverse_ptr(&tp);
-
- fcb->par->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
- fcb->par->subvol->root_item.ctime = now;
-
- goto success;
- }
-
- Status = RtlUnicodeToUTF8N(NULL, 0, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
- if (!NT_SUCCESS(Status)) {
- ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status);
- return Status;
+ if (Vcb->removing || Vcb->locked) {
+ Status = STATUS_ACCESS_DENIED;
+ goto end;
}
-
- utf8 = ExAllocatePoolWithTag(PagedPool, bytecount + 1, ALLOC_TAG);
- if (!utf8) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+
+ switch (IrpSp->Parameters.SetVolume.FsInformationClass) {
+ case FileFsControlInformation:
+ FIXME("STUB: FileFsControlInformation\n");
+ break;
+
+ case FileFsLabelInformation:
+ TRACE("FileFsLabelInformation\n");
+
+ Status = set_label(Vcb, Irp->AssociatedIrp.SystemBuffer);
+ break;
+
+ case FileFsObjectIdInformation:
+ FIXME("STUB: FileFsObjectIdInformation\n");
+ break;
+
+ default:
+ WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp->Parameters.SetVolume.FsInformationClass);
+ break;
}
-
- RtlUnicodeToUTF8N(utf8, bytecount, &bytecount, fcb->filepart.Buffer, fcb->filepart.Length);
- utf8[bytecount] = 0;
-
- crc32 = calc_crc32c(0xfffffffe, (UINT8*)utf8, bytecount);
- TRACE("deleting %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
-
- if (fcb->par->subvol == fcb->subvol)
- parinode = fcb->par->inode;
- else
- parinode = SUBVOL_ROOT_INODE;
-
- // delete DIR_ITEM (0x54)
-
- Status = delete_dir_item(fcb->Vcb, fcb->subvol, parinode, crc32, &fcb->utf8, rollback);
+end:
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+
+ TRACE("returning %08x\n", Status);
+
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+
+ if (top_level)
+ IoSetTopLevelIrp(NULL);
+
+ FsRtlExitFileSystem();
+
+ return Status;
+}
+
+static WCHAR* file_desc_fcb(_In_ fcb* fcb) {
+ char s[60];
+ NTSTATUS Status;
+ UNICODE_STRING us;
+ ANSI_STRING as;
+
+ if (fcb->debug_desc)
+ return fcb->debug_desc;
+
+ if (fcb == fcb->Vcb->volume_fcb)
+ return L"volume FCB";
+
+ fcb->debug_desc = ExAllocatePoolWithTag(PagedPool, 60 * sizeof(WCHAR), ALLOC_TAG);
+ if (!fcb->debug_desc)
+ return L"(memory error)";
+
+ // I know this is pretty hackish...
+ // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
+ // without the CRT, which breaks drivers.
+
+ sprintf(s, "subvol %x, inode %x", (UINT32)fcb->subvol->id, (UINT32)fcb->inode);
+
+ as.Buffer = s;
+ as.Length = as.MaximumLength = (USHORT)strlen(s);
+
+ us.Buffer = fcb->debug_desc;
+ us.MaximumLength = 60 * sizeof(WCHAR);
+ us.Length = 0;
+
+ Status = RtlAnsiStringToUnicodeString(&us, &as, FALSE);
+ if (!NT_SUCCESS(Status))
+ return L"(RtlAnsiStringToUnicodeString error)";
+
+ us.Buffer[us.Length / sizeof(WCHAR)] = 0;
+
+ return fcb->debug_desc;
+}
+
+WCHAR* file_desc_fileref(_In_ file_ref* fileref) {
+ NTSTATUS Status;
+ UNICODE_STRING fn;
+ ULONG reqlen;
+
+ if (fileref->debug_desc)
+ return fileref->debug_desc;
+
+ fn.Length = fn.MaximumLength = 0;
+ Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+ if (Status != STATUS_BUFFER_OVERFLOW)
+ return L"ERROR";
+
+ if (reqlen > 0xffff - sizeof(WCHAR))
+ return L"(too long)";
+
+ fileref->debug_desc = ExAllocatePoolWithTag(PagedPool, reqlen + sizeof(WCHAR), ALLOC_TAG);
+ if (!fileref->debug_desc)
+ return L"(memory error)";
+
+ fn.Buffer = fileref->debug_desc;
+ fn.Length = 0;
+ fn.MaximumLength = (USHORT)(reqlen + sizeof(WCHAR));
+
+ Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
if (!NT_SUCCESS(Status)) {
- ERR("delete_dir_item returned %08x\n", Status);
- return Status;
+ ExFreePool(fileref->debug_desc);
+ fileref->debug_desc = NULL;
+ return L"ERROR";
}
-
- // delete INODE_REF (0xc)
-
- index = 0;
-
- Status = delete_inode_ref(fcb->Vcb, fcb->subvol, fcb->inode, parinode, &fcb->utf8, &index, rollback);
-
- // delete DIR_INDEX (0x60)
-
- searchkey.obj_id = parinode;
- searchkey.obj_type = TYPE_DIR_INDEX;
- searchkey.offset = index;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
+
+ fileref->debug_desc[fn.Length / sizeof(WCHAR)] = 0;
+
+ return fileref->debug_desc;
+}
+
+_Ret_z_
+WCHAR* file_desc(_In_ PFILE_OBJECT FileObject) {
+ fcb* fcb = FileObject->FsContext;
+ ccb* ccb = FileObject->FsContext2;
+ file_ref* fileref = ccb ? ccb->fileref : NULL;
+
+ if (fileref)
+ return file_desc_fileref(fileref);
+ else
+ return file_desc_fcb(fcb);
+}
+
+void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
+ UNICODE_STRING fn;
+ NTSTATUS Status;
+ ULONG reqlen;
+ USHORT name_offset;
+ fcb* fcb = fileref->fcb;
+
+ fn.Length = fn.MaximumLength = 0;
+ Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
+ if (Status != STATUS_BUFFER_OVERFLOW) {
+ ERR("fileref_get_filename returned %08x\n", Status);
+ return;
}
-
- if (!keycmp(&searchkey, &tp.item->key)) {
- delete_tree_item(fcb->Vcb, &tp, rollback);
- TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ if (reqlen > 0xffff) {
+ WARN("reqlen was too long for FsRtlNotifyFilterReportChange\n");
+ return;
}
-
- // delete INODE_ITEM (0x1)
-
- searchkey.obj_id = fcb->inode;
- searchkey.obj_type = TYPE_INODE_ITEM;
- searchkey.offset = 0;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp2, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
- goto exit;
+
+ fn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);
+ if (!fn.Buffer) {
+ ERR("out of memory\n");
+ return;
}
-
- free_traverse_ptr(&tp);
- tp = tp2;
-
- if (keycmp(&searchkey, &tp.item->key)) {
- ERR("error - INODE_ITEM not found\n");
- free_traverse_ptr(&tp);
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
+
+ fn.MaximumLength = (USHORT)reqlen;
+ fn.Length = 0;
+
+ Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
+ if (!NT_SUCCESS(Status)) {
+ ERR("fileref_get_filename returned %08x\n", Status);
+ ExFreePool(fn.Buffer);
+ return;
}
-
- if (tp.item->size < sizeof(INODE_ITEM)) {
- 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));
- free_traverse_ptr(&tp);
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
+
+ FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, name_offset,
+ (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
+ ExFreePool(fn.Buffer);
+}
+
+void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG action, _In_opt_ PUNICODE_STRING stream) {
+ fcb* fcb = fileref->fcb;
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+
+ // no point looking for hardlinks if st_nlink == 1
+ if (fileref->fcb->inode_item.st_nlink == 1) {
+ send_notification_fileref(fileref, filter_match, action, stream);
+ return;
}
-
- ii = (INODE_ITEM*)tp.item->data;
- TRACE("nlink = %u\n", ii->st_nlink);
-
- if (ii->st_nlink > 1) {
- INODE_ITEM* newii;
-
- newii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
- if (!newii) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto exit;
- }
-
- RtlCopyMemory(newii, ii, sizeof(INODE_ITEM));
- newii->st_nlink--;
- newii->transid = fcb->Vcb->superblock.generation;
- newii->sequence++;
- newii->st_ctime = now;
-
- TRACE("replacing (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- delete_tree_item(fcb->Vcb, &tp, rollback);
-
- 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))
- ERR("error - failed to insert item\n");
-
- free_traverse_ptr(&tp);
-
- goto success2;
- }
-
- delete_tree_item(fcb->Vcb, &tp, rollback);
- TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- // delete XATTR_ITEM (0x18)
-
- while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE)) {
- free_traverse_ptr(&tp);
- tp = tp2;
-
- if (tp.item->key.obj_id == fcb->inode) {
- // FIXME - do metadata thing here too?
- if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
- delete_tree_item(fcb->Vcb, &tp, rollback);
- TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
+
+ acquire_fcb_lock_exclusive(fcb->Vcb);
+
+ le = fcb->hardlinks.Flink;
+ while (le != &fcb->hardlinks) {
+ hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+ file_ref* parfr;
+
+ Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
+
+ if (!NT_SUCCESS(Status))
+ ERR("open_fileref_by_inode returned %08x\n", Status);
+ else if (!parfr->deleted) {
+ UNICODE_STRING fn;
+ ULONG pathlen;
+
+ fn.Length = fn.MaximumLength = 0;
+ Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
+ if (Status != STATUS_BUFFER_OVERFLOW) {
+ ERR("fileref_get_filename returned %08x\n", Status);
+ free_fileref(fcb->Vcb, parfr);
+ break;
}
- } else
- break;
- }
-
- free_traverse_ptr(&tp);
-
- // excise extents
-
- InitializeListHead(&changed_sector_list);
-
- if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) {
- Status = excise_extents(fcb->Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), &changed_sector_list, rollback);
- if (!NT_SUCCESS(Status)) {
- ERR("excise_extents returned %08x\n", Status);
- goto exit;
+
+ if (parfr != fcb->Vcb->root_fileref)
+ pathlen += sizeof(WCHAR);
+
+ if (pathlen + hl->name.Length > 0xffff) {
+ WARN("pathlen + hl->name.Length was too long for FsRtlNotifyFilterReportChange\n");
+ free_fileref(fcb->Vcb, parfr);
+ break;
+ }
+
+ fn.MaximumLength = (USHORT)(pathlen + hl->name.Length);
+ fn.Buffer = ExAllocatePoolWithTag(PagedPool, fn.MaximumLength, ALLOC_TAG);
+ if (!fn.Buffer) {
+ ERR("out of memory\n");
+ free_fileref(fcb->Vcb, parfr);
+ break;
+ }
+
+ Status = fileref_get_filename(parfr, &fn, NULL, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("fileref_get_filename returned %08x\n", Status);
+ free_fileref(fcb->Vcb, parfr);
+ ExFreePool(fn.Buffer);
+ break;
+ }
+
+ if (parfr != fcb->Vcb->root_fileref) {
+ fn.Buffer[(pathlen / sizeof(WCHAR)) - 1] = '\\';
+ fn.Length += sizeof(WCHAR);
+ }
+
+ RtlCopyMemory(&fn.Buffer[pathlen / sizeof(WCHAR)], hl->name.Buffer, hl->name.Length);
+ fn.Length += hl->name.Length;
+
+ FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fn, (USHORT)pathlen,
+ (PSTRING)stream, NULL, filter_match, action, NULL, NULL);
+
+ ExFreePool(fn.Buffer);
+
+ free_fileref(fcb->Vcb, parfr);
}
-
- if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
- update_checksum_tree(fcb->Vcb, &changed_sector_list, rollback);
- }
-
-success2:
- // update INODE_ITEM of parent
-
- searchkey.obj_id = parinode;
- searchkey.obj_type = TYPE_INODE_ITEM;
- searchkey.offset = 0;
-
- Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_tree returned %08x\n", Status);
- goto exit;
+
+ le = le->Flink;
}
-
- if (keycmp(&searchkey, &tp.item->key)) {
- ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode, fcb->subvol->id);
- free_traverse_ptr(&tp);
- Status = STATUS_INTERNAL_ERROR;
- goto exit;
+
+ release_fcb_lock(fcb->Vcb);
+}
+
+void mark_fcb_dirty(_In_ fcb* fcb) {
+ if (!fcb->dirty) {
+#ifdef DEBUG_FCB_REFCOUNTS
+ LONG rc;
+#endif
+ fcb->dirty = TRUE;
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ rc = InterlockedIncrement(&fcb->refcount);
+ WARN("fcb %p: refcount now %i\n", fcb, rc);
+#else
+ InterlockedIncrement(&fcb->refcount);
+#endif
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, TRUE);
+ InsertTailList(&fcb->Vcb->dirty_fcbs, &fcb->list_entry_dirty);
+ ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock);
}
-
- TRACE("fcb->par->inode_item.st_size was %llx\n", fcb->par->inode_item.st_size);
- fcb->par->inode_item.st_size -= bytecount * 2;
- TRACE("fcb->par->inode_item.st_size now %llx\n", fcb->par->inode_item.st_size);
- fcb->par->inode_item.transid = fcb->Vcb->superblock.generation;
- fcb->par->inode_item.sequence++;
- fcb->par->inode_item.st_ctime = now;
- fcb->par->inode_item.st_mtime = now;
-
- dirii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
- if (!dirii) {
- ERR("out of memory\n");
- free_traverse_ptr(&tp);
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto exit;
+
+ fcb->Vcb->need_write = TRUE;
+}
+
+void mark_fileref_dirty(_In_ file_ref* fileref) {
+ if (!fileref->dirty) {
+ fileref->dirty = TRUE;
+ increase_fileref_refcount(fileref);
+
+ ExAcquireResourceExclusiveLite(&fileref->fcb->Vcb->dirty_filerefs_lock, TRUE);
+ InsertTailList(&fileref->fcb->Vcb->dirty_filerefs, &fileref->list_entry_dirty);
+ ExReleaseResourceLite(&fileref->fcb->Vcb->dirty_filerefs_lock);
}
-
- RtlCopyMemory(dirii, &fcb->par->inode_item, sizeof(INODE_ITEM));
- delete_tree_item(fcb->Vcb, &tp, rollback);
-
- insert_tree_item(fcb->Vcb, fcb->subvol, searchkey.obj_id, searchkey.obj_type, searchkey.offset, dirii, sizeof(INODE_ITEM), NULL, rollback);
-
- free_traverse_ptr(&tp);
-
- fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
- fcb->subvol->root_item.ctime = now;
-
-success:
- consider_write(fcb->Vcb);
-
- fcb->deleted = TRUE;
-
- fcb->Header.AllocationSize.QuadPart = 0;
- fcb->Header.FileSize.QuadPart = 0;
- fcb->Header.ValidDataLength.QuadPart = 0;
-
- if (FileObject && FileObject->PrivateCacheMap) {
- CC_FILE_SIZES ccfs;
-
- ccfs.AllocationSize = fcb->Header.AllocationSize;
- ccfs.FileSize = fcb->Header.FileSize;
- ccfs.ValidDataLength = fcb->Header.ValidDataLength;
-
- CcSetFileSizes(FileObject, &ccfs);
- }
-
- // FIXME - set deleted flag of any open FCBs for ADS
-
- TRACE("sending notification for deletion of %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
-
- FsRtlNotifyFullReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&fcb->full_filename, fcb->name_offset * sizeof(WCHAR), NULL, NULL,
- fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
- FILE_ACTION_REMOVED, NULL);
-
-#ifdef _DEBUG
- time2 = KeQueryPerformanceCounter(NULL);
-#endif
-
- TRACE("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
-
- Status = STATUS_SUCCESS;
-
-exit:
- if (utf8)
- ExFreePool(utf8);
-
- return Status;
+
+ fileref->fcb->Vcb->need_write = TRUE;
}
-void _free_fcb(fcb* fcb, const char* func, const char* file, unsigned int line) {
- ULONG rc;
-
+#ifdef DEBUG_FCB_REFCOUNTS
+void _free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb, _In_ const char* func) {
+#else
+void free_fcb(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ fcb* fcb) {
+#endif
+ LONG rc;
+
rc = InterlockedDecrement(&fcb->refcount);
-
+
#ifdef DEBUG_FCB_REFCOUNTS
-// WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
#ifdef DEBUG_LONG_MESSAGES
- _debug_message(func, 1, file, line, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
#else
- _debug_message(func, 1, "fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
+ ERR("fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb, rc, fcb->subvol ? fcb->subvol->id : 0, fcb->inode);
#endif
#endif
-
+
if (rc > 0)
return;
-
- ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
- if (fcb->filepart.Buffer)
- RtlFreeUnicodeString(&fcb->filepart);
-
+ if (fcb->list_entry.Flink)
+ RemoveEntryList(&fcb->list_entry);
+
+ if (fcb->list_entry_all.Flink)
+ RemoveEntryList(&fcb->list_entry_all);
+
ExDeleteResourceLite(&fcb->nonpaged->resource);
ExDeleteResourceLite(&fcb->nonpaged->paging_resource);
- ExFreePool(fcb->nonpaged);
-
- if (fcb->par/* && fcb->par != fcb->par->Vcb->root_fcb*/) {
- RemoveEntryList(&fcb->list_entry);
- _free_fcb(fcb->par, func, file, line);
- }
-
- if (fcb->prev)
- fcb->prev->next = fcb->next;
-
- if (fcb->next)
- fcb->next->prev = fcb->prev;
-
- if (fcb->Vcb->fcbs == fcb)
- fcb->Vcb->fcbs = fcb->next;
-
- if (fcb->full_filename.Buffer)
- ExFreePool(fcb->full_filename.Buffer);
-
+ ExDeleteResourceLite(&fcb->nonpaged->dir_children_lock);
+
+ ExFreeToNPagedLookasideList(&Vcb->fcb_np_lookaside, fcb->nonpaged);
+
if (fcb->sd)
ExFreePool(fcb->sd);
-
+
if (fcb->adsxattr.Buffer)
ExFreePool(fcb->adsxattr.Buffer);
-
- if (fcb->utf8.Buffer)
- ExFreePool(fcb->utf8.Buffer);
-
+
+ if (fcb->reparse_xattr.Buffer)
+ ExFreePool(fcb->reparse_xattr.Buffer);
+
+ if (fcb->ea_xattr.Buffer)
+ ExFreePool(fcb->ea_xattr.Buffer);
+
+ if (fcb->adsdata.Buffer)
+ ExFreePool(fcb->adsdata.Buffer);
+
+ if (fcb->debug_desc)
+ ExFreePool(fcb->debug_desc);
+
+ while (!IsListEmpty(&fcb->extents)) {
+ LIST_ENTRY* le = RemoveHeadList(&fcb->extents);
+ extent* ext = CONTAINING_RECORD(le, extent, list_entry);
+
+ if (ext->csum)
+ ExFreePool(ext->csum);
+
+ ExFreePool(ext);
+ }
+
+ while (!IsListEmpty(&fcb->hardlinks)) {
+ LIST_ENTRY* le = RemoveHeadList(&fcb->hardlinks);
+ hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+
+ if (hl->name.Buffer)
+ ExFreePool(hl->name.Buffer);
+
+ if (hl->utf8.Buffer)
+ ExFreePool(hl->utf8.Buffer);
+
+ ExFreePool(hl);
+ }
+
+ while (!IsListEmpty(&fcb->xattrs)) {
+ xattr* xa = CONTAINING_RECORD(RemoveHeadList(&fcb->xattrs), xattr, list_entry);
+
+ ExFreePool(xa);
+ }
+
+ while (!IsListEmpty(&fcb->dir_children_index)) {
+ LIST_ENTRY* le = RemoveHeadList(&fcb->dir_children_index);
+ dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index);
+
+ ExFreePool(dc->utf8.Buffer);
+ ExFreePool(dc->name.Buffer);
+ ExFreePool(dc->name_uc.Buffer);
+ ExFreePool(dc);
+ }
+
+ if (fcb->hash_ptrs)
+ ExFreePool(fcb->hash_ptrs);
+
+ if (fcb->hash_ptrs_uc)
+ ExFreePool(fcb->hash_ptrs_uc);
+
FsRtlUninitializeFileLock(&fcb->lock);
-
- ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
-
- ExFreePool(fcb);
+
+ if (fcb->pool_type == NonPagedPool)
+ ExFreePool(fcb);
+ else
+ ExFreeToPagedLookasideList(&Vcb->fcb_lookaside, fcb);
+
#ifdef DEBUG_FCB_REFCOUNTS
#ifdef DEBUG_LONG_MESSAGES
- _debug_message(func, 1, file, line, "freeing fcb %p\n", fcb);
+ _debug_message(func, file, line, "freeing fcb %p\n", fcb);
#else
- _debug_message(func, 1, "freeing fcb %p\n", fcb);
+ _debug_message(func, "freeing fcb %p\n", fcb);
+#endif
+#endif
+}
+
+void free_fileref(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _Inout_ file_ref* fr) {
+ LONG rc;
+
+ rc = InterlockedDecrement(&fr->refcount);
+
+#ifdef DEBUG_FCB_REFCOUNTS
+ ERR("fileref %p: refcount now %i\n", fr, rc);
#endif
+
+#ifdef _DEBUG
+ if (rc < 0) {
+ ERR("fileref %p: refcount now %i\n", fr, rc);
+ int3;
+ }
#endif
+
+ if (rc > 0)
+ return;
+
+ if (fr->parent)
+ ExAcquireResourceExclusiveLite(&fr->parent->nonpaged->children_lock, TRUE);
+
+ // FIXME - do we need a file_ref lock?
+
+ // FIXME - do delete if needed
+
+ if (fr->debug_desc)
+ ExFreePool(fr->debug_desc);
+
+ ExDeleteResourceLite(&fr->nonpaged->children_lock);
+ ExDeleteResourceLite(&fr->nonpaged->fileref_lock);
+
+ ExFreeToNPagedLookasideList(&Vcb->fileref_np_lookaside, fr->nonpaged);
+
+ // FIXME - throw error if children not empty
+
+ if (fr->fcb->fileref == fr)
+ fr->fcb->fileref = NULL;
+
+ if (fr->dc) {
+ if (fr->fcb->ads)
+ fr->dc->size = fr->fcb->adsdata.Length;
+
+ fr->dc->fileref = NULL;
+ }
+
+ if (fr->list_entry.Flink)
+ RemoveEntryList(&fr->list_entry);
+
+ if (fr->parent) {
+ ExReleaseResourceLite(&fr->parent->nonpaged->children_lock);
+ free_fileref(Vcb, fr->parent);
+ }
+
+ free_fcb(Vcb, fr->fcb);
+
+ ExFreeToPagedLookasideList(&Vcb->fileref_lookaside, fr);
+}
+
+static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP Irp) {
+ fcb* fcb;
+ ccb* ccb;
+ file_ref* fileref = NULL;
+ LONG open_files;
+ device_extension* Vcb;
+
+ UNUSED(Irp);
+
+ TRACE("FileObject = %p\n", FileObject);
+
+ fcb = FileObject->FsContext;
+ if (!fcb) {
+ TRACE("FCB was NULL, returning success\n");
+ return STATUS_SUCCESS;
+ }
+
+ open_files = InterlockedDecrement(&fcb->Vcb->open_files);
+
+ ccb = FileObject->FsContext2;
+
+ TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject), fcb);
+
+ // FIXME - make sure notification gets sent if file is being deleted
+
+ if (ccb) {
+ if (ccb->query_string.Buffer)
+ RtlFreeUnicodeString(&ccb->query_string);
+
+ if (ccb->filename.Buffer)
+ ExFreePool(ccb->filename.Buffer);
+
+ // FIXME - use refcounts for fileref
+ fileref = ccb->fileref;
+
+ if (fcb->Vcb->running_sends > 0) {
+ BOOL send_cancelled = FALSE;
+
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, TRUE);
+
+ if (ccb->send) {
+ ccb->send->cancelling = TRUE;
+ send_cancelled = TRUE;
+ KeSetEvent(&ccb->send->cleared_event, 0, FALSE);
+ }
+
+ ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
+
+ if (send_cancelled) {
+ while (ccb->send) {
+ ExAcquireResourceExclusiveLite(&fcb->Vcb->send_load_lock, TRUE);
+ ExReleaseResourceLite(&fcb->Vcb->send_load_lock);
+ }
+ }
+ }
+
+ ExFreePool(ccb);
+ }
+
+ CcUninitializeCacheMap(FileObject, NULL, NULL);
+
+ if (open_files == 0 && fcb->Vcb->removing) {
+ uninit(fcb->Vcb, FALSE);
+ return STATUS_SUCCESS;
+ }
+
+ if (!(fcb->Vcb->Vpb->Flags & VPB_MOUNTED))
+ return STATUS_SUCCESS;
+
+ Vcb = fcb->Vcb;
+
+ acquire_fcb_lock_exclusive(Vcb);
+
+ if (fileref)
+ free_fileref(fcb->Vcb, fileref);
+ else
+ free_fcb(Vcb, fcb);
+
+ release_fcb_lock(Vcb);
+
+ return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL close_file(device_extension* Vcb, PFILE_OBJECT FileObject) {
- fcb* fcb;
- ccb* ccb;
-
- TRACE("FileObject = %p\n", FileObject);
-
- fcb = FileObject->FsContext;
- if (!fcb) {
- TRACE("FCB was NULL, returning success\n");
- return STATUS_SUCCESS;
+void uninit(_In_ device_extension* Vcb, _In_ BOOL flush) {
+ UINT64 i;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+ LARGE_INTEGER time;
+
+ if (!Vcb->removing) {
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+ Vcb->removing = TRUE;
+ ExReleaseResourceLite(&Vcb->tree_lock);
+ }
+
+ RemoveEntryList(&Vcb->list_entry);
+
+ if (Vcb->balance.thread) {
+ Vcb->balance.paused = FALSE;
+ Vcb->balance.stopping = TRUE;
+ KeSetEvent(&Vcb->balance.event, 0, FALSE);
+ KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode, FALSE, NULL);
+ }
+
+ if (Vcb->scrub.thread) {
+ Vcb->scrub.paused = FALSE;
+ Vcb->scrub.stopping = TRUE;
+ KeSetEvent(&Vcb->scrub.event, 0, FALSE);
+ KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode, FALSE, NULL);
+ }
+
+ if (Vcb->running_sends != 0) {
+ BOOL send_cancelled = FALSE;
+
+ ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
+
+ le = Vcb->send_ops.Flink;
+ while (le != &Vcb->send_ops) {
+ send_info* send = CONTAINING_RECORD(le, send_info, list_entry);
+
+ if (!send->cancelling) {
+ send->cancelling = TRUE;
+ send_cancelled = TRUE;
+ send->ccb = NULL;
+ KeSetEvent(&send->cleared_event, 0, FALSE);
+ }
+
+ le = le->Flink;
+ }
+
+ ExReleaseResourceLite(&Vcb->send_load_lock);
+
+ if (send_cancelled) {
+ while (Vcb->running_sends != 0) {
+ ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
+ ExReleaseResourceLite(&Vcb->send_load_lock);
+ }
+ }
+ }
+
+ Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
+ if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
+ WARN("registry_mark_volume_unmounted returned %08x\n", Status);
+
+ if (flush) {
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+
+ if (Vcb->need_write && !Vcb->readonly) {
+ Status = do_write(Vcb, NULL);
+ if (!NT_SUCCESS(Status))
+ ERR("do_write returned %08x\n", Status);
+ }
+
+ free_trees(Vcb);
+
+ ExReleaseResourceLite(&Vcb->tree_lock);
+ }
+
+ for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
+ Vcb->calcthreads.threads[i].quit = TRUE;
+ }
+
+ KeSetEvent(&Vcb->calcthreads.event, 0, FALSE);
+
+ for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
+ KeWaitForSingleObject(&Vcb->calcthreads.threads[i].finished, Executive, KernelMode, FALSE, NULL);
+
+ ZwClose(Vcb->calcthreads.threads[i].handle);
+ }
+
+ ExDeleteResourceLite(&Vcb->calcthreads.lock);
+ ExFreePool(Vcb->calcthreads.threads);
+
+ time.QuadPart = 0;
+ KeSetTimer(&Vcb->flush_thread_timer, time, NULL); // trigger the timer early
+ KeWaitForSingleObject(&Vcb->flush_thread_finished, Executive, KernelMode, FALSE, NULL);
+
+ acquire_fcb_lock_exclusive(Vcb);
+ free_fcb(Vcb, Vcb->volume_fcb);
+ free_fcb(Vcb, Vcb->dummy_fcb);
+ release_fcb_lock(Vcb);
+
+ if (Vcb->root_file)
+ ObDereferenceObject(Vcb->root_file);
+
+ le = Vcb->chunks.Flink;
+ while (le != &Vcb->chunks) {
+ chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ if (c->cache) {
+ acquire_fcb_lock_exclusive(Vcb);
+ free_fcb(Vcb, c->cache);
+ release_fcb_lock(Vcb);
+ c->cache = NULL;
+ }
+
+ le = le->Flink;
}
-
- ccb = FileObject->FsContext2;
-
- TRACE("close called for %.*S (fcb == %p)\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb);
-
- FsRtlNotifyCleanup(Vcb->NotifySync, &Vcb->DirNotifyList, ccb);
-
- // FIXME - make sure notification gets sent if file is being deleted
-
- if (ccb) {
- if (ccb->query_string.Buffer)
- RtlFreeUnicodeString(&ccb->query_string);
-
- ExFreePool(ccb);
+
+ while (!IsListEmpty(&Vcb->roots)) {
+ root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->roots), root, list_entry);
+
+ ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
+ ExFreePool(r->nonpaged);
+ ExFreePool(r);
}
-
- CcUninitializeCacheMap(FileObject, NULL, NULL);
-
- free_fcb(fcb);
-
- return STATUS_SUCCESS;
-}
-static void STDCALL uninit(device_extension* Vcb) {
- chunk* c;
- space* s;
- UINT64 i;
- LIST_ENTRY rollback;
-
- InitializeListHead(&rollback);
-
- acquire_tree_lock(Vcb, TRUE);
-
- if (Vcb->write_trees > 0)
- do_write(Vcb, &rollback);
-
- free_tree_cache(&Vcb->tree_cache);
-
- clear_rollback(&rollback);
-
- release_tree_lock(Vcb, TRUE);
-
- while (Vcb->roots) {
- root* r = Vcb->roots->next;
-
- ExDeleteResourceLite(&Vcb->roots->nonpaged->load_tree_lock);
- ExFreePool(Vcb->roots->nonpaged);
- ExFreePool(Vcb->roots);
-
- Vcb->roots = r;
- }
-
while (!IsListEmpty(&Vcb->chunks)) {
- LIST_ENTRY* le = RemoveHeadList(&Vcb->chunks);
- c = CONTAINING_RECORD(le, chunk, list_entry);
-
+ chunk* c = CONTAINING_RECORD(RemoveHeadList(&Vcb->chunks), chunk, list_entry);
+
while (!IsListEmpty(&c->space)) {
LIST_ENTRY* le2 = RemoveHeadList(&c->space);
- s = CONTAINING_RECORD(le2, space, list_entry);
-
+ space* s = CONTAINING_RECORD(le2, space, list_entry);
+
ExFreePool(s);
}
-
+
+ while (!IsListEmpty(&c->deleting)) {
+ LIST_ENTRY* le2 = RemoveHeadList(&c->deleting);
+ space* s = CONTAINING_RECORD(le2, space, list_entry);
+
+ ExFreePool(s);
+ }
+
if (c->devices)
ExFreePool(c->devices);
-
+
+ if (c->cache) {
+ acquire_fcb_lock_exclusive(Vcb);
+ free_fcb(Vcb, c->cache);
+ release_fcb_lock(Vcb);
+ }
+
+ ExDeleteResourceLite(&c->range_locks_lock);
+ ExDeleteResourceLite(&c->partial_stripes_lock);
+ ExDeleteResourceLite(&c->lock);
+ ExDeleteResourceLite(&c->changed_extents_lock);
+
ExFreePool(c->chunk_item);
ExFreePool(c);
}
-
- free_fcb(Vcb->volume_fcb);
- free_fcb(Vcb->root_fcb);
-
- for (i = 0; i < Vcb->superblock.num_devices; i++) {
- while (!IsListEmpty(&Vcb->devices[i].disk_holes)) {
- LIST_ENTRY* le = RemoveHeadList(&Vcb->devices[i].disk_holes);
- disk_hole* dh = CONTAINING_RECORD(le, disk_hole, listentry);
-
- ExFreePool(dh);
+
+ // FIXME - free any open fcbs?
+
+ while (!IsListEmpty(&Vcb->devices)) {
+ device* dev = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
+
+ while (!IsListEmpty(&dev->space)) {
+ LIST_ENTRY* le2 = RemoveHeadList(&dev->space);
+ space* s = CONTAINING_RECORD(le2, space, list_entry);
+
+ ExFreePool(s);
}
+
+ ExFreePool(dev);
+ }
+
+ ExAcquireResourceExclusiveLite(&Vcb->scrub.stats_lock, TRUE);
+ while (!IsListEmpty(&Vcb->scrub.errors)) {
+ scrub_error* err = CONTAINING_RECORD(RemoveHeadList(&Vcb->scrub.errors), scrub_error, list_entry);
+
+ ExFreePool(err);
}
-
- ExFreePool(Vcb->devices);
-
+ ExReleaseResourceLite(&Vcb->scrub.stats_lock);
+
ExDeleteResourceLite(&Vcb->fcb_lock);
ExDeleteResourceLite(&Vcb->load_lock);
ExDeleteResourceLite(&Vcb->tree_lock);
-
+ ExDeleteResourceLite(&Vcb->chunk_lock);
+ ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
+ ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
+ ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
+ ExDeleteResourceLite(&Vcb->scrub.stats_lock);
+ ExDeleteResourceLite(&Vcb->send_load_lock);
+
+ ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
+ ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
+ ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
+ ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
+ ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
+ ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
+ ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
+ ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
+ ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
+
ZwClose(Vcb->flush_thread_handle);
}
-static NTSTATUS STDCALL drv_cleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject, _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback) {
+ LARGE_INTEGER newlength, time;
+ BTRFS_TIME now;
+ NTSTATUS Status;
+ ULONG utf8len = 0;
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ ExAcquireResourceExclusiveLite(fileref->fcb->Header.Resource, TRUE);
+
+ if (fileref->deleted) {
+ ExReleaseResourceLite(fileref->fcb->Header.Resource);
+ return STATUS_SUCCESS;
+ }
+
+ if (fileref->fcb->subvol->send_ops > 0) {
+ ExReleaseResourceLite(fileref->fcb->Header.Resource);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ fileref->deleted = TRUE;
+ mark_fileref_dirty(fileref);
+
+ // delete INODE_ITEM (0x1)
+
+ TRACE("nlink = %u\n", fileref->fcb->inode_item.st_nlink);
+
+ if (!fileref->fcb->ads) {
+ if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
+ LIST_ENTRY* le;
+
+ mark_fcb_dirty(fileref->fcb);
+
+ fileref->fcb->inode_item_changed = TRUE;
+
+ if (fileref->fcb->inode_item.st_nlink > 1) {
+ fileref->fcb->inode_item.st_nlink--;
+ fileref->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
+ fileref->fcb->inode_item.sequence++;
+ fileref->fcb->inode_item.st_ctime = now;
+ } else {
+ // excise extents
+
+ if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
+ 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);
+ if (!NT_SUCCESS(Status)) {
+ ERR("excise_extents returned %08x\n", Status);
+ ExReleaseResourceLite(fileref->fcb->Header.Resource);
+ return Status;
+ }
+ }
+
+ fileref->fcb->Header.AllocationSize.QuadPart = 0;
+ fileref->fcb->Header.FileSize.QuadPart = 0;
+ fileref->fcb->Header.ValidDataLength.QuadPart = 0;
+
+ if (FileObject) {
+ CC_FILE_SIZES ccfs;
+
+ ccfs.AllocationSize = fileref->fcb->Header.AllocationSize;
+ ccfs.FileSize = fileref->fcb->Header.FileSize;
+ ccfs.ValidDataLength = fileref->fcb->Header.ValidDataLength;
+
+ Status = STATUS_SUCCESS;
+
+ _SEH2_TRY {
+ CcSetFileSizes(FileObject, &ccfs);
+ } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("CcSetFileSizes threw exception %08x\n", Status);
+ ExReleaseResourceLite(fileref->fcb->Header.Resource);
+ return Status;
+ }
+ }
+
+ fileref->fcb->deleted = TRUE;
+
+ le = fileref->children.Flink;
+ while (le != &fileref->children) {
+ file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
+
+ if (fr2->fcb->ads) {
+ fr2->fcb->deleted = TRUE;
+ mark_fcb_dirty(fr2->fcb);
+ }
+
+ le = le->Flink;
+ }
+ }
+
+ if (fileref->dc) {
+ le = fileref->fcb->hardlinks.Flink;
+ while (le != &fileref->fcb->hardlinks) {
+ hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry);
+
+ if (hl->parent == fileref->parent->fcb->inode && hl->index == fileref->dc->index) {
+ RemoveEntryList(&hl->list_entry);
+
+ if (hl->name.Buffer)
+ ExFreePool(hl->name.Buffer);
+
+ if (hl->utf8.Buffer)
+ ExFreePool(hl->utf8.Buffer);
+
+ ExFreePool(hl);
+ break;
+ }
+
+ le = le->Flink;
+ }
+ }
+ } else if (fileref->fcb->subvol->parent == fileref->parent->fcb->subvol->id) { // valid subvolume
+ if (fileref->fcb->subvol->root_item.num_references > 1) {
+ fileref->fcb->subvol->root_item.num_references--;
+
+ mark_fcb_dirty(fileref->fcb); // so ROOT_ITEM gets updated
+ } else {
+ LIST_ENTRY* le;
+
+ // FIXME - we need a lock here
+
+ RemoveEntryList(&fileref->fcb->subvol->list_entry);
+
+ InsertTailList(&fileref->fcb->Vcb->drop_roots, &fileref->fcb->subvol->list_entry);
+
+ le = fileref->children.Flink;
+ while (le != &fileref->children) {
+ file_ref* fr2 = CONTAINING_RECORD(le, file_ref, list_entry);
+
+ if (fr2->fcb->ads) {
+ fr2->fcb->deleted = TRUE;
+ mark_fcb_dirty(fr2->fcb);
+ }
+
+ le = le->Flink;
+ }
+ }
+ }
+ } else {
+ fileref->fcb->deleted = TRUE;
+ mark_fcb_dirty(fileref->fcb);
+ }
+
+ // remove dir_child from parent
+
+ if (fileref->dc) {
+ TRACE("delete file %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer);
+
+ ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE);
+ RemoveEntryList(&fileref->dc->list_entry_index);
+
+ if (!fileref->fcb->ads)
+ remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc);
+
+ ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock);
+
+ if (!fileref->oldutf8.Buffer)
+ fileref->oldutf8 = fileref->dc->utf8;
+ else
+ ExFreePool(fileref->dc->utf8.Buffer);
+
+ utf8len = fileref->dc->utf8.Length;
+
+ fileref->oldindex = fileref->dc->index;
+
+ ExFreePool(fileref->dc->name.Buffer);
+ ExFreePool(fileref->dc->name_uc.Buffer);
+ ExFreePool(fileref->dc);
+
+ fileref->dc = NULL;
+ }
+
+ // update INODE_ITEM of parent
+
+ ExAcquireResourceExclusiveLite(fileref->parent->fcb->Header.Resource, TRUE);
+
+ fileref->parent->fcb->inode_item.transid = fileref->fcb->Vcb->superblock.generation;
+ fileref->parent->fcb->inode_item.sequence++;
+ fileref->parent->fcb->inode_item.st_ctime = now;
+
+ if (!fileref->fcb->ads) {
+ TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+ fileref->parent->fcb->inode_item.st_size -= utf8len * 2;
+ TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref->parent->fcb->inode, fileref->parent->fcb->inode_item.st_size);
+ fileref->parent->fcb->inode_item.st_mtime = now;
+ }
+
+ fileref->parent->fcb->inode_item_changed = TRUE;
+ ExReleaseResourceLite(fileref->parent->fcb->Header.Resource);
+
+ if (!fileref->fcb->ads && fileref->parent->dc)
+ send_notification_fcb(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL);
+
+ mark_fcb_dirty(fileref->parent->fcb);
+
+ fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation;
+ fileref->fcb->subvol->root_item.ctime = now;
+
+ newlength.QuadPart = 0;
+
+ if (FileObject && !CcUninitializeCacheMap(FileObject, &newlength, NULL))
+ TRACE("CcUninitializeCacheMap failed\n");
+
+ ExReleaseResourceLite(fileref->fcb->Header.Resource);
+
+ return STATUS_SUCCESS;
+}
+
+_Dispatch_type_(IRP_MJ_CLEANUP)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
- fcb* fcb;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ fcb* fcb = FileObject->FsContext;
BOOL top_level;
- TRACE("cleanup\n");
-
FsRtlEnterFileSystem();
+ TRACE("cleanup\n");
+
top_level = is_top_level(Irp);
-
- if (DeviceObject == devobj) {
+
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_cleanup(DeviceObject, Irp);
+ goto exit;
+ } else if (DeviceObject == master_devobj) {
TRACE("closing file system\n");
Status = STATUS_SUCCESS;
goto exit;
+ } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ if (FileObject->Flags & FO_CLEANUP_COMPLETE) {
+ TRACE("FileObject %p already cleaned up\n", FileObject);
+ Status = STATUS_SUCCESS;
+ goto exit;
}
-
- if (FileObject) {
+
+ if (!fcb) {
+ ERR("fcb was NULL\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto exit;
+ }
+
+ // We have to use the pointer to Vcb stored in the fcb, as we can receive cleanup
+ // messages belonging to other devices.
+
+ if (FileObject && FileObject->FsContext) {
LONG oc;
-
- fcb = FileObject->FsContext;
-
+ ccb* ccb;
+ file_ref* fileref;
+ BOOL locked = TRUE;
+
+ ccb = FileObject->FsContext2;
+ fileref = ccb ? ccb->fileref : NULL;
+
TRACE("cleanup called for FileObject %p\n", FileObject);
- TRACE("fcb %p (%.*S), refcount = %u, open_count = %u\n", fcb, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer, fcb->refcount, fcb->open_count);
-
+ TRACE("fileref %p (%S), refcount = %u, open_count = %u\n", fileref, file_desc(FileObject), fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
+
+ ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
+
+ ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+
IoRemoveShareAccess(FileObject, &fcb->share_access);
-
- oc = InterlockedDecrement(&fcb->open_count);
+
+ if (ccb)
+ FsRtlNotifyCleanup(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, ccb);
+
+ if (fileref) {
+ oc = InterlockedDecrement(&fileref->open_count);
#ifdef DEBUG_FCB_REFCOUNTS
- ERR("fcb %p: open_count now %i\n", fcb, oc);
+ ERR("fileref %p: open_count now %i\n", fileref, oc);
#endif
-
- if (oc == 0) {
- if (fcb->delete_on_close && fcb != fcb->Vcb->root_fcb && fcb != fcb->Vcb->volume_fcb) {
- LIST_ENTRY rollback;
- InitializeListHead(&rollback);
-
- acquire_tree_lock(fcb->Vcb, TRUE);
-
- Status = delete_fcb(fcb, FileObject, &rollback);
-
- if (NT_SUCCESS(Status)) {
- LARGE_INTEGER newlength;
-
- if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject)
- CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
-
- newlength.QuadPart = 0;
-
- if (!CcUninitializeCacheMap(FileObject, &newlength, NULL)) {
- TRACE("CcUninitializeCacheMap failed\n");
+ }
+
+ if (ccb && ccb->options & FILE_DELETE_ON_CLOSE && fileref)
+ fileref->delete_on_close = TRUE;
+
+ if (fileref && fileref->delete_on_close && fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && fcb != fcb->Vcb->dummy_fcb)
+ fileref->delete_on_close = FALSE;
+
+ if (fcb->Vcb->locked && fcb->Vcb->locked_fileobj == FileObject) {
+ TRACE("unlocking volume\n");
+ do_unlock_volume(fcb->Vcb);
+ FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
+ }
+
+ if (ccb && ccb->reserving) {
+ fcb->subvol->reserved = NULL;
+ ccb->reserving = FALSE;
+ // FIXME - flush all of subvol's fcbs
+ }
+
+ if (fileref && oc == 0) {
+ if (!fcb->Vcb->removing) {
+ if (fileref && fileref->delete_on_close && fileref != fcb->Vcb->root_fileref && fcb != fcb->Vcb->volume_fcb) {
+ LIST_ENTRY rollback;
+
+ InitializeListHead(&rollback);
+
+ if (!fileref->fcb->ads || fileref->dc) {
+ if (fileref->fcb->ads) {
+ send_notification_fileref(fileref->parent, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
+ FILE_ACTION_REMOVED, &fileref->dc->name);
+ } else
+ send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL);
}
-
+
+ ExReleaseResourceLite(fcb->Header.Resource);
+ locked = FALSE;
+
+ // fcb_lock needs to be acquired before fcb->Header.Resource
+ acquire_fcb_lock_exclusive(fcb->Vcb);
+
+ Status = delete_fileref(fileref, FileObject, Irp, &rollback);
+ if (!NT_SUCCESS(Status)) {
+ ERR("delete_fileref returned %08x\n", Status);
+ do_rollback(fcb->Vcb, &rollback);
+ release_fcb_lock(fcb->Vcb);
+ ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+ goto exit;
+ }
+
+ release_fcb_lock(fcb->Vcb);
+
+ locked = FALSE;
+
clear_rollback(&rollback);
- } else
- do_rollback(fcb->Vcb, &rollback);
-
- release_tree_lock(fcb->Vcb, TRUE);
- } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
- IO_STATUS_BLOCK iosb;
- CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
-
- if (!NT_SUCCESS(iosb.Status)) {
- ERR("CcFlushCache returned %08x\n", iosb.Status);
- }
+ } else if (FileObject->Flags & FO_CACHE_SUPPORTED && fcb->nonpaged->segment_object.DataSectionObject) {
+ IO_STATUS_BLOCK iosb;
+ CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
+
+ if (!NT_SUCCESS(iosb.Status)) {
+ ERR("CcFlushCache returned %08x\n", iosb.Status);
+ }
+
+ if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
+ ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
+ ExReleaseResourceLite(fcb->Header.PagingIoResource);
+ }
- ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
- ExReleaseResourceLite(fcb->Header.PagingIoResource);
+ CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
- CcPurgeCacheSection(&fcb->nonpaged->segment_object, NULL, 0, FALSE);
-
- TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
- FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+ TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
+ FileObject, fcb, fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
+ }
}
-
+
if (fcb->Vcb && fcb != fcb->Vcb->volume_fcb)
CcUninitializeCacheMap(FileObject, NULL, NULL);
}
-
+
+ if (locked)
+ ExReleaseResourceLite(fcb->Header.Resource);
+
+ ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+
FileObject->Flags |= FO_CLEANUP_COMPLETE;
}
-
+
Status = STATUS_SUCCESS;
exit:
+ TRACE("returning %08x\n", Status);
+
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
-
+
IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
- if (top_level)
+
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
return Status;
}
-ULONG STDCALL get_file_attributes(device_extension* Vcb, INODE_ITEM* ii, root* r, UINT64 inode, UINT8 type, BOOL dotfile, BOOL ignore_xa) {
+_Success_(return)
+BOOL get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ UINT16 len, _Out_ ULONG* atts) {
+ if (len > 2 && val[0] == '0' && val[1] == 'x') {
+ int i;
+ ULONG dosnum = 0;
+
+ for (i = 2; i < len; i++) {
+ dosnum *= 0x10;
+
+ if (val[i] >= '0' && val[i] <= '9')
+ dosnum |= val[i] - '0';
+ else if (val[i] >= 'a' && val[i] <= 'f')
+ dosnum |= val[i] + 10 - 'a';
+ else if (val[i] >= 'A' && val[i] <= 'F')
+ dosnum |= val[i] + 10 - 'a';
+ }
+
+ TRACE("DOSATTRIB: %08x\n", dosnum);
+
+ *atts = dosnum;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+ULONG get_file_attributes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ root* r, _In_ UINT64 inode,
+ _In_ UINT8 type, _In_ BOOL dotfile, _In_ BOOL ignore_xa, _In_opt_ PIRP Irp) {
ULONG att;
char* eaval;
UINT16 ealen;
-
- if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen)) {
- if (ealen > 2) {
- if (eaval[0] == '0' && eaval[1] == 'x') {
- int i;
- ULONG dosnum = 0;
-
- for (i = 2; i < ealen; i++) {
- dosnum *= 0x10;
-
- if (eaval[i] >= '0' && eaval[i] <= '9')
- dosnum |= eaval[i] - '0';
- else if (eaval[i] >= 'a' && eaval[i] <= 'f')
- dosnum |= eaval[i] + 10 - 'a';
- else if (eaval[i] >= 'A' && eaval[i] <= 'F')
- dosnum |= eaval[i] + 10 - 'a';
- }
-
- TRACE("DOSATTRIB: %08x\n", dosnum);
- ExFreePool(eaval);
-
- return dosnum;
+ if (!ignore_xa && get_xattr(Vcb, r, inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8**)&eaval, &ealen, Irp)) {
+ ULONG dosnum = 0;
+
+ if (get_file_attributes_from_xattr(eaval, ealen, &dosnum)) {
+ ExFreePool(eaval);
+
+ if (type == BTRFS_TYPE_DIRECTORY)
+ dosnum |= FILE_ATTRIBUTE_DIRECTORY;
+ else if (type == BTRFS_TYPE_SYMLINK)
+ dosnum |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+ if (type != BTRFS_TYPE_DIRECTORY)
+ dosnum &= ~FILE_ATTRIBUTE_DIRECTORY;
+
+ if (inode == SUBVOL_ROOT_INODE) {
+ if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
+ dosnum |= FILE_ATTRIBUTE_READONLY;
+ else
+ dosnum &= ~FILE_ATTRIBUTE_READONLY;
}
+
+ return dosnum;
}
-
+
ExFreePool(eaval);
}
-
+
switch (type) {
case BTRFS_TYPE_DIRECTORY:
att = FILE_ATTRIBUTE_DIRECTORY;
break;
-
+
case BTRFS_TYPE_SYMLINK:
att = FILE_ATTRIBUTE_REPARSE_POINT;
break;
-
+
default:
att = 0;
break;
}
-
+
if (dotfile) {
att |= FILE_ATTRIBUTE_HIDDEN;
}
-
+
att |= FILE_ATTRIBUTE_ARCHIVE;
-
+
+ if (inode == SUBVOL_ROOT_INODE) {
+ if (r->root_item.flags & BTRFS_SUBVOL_READONLY)
+ att |= FILE_ATTRIBUTE_READONLY;
+ else
+ att &= ~FILE_ATTRIBUTE_READONLY;
+ }
+
// FIXME - get READONLY from ii->st_mode
// FIXME - return SYSTEM for block/char devices?
-
+
if (att == 0)
att = FILE_ATTRIBUTE_NORMAL;
-
+
return att;
}
-// static int STDCALL utf8icmp(char* a, char* b) {
-// return strcmp(a, b); // FIXME - don't treat as ASCII
-// }
-
-NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, LONGLONG StartingOffset, ULONG Length, PUCHAR Buffer) {
- IO_STATUS_BLOCK* IoStatus;
+NTSTATUS sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject, _In_ UINT64 StartingOffset, _In_ ULONG Length,
+ _Out_writes_bytes_(Length) PUCHAR Buffer, _In_ BOOL override) {
+ IO_STATUS_BLOCK IoStatus;
LARGE_INTEGER Offset;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status;
- read_context* context;
-
+ read_context context;
+
num_reads++;
-
- context = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_context), ALLOC_TAG);
- if (!context) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlZeroMemory(context, sizeof(read_context));
- KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
-
- IoStatus = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_STATUS_BLOCK), ALLOC_TAG);
- if (!IoStatus) {
- ERR("out of memory\n");
- ExFreePool(context);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- Offset.QuadPart = StartingOffset;
+ RtlZeroMemory(&context, sizeof(read_context));
+ KeInitializeEvent(&context.Event, NotificationEvent, FALSE);
+
+ Offset.QuadPart = (LONGLONG)StartingOffset;
-// Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
-
+
if (!Irp) {
ERR("IoAllocateIrp failed\n");
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto exit;
+ return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
+ Irp->Flags |= IRP_NOCACHE;
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_READ;
-
+
+ if (override)
+ IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
if (DeviceObject->Flags & DO_BUFFERED_IO) {
- FIXME("FIXME - buffered IO\n");
+ Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG);
+ if (!Irp->AssociatedIrp.SystemBuffer) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
+
+ Irp->UserBuffer = Buffer;
} else if (DeviceObject->Flags & DO_DIRECT_IO) {
-// TRACE("direct IO\n");
-
Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL);
if (!Irp->MdlAddress) {
ERR("IoAllocateMdl failed\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
- // IoFreeIrp(Irp);
goto exit;
-// } else {
-// TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
}
-
- MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
- } else {
-// TRACE("neither buffered nor direct IO\n");
+
+ Status = STATUS_SUCCESS;
+
+ _SEH2_TRY {
+ MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
+ } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ } _SEH2_END;
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("MmProbeAndLockPages threw exception %08x\n", Status);
+ IoFreeMdl(Irp->MdlAddress);
+ goto exit;
+ }
+ } else
Irp->UserBuffer = Buffer;
- }
IrpSp->Parameters.Read.Length = Length;
IrpSp->Parameters.Read.ByteOffset = Offset;
-
- Irp->UserIosb = IoStatus;
-// Irp->Tail.Overlay.Thread = PsGetCurrentThread();
-
- Irp->UserEvent = &context->Event;
-
-// IoQueueThreadIrp(Irp);
-
- IoSetCompletionRoutine(Irp, read_completion, context, TRUE, TRUE, TRUE);
-
-// if (Override)
-// {
-// Stack = IoGetNextIrpStackLocation(Irp);
-// Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
-// }
-
-// TRACE("Calling IO Driver... with irp %p\n", Irp);
+
+ Irp->UserIosb = &IoStatus;
+
+ Irp->UserEvent = &context.Event;
+
+ IoSetCompletionRoutine(Irp, read_completion, &context, TRUE, TRUE, TRUE);
+
Status = IoCallDriver(DeviceObject, Irp);
-// TRACE("Waiting for IO Operation for %p\n", Irp);
if (Status == STATUS_PENDING) {
-// TRACE("Operation pending\n");
- KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
-// TRACE("Getting IO Status... for %p\n", Irp);
- Status = context->iosb.Status;
+ KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL);
+ Status = context.iosb.Status;
}
-
+
if (DeviceObject->Flags & DO_DIRECT_IO) {
MmUnlockPages(Irp->MdlAddress);
IoFreeMdl(Irp->MdlAddress);
}
-
+
exit:
IoFreeIrp(Irp);
- ExFreePool(IoStatus);
- ExFreePool(context);
-
return Status;
}
-static NTSTATUS STDCALL read_superblock(device_extension* Vcb, PDEVICE_OBJECT device) {
+static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT device, _In_ UINT64 length) {
NTSTATUS Status;
superblock* sb;
- unsigned int i, to_read;
- UINT32 crc32;
-
- to_read = sector_align(sizeof(superblock), device->SectorSize);
-
+ ULONG i, to_read;
+ UINT8 valid_superblocks;
+
+ to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
+
sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
if (!sb) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
+ if (superblock_addrs[0] + to_read > length) {
+ WARN("device was too short to have any superblock\n");
+ ExFreePool(sb);
+ return STATUS_UNRECOGNIZED_VOLUME;
+ }
+
i = 0;
-
+ valid_superblocks = 0;
+
while (superblock_addrs[i] > 0) {
- if (i > 0 && superblock_addrs[i] + sizeof(superblock) > Vcb->length)
+ UINT32 crc32;
+
+ if (i > 0 && superblock_addrs[i] + to_read > length)
break;
-
- Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb);
+
+ Status = sync_read_phys(device, superblock_addrs[i], to_read, (PUCHAR)sb, FALSE);
if (!NT_SUCCESS(Status)) {
ERR("Failed to read superblock %u: %08x\n", i, Status);
ExFreePool(sb);
return Status;
}
-
- TRACE("got superblock %u!\n", i);
- if (i == 0 || sb->generation > Vcb->superblock.generation)
- RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
-
+ if (sb->magic != BTRFS_MAGIC) {
+ if (i == 0) {
+ TRACE("not a BTRFS volume\n");
+ ExFreePool(sb);
+ return STATUS_UNRECOGNIZED_VOLUME;
+ }
+ } else {
+ TRACE("got superblock %u!\n", i);
+
+ crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+
+ if (crc32 != *((UINT32*)sb->checksum))
+ WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+ else if (sb->sector_size == 0)
+ WARN("superblock sector size was 0\n");
+ else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
+ WARN("invalid node size %x\n", sb->node_size);
+ else if ((sb->node_size % sb->sector_size) != 0)
+ WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
+ else if (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation) {
+ RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
+ valid_superblocks++;
+ }
+ }
+
i++;
}
-
+
ExFreePool(sb);
-
- crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
- crc32 = ~crc32;
- TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)Vcb->superblock.checksum));
-
- if (crc32 != *((UINT32*)Vcb->superblock.checksum))
- return STATUS_INTERNAL_ERROR; // FIXME - correct error?
-
+
+ if (valid_superblocks == 0) {
+ ERR("could not find any valid superblocks\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
TRACE("label is %s\n", Vcb->superblock.label);
-// utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
-
+
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL dev_ioctl(PDEVICE_OBJECT DeviceObject, ULONG ControlCode, PVOID InputBuffer,
- ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, BOOLEAN Override)
-{
+NTSTATUS dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject, _In_ ULONG ControlCode, _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize,
+ _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_ ULONG OutputBufferSize, _In_ BOOLEAN Override, _Out_opt_ IO_STATUS_BLOCK* iosb) {
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
- PIO_STACK_LOCATION Stack;
+ PIO_STACK_LOCATION IrpSp;
IO_STATUS_BLOCK IoStatus;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
if (Override) {
- Stack = IoGetNextIrpStackLocation(Irp);
- Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+ IrpSp = IoGetNextIrpStackLocation(Irp);
+ IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
Status = IoCallDriver(DeviceObject, Irp);
Status = IoStatus.Status;
}
+ if (iosb)
+ *iosb = IoStatus;
+
return Status;
}
-// static void STDCALL find_chunk_root(device_extension* Vcb) {
-// UINT32 i;
-// KEY* key;
-//
-// i = 0;
-// while (i < Vcb->superblock.n) {
-// key = &Vcb->superblock.sys_chunk_array[i];
-// i += sizeof(KEY);
-// }
-//
-// // FIXME
-// }
-
-// static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) {
-// if (!Vcb->log_to_phys) {
-// Vcb->log_to_phys = ltp;
-// ltp->next = NULL;
-// return;
-// }
-//
-// // FIXME - these should be ordered
-// ltp->next = Vcb->log_to_phys;
-// Vcb->log_to_phys = ltp;
-// }
-
-static NTSTATUS STDCALL add_root(device_extension* Vcb, UINT64 id, UINT64 addr, traverse_ptr* tp) {
+_Requires_exclusive_lock_held_(Vcb->tree_lock)
+static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ UINT64 id, _In_ UINT64 addr,
+ _In_ UINT64 generation, _In_opt_ traverse_ptr* tp) {
root* r = ExAllocatePoolWithTag(PagedPool, sizeof(root), ALLOC_TAG);
if (!r) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
r->id = id;
+ r->dirty = FALSE;
+ r->received = FALSE;
+ r->reserved = NULL;
r->treeholder.address = addr;
r->treeholder.tree = NULL;
- init_tree_holder(&r->treeholder);
- r->prev = NULL;
- r->next = Vcb->roots;
+ r->treeholder.generation = generation;
+ r->parent = 0;
+ r->send_ops = 0;
+ InitializeListHead(&r->fcbs);
r->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(root_nonpaged), ALLOC_TAG);
if (!r->nonpaged) {
ExFreePool(r);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
ExInitializeResourceLite(&r->nonpaged->load_tree_lock);
-
+
r->lastinode = 0;
-
+
if (tp) {
RtlCopyMemory(&r->root_item, tp->item->data, min(sizeof(ROOT_ITEM), tp->item->size));
if (tp->item->size < sizeof(ROOT_ITEM))
RtlZeroMemory(((UINT8*)&r->root_item) + tp->item->size, sizeof(ROOT_ITEM) - tp->item->size);
+ } else
+ RtlZeroMemory(&r->root_item, sizeof(ROOT_ITEM));
+
+ if (!Vcb->readonly && (r->id == BTRFS_ROOT_ROOT || r->id == BTRFS_ROOT_FSTREE || (r->id >= 0x100 && !(r->id & 0xf000000000000000)))) { // FS tree root
+ // FIXME - don't call this if subvol is readonly (though we will have to if we ever toggle this flag)
+ get_last_inode(Vcb, r, NULL);
+
+ if (r->id == BTRFS_ROOT_ROOT && r->lastinode < 0x100)
+ r->lastinode = 0x100;
}
-
- if (Vcb->roots)
- Vcb->roots->prev = r;
-
- Vcb->roots = r;
-
+
+ InsertTailList(&Vcb->roots, &r->list_entry);
+
switch (r->id) {
case BTRFS_ROOT_ROOT:
Vcb->root_root = r;
break;
-
+
case BTRFS_ROOT_EXTENT:
Vcb->extent_root = r;
break;
-
+
case BTRFS_ROOT_CHUNK:
Vcb->chunk_root = r;
break;
-
+
case BTRFS_ROOT_DEVTREE:
Vcb->dev_root = r;
break;
-
+
case BTRFS_ROOT_CHECKSUM:
Vcb->checksum_root = r;
break;
+
+ case BTRFS_ROOT_UUID:
+ Vcb->uuid_root = r;
+ break;
+
+ case BTRFS_ROOT_FREE_SPACE:
+ Vcb->space_root = r;
+ break;
+
+ case BTRFS_ROOT_DATA_RELOC:
+ Vcb->data_reloc_root = r;
+ break;
}
-
+
return STATUS_SUCCESS;
}
-static NTSTATUS STDCALL look_for_roots(device_extension* Vcb) {
+static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock) _In_ device_extension* Vcb, _In_opt_ PIRP Irp) {
traverse_ptr tp, next_tp;
KEY searchkey;
BOOL b;
NTSTATUS Status;
-
+
searchkey.obj_id = 0;
searchkey.obj_type = 0;
searchkey.offset = 0;
-
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
- ERR("error - find_tree returned %08x\n", Status);
+ ERR("error - find_item returned %08x\n", Status);
return Status;
}
-
+
do {
TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
+
if (tp.item->key.obj_type == TYPE_ROOT_ITEM) {
ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
-
+
if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
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));
} else {
TRACE("root %llx - address %llx\n", tp.item->key.obj_id, ri->block_number);
-
- Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, &tp);
+
+ Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, ri->generation, &tp);
if (!NT_SUCCESS(Status)) {
ERR("add_root returned %08x\n", Status);
return Status;
}
}
+ } else if (tp.item->key.obj_type == TYPE_ROOT_BACKREF && !IsListEmpty(&Vcb->roots)) {
+ root* lastroot = CONTAINING_RECORD(Vcb->roots.Blink, root, list_entry);
+
+ if (lastroot->id == tp.item->key.obj_id)
+ lastroot->parent = tp.item->key.offset;
}
-
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-
- if (b) {
- free_traverse_ptr(&tp);
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+
+ if (b)
tp = next_tp;
- }
} while (b);
-
- free_traverse_ptr(&tp);
-
- return STATUS_SUCCESS;
-}
-static NTSTATUS add_disk_hole(LIST_ENTRY* disk_holes, UINT64 address, UINT64 size) {
- disk_hole* dh = ExAllocatePoolWithTag(PagedPool, sizeof(disk_hole), ALLOC_TAG);
-
- if (!dh) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ if (!Vcb->readonly && !Vcb->data_reloc_root) {
+ root* reloc_root;
+ INODE_ITEM* ii;
+ UINT16 irlen;
+ INODE_REF* ir;
+ LARGE_INTEGER time;
+ BTRFS_TIME now;
+
+ WARN("data reloc root doesn't exist, creating it\n");
+
+ Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, FALSE, 0, Irp);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("create_root returned %08x\n", Status);
+ return Status;
+ }
+
+ reloc_root->root_item.inode.generation = 1;
+ reloc_root->root_item.inode.st_size = 3;
+ reloc_root->root_item.inode.st_blocks = Vcb->superblock.node_size;
+ reloc_root->root_item.inode.st_nlink = 1;
+ reloc_root->root_item.inode.st_mode = 040755;
+ reloc_root->root_item.inode.flags = 0xffffffff80000000;
+ reloc_root->root_item.objid = SUBVOL_ROOT_INODE;
+ reloc_root->root_item.bytes_used = Vcb->superblock.node_size;
+
+ ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
+ if (!ii) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ KeQuerySystemTime(&time);
+ win_time_to_unix(time, &now);
+
+ RtlZeroMemory(ii, sizeof(INODE_ITEM));
+ ii->generation = Vcb->superblock.generation;
+ ii->st_blocks = Vcb->superblock.node_size;
+ ii->st_nlink = 1;
+ ii->st_mode = 040755;
+ ii->st_atime = now;
+ ii->st_ctime = now;
+ ii->st_mtime = now;
+
+ Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_tree_item returned %08x\n", Status);
+ ExFreePool(ii);
+ return Status;
+ }
+
+ irlen = (UINT16)offsetof(INODE_REF, name[0]) + 2;
+ ir = ExAllocatePoolWithTag(PagedPool, irlen, ALLOC_TAG);
+ if (!ir) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ir->index = 0;
+ ir->n = 2;
+ ir->name[0] = '.';
+ ir->name[1] = '.';
+
+ Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("insert_tree_item returned %08x\n", Status);
+ ExFreePool(ir);
+ return Status;
+ }
+
+ Vcb->data_reloc_root = reloc_root;
+ Vcb->need_write = TRUE;
}
-
- dh->address = address;
- dh->size = size;
- dh->provisional = FALSE;
-
- InsertTailList(disk_holes, &dh->listentry);
-
+
return STATUS_SUCCESS;
}
-static NTSTATUS find_disk_holes(device_extension* Vcb, device* dev) {
+static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_ device* dev, _In_opt_ PIRP Irp) {
KEY searchkey;
traverse_ptr tp, next_tp;
BOOL b;
UINT64 lastaddr;
NTSTATUS Status;
-
- InitializeListHead(&dev->disk_holes);
-
+
+ InitializeListHead(&dev->space);
+
+ searchkey.obj_id = 0;
+ searchkey.obj_type = TYPE_DEV_STATS;
+ searchkey.offset = dev->devitem.dev_id;
+
+ Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
+ if (NT_SUCCESS(Status) && !keycmp(tp.item->key, searchkey))
+ RtlCopyMemory(dev->stats, tp.item->data, min(sizeof(UINT64) * 5, tp.item->size));
+
searchkey.obj_id = dev->devitem.dev_id;
searchkey.obj_type = TYPE_DEV_EXTENT;
searchkey.offset = 0;
-
- Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE);
+
+ Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
- ERR("error - find_tree returned %08x\n", Status);
+ ERR("error - find_item returned %08x\n", Status);
return Status;
}
-
+
lastaddr = 0;
-
+
do {
if (tp.item->key.obj_id == dev->devitem.dev_id && tp.item->key.obj_type == TYPE_DEV_EXTENT) {
if (tp.item->size >= sizeof(DEV_EXTENT)) {
DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
-
+
if (tp.item->key.offset > lastaddr) {
- Status = add_disk_hole(&dev->disk_holes, lastaddr, tp.item->key.offset - lastaddr);
+ Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
if (!NT_SUCCESS(Status)) {
- ERR("add_disk_hole returned %08x\n", Status);
+ ERR("add_space_entry returned %08x\n", Status);
return Status;
}
}
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));
}
}
-
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+
if (b) {
- free_traverse_ptr(&tp);
tp = next_tp;
if (tp.item->key.obj_id > searchkey.obj_id || tp.item->key.obj_type > searchkey.obj_type)
break;
}
} while (b);
-
- free_traverse_ptr(&tp);
-
+
if (lastaddr < dev->devitem.num_bytes) {
- Status = add_disk_hole(&dev->disk_holes, lastaddr, dev->devitem.num_bytes - lastaddr);
+ Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
if (!NT_SUCCESS(Status)) {
- ERR("add_disk_hole returned %08x\n", Status);
+ ERR("add_space_entry returned %08x\n", Status);
return Status;
}
}
-
- // FIXME - free disk_holes when unmounting
-
+
+ // The Linux driver doesn't like to allocate chunks within the first megabyte of a device.
+
+ space_list_subtract2(&dev->space, NULL, 0, 0x100000, NULL, NULL);
+
return STATUS_SUCCESS;
}
-device* find_device_from_uuid(device_extension* Vcb, BTRFS_UUID* uuid) {
- UINT64 i;
-
- for (i = 0; i < Vcb->superblock.num_devices; i++) {
- TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i,
- 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],
- 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]);
-
- if (Vcb->devices[i].devobj && RtlCompareMemory(&Vcb->devices[i].devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
- TRACE("returning device %llx\n", i);
- return &Vcb->devices[i];
+static void add_device_to_list(_In_ device_extension* Vcb, _In_ device* dev) {
+ LIST_ENTRY* le;
+
+ le = Vcb->devices.Flink;
+
+ while (le != &Vcb->devices) {
+ device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+
+ if (dev2->devitem.dev_id > dev->devitem.dev_id) {
+ InsertHeadList(le->Blink, &dev->list_entry);
+ return;
+ }
+
+ le = le->Flink;
+ }
+
+ InsertTailList(&Vcb->devices, &dev->list_entry);
+}
+
+_Ret_maybenull_
+device* find_device_from_uuid(_In_ device_extension* Vcb, _In_ BTRFS_UUID* uuid) {
+ volume_device_extension* vde;
+ pdo_device_extension* pdode;
+ LIST_ENTRY* le;
+
+ le = Vcb->devices.Flink;
+ while (le != &Vcb->devices) {
+ device* dev = CONTAINING_RECORD(le, device, list_entry);
+
+ TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", dev->devitem.dev_id,
+ 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],
+ 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]);
+
+ if (RtlCompareMemory(&dev->devitem.device_uuid, uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+ TRACE("returning device %llx\n", dev->devitem.dev_id);
+ return dev;
+ }
+
+ le = le->Flink;
+ }
+
+ vde = Vcb->vde;
+
+ if (!vde)
+ goto end;
+
+ pdode = vde->pdode;
+
+ ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
+
+ if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+ le = pdode->children.Flink;
+
+ while (le != &pdode->children) {
+ volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+
+ if (RtlCompareMemory(uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+ device* dev;
+
+ dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+ if (!dev) {
+ ExReleaseResourceLite(&pdode->child_lock);
+ ERR("out of memory\n");
+ return NULL;
+ }
+
+ RtlZeroMemory(dev, sizeof(device));
+ dev->devobj = vc->devobj;
+ dev->devitem.device_uuid = *uuid;
+ dev->devitem.dev_id = vc->devid;
+ dev->devitem.num_bytes = vc->size;
+ dev->seeding = vc->seeding;
+ dev->readonly = dev->seeding;
+ dev->reloc = FALSE;
+ dev->removable = FALSE;
+ dev->disk_num = vc->disk_num;
+ dev->part_num = vc->part_num;
+ dev->num_trim_entries = 0;
+ InitializeListHead(&dev->trim_list);
+
+ add_device_to_list(Vcb, dev);
+ Vcb->devices_loaded++;
+
+ ExReleaseResourceLite(&pdode->child_lock);
+
+ return dev;
+ }
+
+ le = le->Flink;
}
}
-
+
+ ExReleaseResourceLite(&pdode->child_lock);
+
+end:
WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
uuid->uuid[0], uuid->uuid[1], uuid->uuid[2], uuid->uuid[3], uuid->uuid[4], uuid->uuid[5], uuid->uuid[6], uuid->uuid[7],
uuid->uuid[8], uuid->uuid[9], uuid->uuid[10], uuid->uuid[11], uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]);
-
+
return NULL;
}
-static NTSTATUS STDCALL load_chunk_root(device_extension* Vcb) {
+static BOOL is_device_removable(_In_ PDEVICE_OBJECT devobj) {
+ NTSTATUS Status;
+ STORAGE_HOTPLUG_INFO shi;
+
+ Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), TRUE, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("dev_ioctl returned %08x\n", Status);
+ return FALSE;
+ }
+
+ return shi.MediaRemovable != 0 ? TRUE : FALSE;
+}
+
+static ULONG get_device_change_count(_In_ PDEVICE_OBJECT devobj) {
+ NTSTATUS Status;
+ ULONG cc;
+ IO_STATUS_BLOCK iosb;
+
+ Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("dev_ioctl returned %08x\n", Status);
+ return 0;
+ }
+
+ if (iosb.Information < sizeof(ULONG)) {
+ ERR("iosb.Information was too short\n");
+ return 0;
+ }
+
+ return cc;
+}
+
+void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ BOOL get_nums) {
+ NTSTATUS Status;
+ ULONG aptelen;
+ ATA_PASS_THROUGH_EX* apte;
+ STORAGE_PROPERTY_QUERY spq;
+ DEVICE_TRIM_DESCRIPTOR dtd;
+
+ dev->removable = is_device_removable(dev->devobj);
+ dev->change_count = dev->removable ? get_device_change_count(dev->devobj) : 0;
+
+ if (get_nums) {
+ STORAGE_DEVICE_NUMBER sdn;
+
+ Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
+ &sdn, sizeof(STORAGE_DEVICE_NUMBER), TRUE, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
+ dev->disk_num = 0xffffffff;
+ dev->part_num = 0xffffffff;
+ } else {
+ dev->disk_num = sdn.DeviceNumber;
+ dev->part_num = sdn.PartitionNumber;
+ }
+ }
+
+ dev->trim = FALSE;
+ dev->readonly = dev->seeding;
+ dev->reloc = FALSE;
+ dev->num_trim_entries = 0;
+ dev->stats_changed = FALSE;
+ InitializeListHead(&dev->trim_list);
+
+ if (!dev->readonly) {
+ Status = dev_ioctl(dev->devobj, IOCTL_DISK_IS_WRITABLE, NULL, 0,
+ NULL, 0, TRUE, NULL);
+ if (Status == STATUS_MEDIA_WRITE_PROTECTED)
+ dev->readonly = TRUE;
+ }
+
+ aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
+ apte = ExAllocatePoolWithTag(NonPagedPool, aptelen, ALLOC_TAG);
+ if (!apte) {
+ ERR("out of memory\n");
+ return;
+ }
+
+ RtlZeroMemory(apte, aptelen);
+
+ apte->Length = sizeof(ATA_PASS_THROUGH_EX);
+ apte->AtaFlags = ATA_FLAGS_DATA_IN;
+ apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
+ apte->TimeOutValue = 3;
+ apte->DataBufferOffset = apte->Length;
+ apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY;
+
+ Status = dev_ioctl(dev->devobj, IOCTL_ATA_PASS_THROUGH, apte, aptelen,
+ apte, aptelen, TRUE, NULL);
+
+ if (!NT_SUCCESS(Status))
+ TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status);
+ else {
+ IDENTIFY_DEVICE_DATA* idd = (IDENTIFY_DEVICE_DATA*)((UINT8*)apte + sizeof(ATA_PASS_THROUGH_EX));
+
+ if (idd->CommandSetSupport.FlushCache) {
+ dev->can_flush = TRUE;
+ TRACE("FLUSH CACHE supported\n");
+ } else
+ TRACE("FLUSH CACHE not supported\n");
+ }
+
+ ExFreePool(apte);
+
+ spq.PropertyId = StorageDeviceTrimProperty;
+ spq.QueryType = PropertyStandardQuery;
+ spq.AdditionalParameters[0] = 0;
+
+ Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(STORAGE_PROPERTY_QUERY),
+ &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), TRUE, NULL);
+
+ if (NT_SUCCESS(Status)) {
+ if (dtd.TrimEnabled) {
+ dev->trim = TRUE;
+ Vcb->trim = TRUE;
+ TRACE("TRIM supported\n");
+ } else
+ TRACE("TRIM not supported\n");
+ }
+
+ RtlZeroMemory(dev->stats, sizeof(UINT64) * 5);
+}
+
+static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
traverse_ptr tp, next_tp;
KEY searchkey;
BOOL b;
chunk* c;
- UINT64 i;
NTSTATUS Status;
searchkey.obj_id = 0;
searchkey.obj_type = 0;
searchkey.offset = 0;
-
- Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE);
+
+ Vcb->data_flags = 0;
+ Vcb->metadata_flags = 0;
+ Vcb->system_flags = 0;
+
+ Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
if (!NT_SUCCESS(Status)) {
ERR("error - find_item returned %08x\n", Status);
return Status;
}
-
+
do {
TRACE("(%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
-
- if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM && tp.item->key.offset == 1) {
- // FIXME - this is a hack; make this work with multiple devices!
- if (tp.item->size > 0)
- RtlCopyMemory(&Vcb->devices[0].devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
+
+ if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
+ if (tp.item->size < sizeof(DEV_ITEM)) {
+ ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
+ } else {
+ DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
+ LIST_ENTRY* le;
+ BOOL done = FALSE;
+
+ le = Vcb->devices.Flink;
+ while (le != &Vcb->devices) {
+ device* dev = CONTAINING_RECORD(le, device, list_entry);
+
+ if (dev->devobj && RtlCompareMemory(&dev->devitem.device_uuid, &di->device_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+ RtlCopyMemory(&dev->devitem, tp.item->data, min(tp.item->size, sizeof(DEV_ITEM)));
+
+ if (le != Vcb->devices.Flink)
+ init_device(Vcb, dev, TRUE);
+
+ done = TRUE;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!done && Vcb->vde) {
+ volume_device_extension* vde = Vcb->vde;
+ pdo_device_extension* pdode = vde->pdode;
+
+ ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
+
+ if (Vcb->devices_loaded < Vcb->superblock.num_devices) {
+ le = pdode->children.Flink;
+
+ while (le != &pdode->children) {
+ volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry);
+
+ if (RtlCompareMemory(&di->device_uuid, &vc->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+ device* dev;
+
+ dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+ if (!dev) {
+ ExReleaseResourceLite(&pdode->child_lock);
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(dev, sizeof(device));
+
+ dev->devobj = vc->devobj;
+ RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
+ dev->seeding = vc->seeding;
+ init_device(Vcb, dev, FALSE);
+
+ if (dev->devitem.num_bytes > vc->size) {
+ WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", tp.item->key.offset,
+ dev->devitem.num_bytes, vc->size);
+
+ dev->devitem.num_bytes = vc->size;
+ }
+
+ dev->disk_num = vc->disk_num;
+ dev->part_num = vc->part_num;
+ add_device_to_list(Vcb, dev);
+ Vcb->devices_loaded++;
+
+ done = TRUE;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!done) {
+ if (!Vcb->options.allow_degraded) {
+ ERR("volume not found: device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp.item->key.offset,
+ 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],
+ 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]);
+ } else {
+ device* dev;
+
+ dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+ if (!dev) {
+ ExReleaseResourceLite(&pdode->child_lock);
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(dev, sizeof(device));
+
+ // Missing device, so we keep dev->devobj as NULL
+ RtlCopyMemory(&dev->devitem, di, min(tp.item->size, sizeof(DEV_ITEM)));
+ InitializeListHead(&dev->trim_list);
+
+ add_device_to_list(Vcb, dev);
+ Vcb->devices_loaded++;
+ }
+ }
+ } else
+ ERR("unexpected device %llx found\n", tp.item->key.offset);
+
+ ExReleaseResourceLite(&pdode->child_lock);
+ }
+ }
} else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
if (tp.item->size < sizeof(CHUNK_ITEM)) {
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));
- } else {
- c = ExAllocatePoolWithTag(PagedPool, sizeof(chunk), ALLOC_TAG);
-
+ } else {
+ c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
+
if (!c) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
c->size = tp.item->size;
c->offset = tp.item->key.offset;
c->used = c->oldused = 0;
+ c->cache = c->old_cache = NULL;
+ c->created = FALSE;
+ c->readonly = FALSE;
+ c->reloc = FALSE;
+ c->cache_loaded = FALSE;
+ c->changed = FALSE;
c->space_changed = FALSE;
-
- c->chunk_item = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
-
+ c->balance_num = 0;
+
+ c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, tp.item->size, ALLOC_TAG);
+
if (!c->chunk_item) {
ERR("out of memory\n");
+ ExFreePool(c);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
- RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
-
+
+ RtlCopyMemory(c->chunk_item, tp.item->data, tp.item->size);
+
+ if (c->chunk_item->type & BLOCK_FLAG_DATA && c->chunk_item->type > Vcb->data_flags)
+ Vcb->data_flags = c->chunk_item->type;
+
+ if (c->chunk_item->type & BLOCK_FLAG_METADATA && c->chunk_item->type > Vcb->metadata_flags)
+ Vcb->metadata_flags = c->chunk_item->type;
+
+ if (c->chunk_item->type & BLOCK_FLAG_SYSTEM && c->chunk_item->type > Vcb->system_flags)
+ Vcb->system_flags = c->chunk_item->type;
+
+ if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
+ if (c->chunk_item->sub_stripes == 0 || c->chunk_item->sub_stripes > c->chunk_item->num_stripes) {
+ ERR("chunk %llx: invalid stripes (num_stripes %u, sub_stripes %u)\n", c->offset, c->chunk_item->num_stripes, c->chunk_item->sub_stripes);
+ ExFreePool(c->chunk_item);
+ ExFreePool(c);
+ return STATUS_INTERNAL_ERROR;
+ }
+ }
+
if (c->chunk_item->num_stripes > 0) {
CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
-
- c->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
-
+ UINT16 i;
+
+ c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * c->chunk_item->num_stripes, ALLOC_TAG);
+
if (!c->devices) {
ERR("out of memory\n");
+ ExFreePool(c->chunk_item);
+ ExFreePool(c);
return STATUS_INSUFFICIENT_RESOURCES;
}
-
+
for (i = 0; i < c->chunk_item->num_stripes; i++) {
c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
TRACE("device %llu = %p\n", i, c->devices[i]);
+
+ if (!c->devices[i]) {
+ ERR("missing device\n");
+ ExFreePool(c->chunk_item);
+ ExFreePool(c);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (c->devices[i]->readonly)
+ c->readonly = TRUE;
}
- } else
- c->devices = NULL;
-
+ } else {
+ ERR("chunk %llx: number of stripes is 0\n", c->offset);
+ ExFreePool(c->chunk_item);
+ ExFreePool(c);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ ExInitializeResourceLite(&c->lock);
+ ExInitializeResourceLite(&c->changed_extents_lock);
+
InitializeListHead(&c->space);
+ InitializeListHead(&c->space_size);
+ InitializeListHead(&c->deleting);
+ InitializeListHead(&c->changed_extents);
+
+ InitializeListHead(&c->range_locks);
+ ExInitializeResourceLite(&c->range_locks_lock);
+ KeInitializeEvent(&c->range_locks_event, NotificationEvent, FALSE);
+
+ InitializeListHead(&c->partial_stripes);
+ ExInitializeResourceLite(&c->partial_stripes_lock);
+
+ c->last_alloc_set = FALSE;
+
+ c->last_stripe = 0;
InsertTailList(&Vcb->chunks, &c->list_entry);
+
+ c->list_entry_balance.Flink = NULL;
}
}
-
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
-
- if (b) {
- free_traverse_ptr(&tp);
+
+ b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
+
+ if (b)
tp = next_tp;
- }
} while (b);
-
- free_traverse_ptr(&tp);
-
+
Vcb->log_to_phys_loaded = TRUE;
-
+
+ if (Vcb->data_flags == 0)
+ Vcb->data_flags = BLOCK_FLAG_DATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID0 : 0);
+
+ if (Vcb->metadata_flags == 0)
+ Vcb->metadata_flags = BLOCK_FLAG_METADATA | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
+
+ if (Vcb->system_flags == 0)
+ Vcb->system_flags = BLOCK_FLAG_SYSTEM | (Vcb->superblock.num_devices > 1 ? BLOCK_FLAG_RAID1 : BLOCK_FLAG_DUPLICATE);
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) {
+ Vcb->metadata_flags |= BLOCK_FLAG_DATA;
+ Vcb->data_flags = Vcb->metadata_flags;
+ }
+
return STATUS_SUCCESS;
}
-static BOOL load_stored_free_space_cache(device_extension* Vcb, chunk* c) {
- KEY searchkey;
- traverse_ptr tp, tp2;
- FREE_SPACE_ITEM* fsi;
- UINT64 inode, num_sectors, i, generation;
- INODE_ITEM* ii;
- UINT8* data;
- NTSTATUS Status;
- UINT32 *checksums, crc32;
+void protect_superblocks(_Inout_ chunk* c) {
+ UINT16 i = 0, j;
+ UINT64 off_start, off_end;
+
+ // The Linux driver also protects all the space before the first superblock.
+ // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
+ // evidently Linux assumes the chunk at 0 is always SINGLE.
+ if (c->offset < superblock_addrs[0])
+ space_list_subtract(c, FALSE, c->offset, superblock_addrs[0] - c->offset, NULL);
+
+ while (superblock_addrs[i] != 0) {
+ CHUNK_ITEM* ci = c->chunk_item;
+ CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
+
+ if (ci->type & BLOCK_FLAG_RAID0 || ci->type & BLOCK_FLAG_RAID10) {
+ for (j = 0; j < ci->num_stripes; j++) {
+ UINT16 sub_stripes = max(ci->sub_stripes, 1);
+
+ if (cis[j].offset + (ci->size * ci->num_stripes / sub_stripes) > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
#ifdef _DEBUG
- FREE_SPACE_ENTRY* fse;
- UINT64 num_entries;
+ UINT64 startoff;
+ UINT16 startoffstripe;
#endif
-
- TRACE("(%p, %llx)\n", Vcb, c->offset);
-
- if (Vcb->superblock.generation != Vcb->superblock.cache_generation)
- return FALSE;
-
- searchkey.obj_id = FREE_SPACE_CACHE_ID;
- searchkey.obj_type = 0;
- searchkey.offset = c->offset;
-
- Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return FALSE;
- }
-
- if (keycmp(&tp.item->key, &searchkey)) {
- WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- if (tp.item->size < sizeof(FREE_SPACE_ITEM)) {
- WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(FREE_SPACE_ITEM));
- free_traverse_ptr(&tp);
- return FALSE;
- }
-
- fsi = (FREE_SPACE_ITEM*)tp.item->data;
-
- if (fsi->generation != Vcb->superblock.cache_generation) {
- WARN("cache had generation %llx, expecting %llx\n", fsi->generation, Vcb->superblock.cache_generation);
- free_traverse_ptr(&tp);
- return FALSE;
+
+ TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+ off_start = superblock_addrs[i] - cis[j].offset;
+ off_start -= off_start % ci->stripe_length;
+ off_start *= ci->num_stripes / sub_stripes;
+ off_start += (j / sub_stripes) * ci->stripe_length;
+
+ off_end = off_start + ci->stripe_length;
+
+#ifdef _DEBUG
+ get_raid0_offset(off_start, ci->stripe_length, ci->num_stripes / sub_stripes, &startoff, &startoffstripe);
+ TRACE("j = %u, startoffstripe = %u\n", j, startoffstripe);
+ TRACE("startoff = %llx, superblock = %llx\n", startoff + cis[j].offset, superblock_addrs[i]);
+#endif
+
+ space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+ }
+ }
+ } else if (ci->type & BLOCK_FLAG_RAID5) {
+ UINT64 stripe_size = ci->size / (ci->num_stripes - 1);
+
+ for (j = 0; j < ci->num_stripes; j++) {
+ if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+ TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+ off_start = superblock_addrs[i] - cis[j].offset;
+ off_start -= off_start % ci->stripe_length;
+ off_start *= ci->num_stripes - 1;
+
+ off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
+ off_end *= ci->num_stripes - 1;
+
+ TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
+
+ space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+ }
+ }
+ } else if (ci->type & BLOCK_FLAG_RAID6) {
+ UINT64 stripe_size = ci->size / (ci->num_stripes - 2);
+
+ for (j = 0; j < ci->num_stripes; j++) {
+ if (cis[j].offset + stripe_size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+ TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+ off_start = superblock_addrs[i] - cis[j].offset;
+ off_start -= off_start % ci->stripe_length;
+ off_start *= ci->num_stripes - 2;
+
+ off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), ci->stripe_length);
+ off_end *= ci->num_stripes - 2;
+
+ TRACE("cutting out %llx, size %llx\n", c->offset + off_start, off_end - off_start);
+
+ space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+ }
+ }
+ } else { // SINGLE, DUPLICATE, RAID1
+ for (j = 0; j < ci->num_stripes; j++) {
+ if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
+ TRACE("cut out superblock in chunk %llx\n", c->offset);
+
+ // The Linux driver protects the whole stripe in which the superblock lives
+
+ off_start = ((superblock_addrs[i] - cis[j].offset) / c->chunk_item->stripe_length) * c->chunk_item->stripe_length;
+ off_end = sector_align(superblock_addrs[i] - cis[j].offset + sizeof(superblock), c->chunk_item->stripe_length);
+
+ space_list_subtract(c, FALSE, c->offset + off_start, off_end - off_start, NULL);
+ }
+ }
+ }
+
+ i++;
}
-
- if (fsi->key.obj_type != TYPE_INODE_ITEM) {
- WARN("cache pointed to something other than an INODE_ITEM\n");
- free_traverse_ptr(&tp);
- return FALSE;
+}
+
+NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
+ LIST_ENTRY* le = Vcb->chunks.Flink;
+ chunk* c;
+ KEY searchkey;
+ traverse_ptr tp;
+ BLOCK_GROUP_ITEM* bgi;
+ NTSTATUS Status;
+
+ searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
+
+ while (le != &Vcb->chunks) {
+ c = CONTAINING_RECORD(le, chunk, list_entry);
+
+ searchkey.obj_id = c->offset;
+ searchkey.offset = c->chunk_item->size;
+
+ Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ return Status;
+ }
+
+ if (!keycmp(searchkey, tp.item->key)) {
+ if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
+ bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
+
+ c->used = c->oldused = bgi->used;
+
+ TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
+ } else {
+ ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
+ 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));
+ }
+ }
+
+ le = le->Flink;
}
-
- if (fsi->num_bitmaps > 0) {
- WARN("cache had bitmaps, unsure of how to deal with these\n");
- free_traverse_ptr(&tp);
- return FALSE;
+
+ Vcb->chunk_usage_found = TRUE;
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS load_sys_chunks(_In_ device_extension* Vcb) {
+ KEY key;
+ ULONG n = Vcb->superblock.n;
+
+ while (n > 0) {
+ if (n > sizeof(KEY)) {
+ RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
+ n -= sizeof(KEY);
+ } else
+ return STATUS_SUCCESS;
+
+ TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
+
+ if (key.obj_type == TYPE_CHUNK_ITEM) {
+ CHUNK_ITEM* ci;
+ USHORT cisize;
+ sys_chunk* sc;
+
+ if (n < sizeof(CHUNK_ITEM))
+ return STATUS_SUCCESS;
+
+ ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
+ cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
+
+ if (n < cisize)
+ return STATUS_SUCCESS;
+
+ sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
+
+ if (!sc) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ sc->key = key;
+ sc->size = cisize;
+ sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
+
+ if (!sc->data) {
+ ERR("out of memory\n");
+ ExFreePool(sc);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(sc->data, ci, sc->size);
+ InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
+
+ n -= cisize;
+ } else {
+ ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
+ return STATUS_INTERNAL_ERROR;
+ }
}
-
- inode = fsi->key.obj_id;
-
- searchkey = fsi->key;
-#ifdef _DEBUG
- num_entries = fsi->num_entries;
-#endif
-
- Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- free_traverse_ptr(&tp);
- return FALSE;
+
+ return STATUS_SUCCESS;
+}
+
+_Ret_maybenull_
+static root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp) {
+ LIST_ENTRY* le;
+
+ static char fn[] = "default";
+ static UINT32 crc32 = 0x8dbfc2d2;
+
+ if (Vcb->options.subvol_id != 0) {
+ le = Vcb->roots.Flink;
+ while (le != &Vcb->roots) {
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r->id == Vcb->options.subvol_id)
+ return r;
+
+ le = le->Flink;
+ }
}
-
- free_traverse_ptr(&tp);
-
- if (keycmp(&tp2.item->key, &searchkey)) {
- WARN("(%llx,%x,%llx) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
- free_traverse_ptr(&tp2);
- return FALSE;
+
+ if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
+ NTSTATUS Status;
+ KEY searchkey;
+ traverse_ptr tp;
+ DIR_ITEM* di;
+
+ searchkey.obj_id = Vcb->superblock.root_dir_objectid;
+ searchkey.obj_type = TYPE_DIR_ITEM;
+ searchkey.offset = crc32;
+
+ Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto end;
+ }
+
+ if (keycmp(tp.item->key, searchkey)) {
+ ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
+ goto end;
+ }
+
+ if (tp.item->size < sizeof(DIR_ITEM)) {
+ 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));
+ goto end;
+ }
+
+ di = (DIR_ITEM*)tp.item->data;
+
+ if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
+ 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(DIR_ITEM) - 1 + di->n);
+ goto end;
+ }
+
+ if (di->n != strlen(fn) || RtlCompareMemory(di->name, fn, di->n) != di->n) {
+ ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
+ goto end;
+ }
+
+ if (di->key.obj_type != TYPE_ROOT_ITEM) {
+ ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di->key.obj_id, di->key.obj_type, di->key.offset);
+ goto end;
+ }
+
+ le = Vcb->roots.Flink;
+ while (le != &Vcb->roots) {
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r->id == di->key.obj_id)
+ return r;
+
+ le = le->Flink;
+ }
+
+ ERR("could not find root %llx, using default instead\n", di->key.obj_id);
}
-
- if (tp2.item->size < sizeof(INODE_ITEM)) {
- WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp2.item->size, sizeof(INODE_ITEM));
- free_traverse_ptr(&tp2);
- return FALSE;
+
+end:
+ le = Vcb->roots.Flink;
+ while (le != &Vcb->roots) {
+ root* r = CONTAINING_RECORD(le, root, list_entry);
+
+ if (r->id == BTRFS_ROOT_FSTREE)
+ return r;
+
+ le = le->Flink;
}
-
- ii = (INODE_ITEM*)tp2.item->data;
-
- data = ExAllocatePoolWithTag(PagedPool, ii->st_size, ALLOC_TAG);
-
- if (!data) {
+
+ return NULL;
+}
+
+void init_file_cache(_In_ PFILE_OBJECT FileObject, _In_ CC_FILE_SIZES* ccfs) {
+ TRACE("(%p, %p)\n", FileObject, ccfs);
+
+ CcInitializeCacheMap(FileObject, ccfs, FALSE, cache_callbacks, FileObject);
+
+ if (diskacc)
+ fCcSetAdditionalCacheAttributesEx(FileObject, CC_ENABLE_DISK_IO_ACCOUNTING);
+
+ CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
+}
+
+static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
+ device_extension* Vcb = DeviceObject->DeviceExtension;
+ ULONG i;
+
+ Vcb->calcthreads.num_threads = KeQueryActiveProcessorCount(NULL);
+
+ Vcb->calcthreads.threads = ExAllocatePoolWithTag(NonPagedPool, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads, ALLOC_TAG);
+ if (!Vcb->calcthreads.threads) {
ERR("out of memory\n");
- free_traverse_ptr(&tp2);
- return FALSE;
+ return STATUS_INSUFFICIENT_RESOURCES;
}
-
- Status = read_file(Vcb, Vcb->root_root, inode, data, 0, ii->st_size, NULL);
- if (!NT_SUCCESS(Status)) {
- ERR("read_file returned %08x\n", Status);
- ExFreePool(data);
- free_traverse_ptr(&tp2);
- return FALSE;
+
+ InitializeListHead(&Vcb->calcthreads.job_list);
+ ExInitializeResourceLite(&Vcb->calcthreads.lock);
+ KeInitializeEvent(&Vcb->calcthreads.event, NotificationEvent, FALSE);
+
+ RtlZeroMemory(Vcb->calcthreads.threads, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads);
+
+ for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
+ NTSTATUS Status;
+
+ Vcb->calcthreads.threads[i].DeviceObject = DeviceObject;
+ KeInitializeEvent(&Vcb->calcthreads.threads[i].finished, NotificationEvent, FALSE);
+
+ Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, NULL, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
+ if (!NT_SUCCESS(Status)) {
+ ULONG j;
+
+ ERR("PsCreateSystemThread returned %08x\n", Status);
+
+ for (j = 0; j < i; j++) {
+ Vcb->calcthreads.threads[i].quit = TRUE;
+ }
+
+ KeSetEvent(&Vcb->calcthreads.event, 0, FALSE);
+
+ return Status;
+ }
}
-
- num_sectors = ii->st_size / Vcb->superblock.sector_size;
-
- generation = *(data + (num_sectors * sizeof(UINT32)));
-
- if (generation != Vcb->superblock.cache_generation) {
- ERR("generation was %llx, expected %llx\n", generation, Vcb->superblock.cache_generation);
- ExFreePool(data);
- free_traverse_ptr(&tp2);
+
+ return STATUS_SUCCESS;
+}
+
+static BOOL is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
+ NTSTATUS Status;
+ MOUNTDEV_NAME mdn, *mdn2;
+ ULONG mdnsize;
+
+ Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
+ if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+ ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
return FALSE;
}
-
- checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * num_sectors, ALLOC_TAG); // FIXME - get rid of this
-
- if (!checksums) {
+
+ mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
+
+ mdn2 = ExAllocatePoolWithTag(PagedPool, mdnsize, ALLOC_TAG);
+ if (!mdn2) {
ERR("out of memory\n");
- ExFreePool(data);
- free_traverse_ptr(&tp2);
return FALSE;
}
-
- RtlCopyMemory(checksums, data, sizeof(UINT32) * num_sectors);
-
- for (i = 0; i < num_sectors; i++) {
- if (i * Vcb->superblock.sector_size > sizeof(UINT32) * num_sectors)
- crc32 = ~calc_crc32c(0xffffffff, &data[i * Vcb->superblock.sector_size], Vcb->superblock.sector_size);
- else if ((i + 1) * Vcb->superblock.sector_size < sizeof(UINT32) * num_sectors)
- crc32 = 0; // FIXME - test this
- else
- crc32 = ~calc_crc32c(0xffffffff, &data[sizeof(UINT32) * num_sectors], ((i + 1) * Vcb->superblock.sector_size) - (sizeof(UINT32) * num_sectors));
-
- if (crc32 != checksums[i]) {
- WARN("checksum %llu was %08x, expected %08x\n", i, crc32, checksums[i]);
- ExFreePool(checksums);
- ExFreePool(data);
- free_traverse_ptr(&tp2);
- return FALSE;
- }
+
+ Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, TRUE, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+ ExFreePool(mdn2);
+ return FALSE;
}
-
- ExFreePool(checksums);
-
-#ifdef _DEBUG
- fse = (FREE_SPACE_ENTRY*)&data[(sizeof(UINT32) * num_sectors) + sizeof(UINT64)];
- for (i = 0; i < num_entries; i++) {
- TRACE("(%llx,%llx,%x)\n", fse[i].offset, fse[i].size, fse[i].type);
+ if (mdn2->NameLength > wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR) &&
+ RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) == wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) {
+ ExFreePool(mdn2);
+ return TRUE;
}
-#endif
-
- FIXME("FIXME - read cache\n");
-
- ExFreePool(data);
- free_traverse_ptr(&tp2);
-
+
+ ExFreePool(mdn2);
+
return FALSE;
}
-static NTSTATUS load_free_space_cache(device_extension* Vcb, chunk* c) {
- traverse_ptr tp, next_tp;
- KEY searchkey;
- UINT64 lastaddr;
- BOOL b;
- space *s, *s2;
- LIST_ENTRY* le;
+static NTSTATUS get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _In_ const GUID* guid) {
NTSTATUS Status;
-
- load_stored_free_space_cache(Vcb, c);
-
- TRACE("generating free space cache for chunk %llx\n", c->offset);
-
- searchkey.obj_id = c->offset;
- searchkey.obj_type = TYPE_EXTENT_ITEM;
- searchkey.offset = 0;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
+ WCHAR *list = NULL, *s;
+
+ Status = IoGetDeviceInterfaces((PVOID)guid, NULL, 0, &list);
if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
+ ERR("IoGetDeviceInterfaces returned %08x\n", Status);
return Status;
}
-
- lastaddr = c->offset;
-
- do {
- if (tp.item->key.obj_id >= c->offset + c->chunk_item->size)
- break;
-
- if (tp.item->key.obj_id >= c->offset && (tp.item->key.obj_type == TYPE_EXTENT_ITEM || tp.item->key.obj_type == TYPE_METADATA_ITEM)) {
- if (tp.item->key.obj_id > lastaddr) {
- s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s) {
+
+ s = list;
+ while (s[0] != 0) {
+ PFILE_OBJECT FileObject;
+ PDEVICE_OBJECT devobj;
+ UNICODE_STRING name;
+
+ name.Length = name.MaximumLength = (USHORT)wcslen(s) * sizeof(WCHAR);
+ name.Buffer = s;
+
+ if (NT_SUCCESS(IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &FileObject, &devobj))) {
+ if (DeviceObject == devobj || DeviceObject == FileObject->DeviceObject) {
+ ObDereferenceObject(FileObject);
+
+ pnp_name->Buffer = ExAllocatePoolWithTag(PagedPool, name.Length, ALLOC_TAG);
+ if (!pnp_name->Buffer) {
ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end;
}
-
- s->offset = lastaddr;
- s->size = tp.item->key.obj_id - lastaddr;
- s->type = SPACE_TYPE_FREE;
- InsertTailList(&c->space, &s->list_entry);
-
- TRACE("(%llx,%llx)\n", s->offset, s->size);
- }
-
- if (tp.item->key.obj_type == TYPE_METADATA_ITEM)
- lastaddr = tp.item->key.obj_id + Vcb->superblock.node_size;
- else
- lastaddr = tp.item->key.obj_id + tp.item->key.offset;
- }
-
- b = find_next_item(Vcb, &tp, &next_tp, FALSE);
- if (b) {
- free_traverse_ptr(&tp);
- tp = next_tp;
- }
- } while (b);
-
- if (lastaddr < c->offset + c->chunk_item->size) {
- s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- s->offset = lastaddr;
- s->size = c->offset + c->chunk_item->size - lastaddr;
- s->type = SPACE_TYPE_FREE;
- InsertTailList(&c->space, &s->list_entry);
-
- TRACE("(%llx,%llx)\n", s->offset, s->size);
- }
-
- free_traverse_ptr(&tp);
-
- // add allocated space
-
- lastaddr = c->offset;
-
- le = c->space.Flink;
- while (le != &c->space) {
- s = CONTAINING_RECORD(le, space, list_entry);
-
- if (s->offset > lastaddr) {
- s2 = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s2) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+
+ RtlCopyMemory(pnp_name->Buffer, name.Buffer, name.Length);
+ pnp_name->Length = pnp_name->MaximumLength = name.Length;
+
+ Status = STATUS_SUCCESS;
+ goto end;
}
-
- s2->offset = lastaddr;
- s2->size = s->offset - lastaddr;
- s2->type = SPACE_TYPE_USED;
-
- InsertTailList(&s->list_entry, &s2->list_entry);
- }
-
- lastaddr = s->offset + s->size;
-
- le = le->Flink;
- }
-
- if (lastaddr < c->offset + c->chunk_item->size) {
- s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
-
- if (!s) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
+
+ ObDereferenceObject(FileObject);
}
-
- s->offset = lastaddr;
- s->size = c->offset + c->chunk_item->size - lastaddr;
- s->type = SPACE_TYPE_USED;
- InsertTailList(&c->space, &s->list_entry);
- }
-
- le = c->space.Flink;
- while (le != &c->space) {
- s = CONTAINING_RECORD(le, space, list_entry);
-
- TRACE("%llx,%llx,%u\n", s->offset, s->size, s->type);
-
- le = le->Flink;
+
+ s = &s[wcslen(s) + 1];
}
-
- return STATUS_SUCCESS;
+
+ pnp_name->Length = pnp_name->MaximumLength = 0;
+ pnp_name->Buffer = 0;
+
+ Status = STATUS_NOT_FOUND;
+
+end:
+ if (list)
+ ExFreePool(list);
+
+ return Status;
}
-void protect_superblocks(device_extension* Vcb, chunk* c) {
- int i = 0, j;
- UINT64 addr;
-
- // FIXME - this will need modifying for RAID
-
- while (superblock_addrs[i] != 0) {
- CHUNK_ITEM* ci = c->chunk_item;
- CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&ci[1];
-
- for (j = 0; j < ci->num_stripes; j++) {
- if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
- UINT32 size;
-
- TRACE("cut out superblock in chunk %llx\n", c->offset);
-
- addr = (superblock_addrs[i] - cis[j].offset) + c->offset;
- TRACE("addr %llx\n", addr);
-
- // This prevents trees from spanning a stripe boundary, which btrfs check complains
- // about. It also prevents the chunk tree being placed at 0x11000, which for some
- // reason makes the FS unmountable on Linux (it tries to read 0x10000, i.e. the
- // superblock, instead).
- if (ci->type & BLOCK_FLAG_SYSTEM || ci->type & BLOCK_FLAG_METADATA)
- size = max(sizeof(superblock), Vcb->superblock.node_size);
- else
- size = sizeof(superblock);
-
- add_to_space_list(c, addr, size, SPACE_TYPE_USED);
- }
- }
-
- i++;
+NTSTATUS get_device_pnp_name(_In_ PDEVICE_OBJECT DeviceObject, _Out_ PUNICODE_STRING pnp_name, _Out_ const GUID** guid) {
+ NTSTATUS Status;
+
+ Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_VOLUME);
+ if (NT_SUCCESS(Status)) {
+ *guid = &GUID_DEVINTERFACE_VOLUME;
+ return Status;
+ }
+
+ Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_HIDDEN_VOLUME);
+ if (NT_SUCCESS(Status)) {
+ *guid = &GUID_DEVINTERFACE_HIDDEN_VOLUME;
+ return Status;
+ }
+
+ Status = get_device_pnp_name_guid(DeviceObject, pnp_name, &GUID_DEVINTERFACE_DISK);
+ if (NT_SUCCESS(Status)) {
+ *guid = &GUID_DEVINTERFACE_DISK;
+ return Status;
}
+
+ return STATUS_NOT_FOUND;
}
-static NTSTATUS STDCALL find_chunk_usage(device_extension* Vcb) {
- LIST_ENTRY* le = Vcb->chunks.Flink;
- chunk* c;
- KEY searchkey;
- traverse_ptr tp;
- BLOCK_GROUP_ITEM* bgi;
+_Success_(return>=0)
+static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ BOOL* no_pnp) {
NTSTATUS Status;
-
-// c00000,c0,800000
-// block_group_item size=7f0000 chunktreeid=100 flags=1
-
- searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
-
- while (le != &Vcb->chunks) {
- c = CONTAINING_RECORD(le, chunk, list_entry);
-
- searchkey.obj_id = c->offset;
- searchkey.offset = c->chunk_item->size;
-
- Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
- if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- return Status;
- }
-
- if (!keycmp(&searchkey, &tp.item->key)) {
- if (tp.item->size >= sizeof(BLOCK_GROUP_ITEM)) {
- bgi = (BLOCK_GROUP_ITEM*)tp.item->data;
-
- c->used = c->oldused = bgi->used;
-
- TRACE("chunk %llx has %llx bytes used\n", c->offset, c->used);
- } else {
- ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
- 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));
- }
- }
-
- free_traverse_ptr(&tp);
-// if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
-// cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
-//
-// return (addr - c->offset) + cis->offset;
-// }
-
- // FIXME - make sure we free occasionally after doing one of these, or we
- // might use up a lot of memory with a big disk.
-
- Status = load_free_space_cache(Vcb, c);
- if (!NT_SUCCESS(Status)) {
- ERR("load_free_space_cache returned %08x\n", Status);
- return Status;
- }
-
- protect_superblocks(Vcb, c);
+ ULONG to_read;
+ superblock* sb;
+ UINT32 crc32;
+ UNICODE_STRING pnp_name;
+ const GUID* guid;
- le = le->Flink;
+ to_read = DeviceObject->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), DeviceObject->SectorSize);
+
+ sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+ if (!sb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
}
-
- return STATUS_SUCCESS;
+
+ Status = sync_read_phys(DeviceObject, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("sync_read_phys returned %08x\n", Status);
+ goto end;
+ }
+
+ if (sb->magic != BTRFS_MAGIC) {
+ Status = STATUS_SUCCESS;
+ goto end;
+ }
+
+ crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+
+ if (crc32 != *((UINT32*)sb->checksum)) {
+ WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+ Status = STATUS_SUCCESS;
+ goto end;
+ }
+
+ DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
+
+ pnp_name.Buffer = NULL;
+
+ Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
+ if (!NT_SUCCESS(Status)) {
+ WARN("get_device_pnp_name returned %08x\n", Status);
+ pnp_name.Length = 0;
+ }
+
+ if (pnp_name.Length == 0)
+ *no_pnp = TRUE;
+ else {
+ *no_pnp = FALSE;
+ volume_arrival(drvobj, &pnp_name);
+ }
+
+ if (pnp_name.Buffer)
+ ExFreePool(pnp_name.Buffer);
+
+ Status = STATUS_SUCCESS;
+
+end:
+ ExFreePool(sb);
+
+ return Status;
}
-// static void STDCALL root_test(device_extension* Vcb) {
-// root* r;
-// KEY searchkey;
-// traverse_ptr tp, next_tp;
-// BOOL b;
-//
-// r = Vcb->roots;
-// while (r) {
-// if (r->id == 0x102)
-// break;
-// r = r->next;
-// }
-//
-// if (!r) {
-// ERR("Could not find root tree.\n");
-// return;
-// }
-//
-// searchkey.obj_id = 0x1b6;
-// searchkey.obj_type = 0xb;
-// searchkey.offset = 0;
-//
-// if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
-// ERR("Could not find first item.\n");
-// return;
-// }
-//
-// b = TRUE;
-// do {
-// TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
-//
-// b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
-//
-// if (b) {
-// free_traverse_ptr(&tp);
-// tp = next_tp;
-// }
-// } while (b);
-//
-// free_traverse_ptr(&tp);
-// }
-
-static NTSTATUS load_sys_chunks(device_extension* Vcb) {
- KEY key;
- ULONG n = Vcb->superblock.n;
-
- while (n > 0) {
- if (n > sizeof(KEY)) {
- RtlCopyMemory(&key, &Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n], sizeof(KEY));
- n -= sizeof(KEY);
- } else
- return STATUS_SUCCESS;
-
- TRACE("bootstrap: %llx,%x,%llx\n", key.obj_id, key.obj_type, key.offset);
-
- if (key.obj_type == TYPE_CHUNK_ITEM) {
- CHUNK_ITEM* ci;
- ULONG cisize;
- sys_chunk* sc;
-
- if (n < sizeof(CHUNK_ITEM))
- return STATUS_SUCCESS;
-
- ci = (CHUNK_ITEM*)&Vcb->superblock.sys_chunk_array[Vcb->superblock.n - n];
- cisize = sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE));
-
- if (n < cisize)
- return STATUS_SUCCESS;
-
- sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
-
- if (!sc) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- sc->key = key;
- sc->size = cisize;
- sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
-
- if (!sc->data) {
- ERR("out of memory\n");
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
- RtlCopyMemory(sc->data, ci, sc->size);
- InsertTailList(&Vcb->sys_chunks, &sc->list_entry);
-
- n -= cisize;
- } else {
- ERR("unexpected item %llx,%x,%llx in bootstrap\n", key.obj_id, key.obj_type, key.offset);
- return STATUS_INTERNAL_ERROR;
+static BOOL still_has_superblock(_In_ PDEVICE_OBJECT device) {
+ NTSTATUS Status;
+ ULONG to_read;
+ superblock* sb;
+ PDEVICE_OBJECT device2;
+
+ if (!device)
+ return FALSE;
+
+ to_read = device->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), device->SectorSize);
+
+ sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+ if (!sb) {
+ ERR("out of memory\n");
+ return FALSE;
+ }
+
+ Status = sync_read_phys(device, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ERR("Failed to read superblock: %08x\n", Status);
+ ExFreePool(sb);
+ return FALSE;
+ }
+
+ if (sb->magic != BTRFS_MAGIC) {
+ TRACE("not a BTRFS volume\n");
+ ExFreePool(sb);
+ return FALSE;
+ } else {
+ UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+
+ if (crc32 != *((UINT32*)sb->checksum)) {
+ WARN("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+ ExFreePool(sb);
+ return FALSE;
}
}
-
- return STATUS_SUCCESS;
-}
-static root* find_default_subvol(device_extension* Vcb) {
- root* subvol;
- UINT64 inode;
- UINT8 type;
- UNICODE_STRING filename;
-
- static WCHAR fn[] = L"default";
- static UINT32 crc32 = 0x8dbfc2d2;
-
- if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) {
- filename.Buffer = fn;
- filename.Length = filename.MaximumLength = (USHORT)wcslen(fn) * sizeof(WCHAR);
-
- if (!find_file_in_dir_with_crc32(Vcb, &filename, crc32, Vcb->root_root, Vcb->superblock.root_dir_objectid, &subvol, &inode, &type, NULL))
- WARN("couldn't find default subvol DIR_ITEM, using default tree\n");
- else
- return subvol;
- }
-
- subvol = Vcb->roots;
- while (subvol && subvol->id != BTRFS_ROOT_FSTREE)
- subvol = subvol->next;
-
- return subvol;
+ device2 = device;
+
+ do {
+ device2->Flags &= ~DO_VERIFY_VOLUME;
+ device2 = IoGetLowerDeviceObject(device2);
+ } while (device2);
+
+ ExFreePool(sb);
+ return TRUE;
}
-static NTSTATUS STDCALL mount_vol(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
- PIO_STACK_LOCATION Stack;
+static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
+ PIO_STACK_LOCATION IrpSp;
PDEVICE_OBJECT NewDeviceObject = NULL;
- PDEVICE_OBJECT DeviceToMount;
+ PDEVICE_OBJECT DeviceToMount, readobj;
NTSTATUS Status;
device_extension* Vcb = NULL;
- PARTITION_INFORMATION_EX piex;
- UINT64 i;
- LIST_ENTRY* le;
+ LIST_ENTRY *le, batchlist;
KEY searchkey;
traverse_ptr tp;
-
- TRACE("mount_vol called\n");
-
- if (DeviceObject != devobj)
- {
+ fcb* root_fcb = NULL;
+ ccb* root_ccb = NULL;
+ BOOL init_lookaside = FALSE;
+ device* dev;
+ volume_device_extension* vde = NULL;
+ pdo_device_extension* pdode = NULL;
+ volume_child* vc;
+ BOOL no_pnp = FALSE;
+ UINT64 readobjsize;
+
+ TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+ if (DeviceObject != master_devobj) {
Status = STATUS_INVALID_DEVICE_REQUEST;
goto exit;
}
- Stack = IoGetCurrentIrpStackLocation(Irp);
- DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ DeviceToMount = IrpSp->Parameters.MountVolume.DeviceObject;
+
+ if (!is_btrfs_volume(DeviceToMount)) {
+ Status = check_mount_device(DeviceToMount, &no_pnp);
+ if (!NT_SUCCESS(Status))
+ WARN("check_mount_device returned %08x\n", Status);
+
+ if (!no_pnp) {
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit2;
+ }
+ } else {
+ PDEVICE_OBJECT pdo;
+
+ pdo = DeviceToMount;
+
+ while (IoGetLowerDeviceObject(pdo)) {
+ pdo = IoGetLowerDeviceObject(pdo);
+ }
+
+ ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
+
+ le = pdo_list.Flink;
+ while (le != &pdo_list) {
+ pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+
+ if (pdode->pdo == pdo) {
+ vde = pdode->vde;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ ExReleaseResourceLite(&pdo_list_lock);
+
+ if (!vde || vde->type != VCB_TYPE_VOLUME) {
+ vde = NULL;
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit2;
+ }
+ }
+
+ if (vde) {
+ pdode = vde->pdode;
+
+ ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
+
+ le = pdode->children.Flink;
+ while (le != &pdode->children) {
+ LIST_ENTRY* le2 = le->Flink;
+
+ vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
+
+ if (!still_has_superblock(vc->devobj)) {
+ remove_volume_child(vde, vc, FALSE);
+
+ if (pdode->num_children == 0) {
+ ERR("error - number of devices is zero\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit2;
+ }
+
+ Status = STATUS_DEVICE_NOT_READY;
+ goto exit2;
+ }
+
+ le = le2;
+ }
+
+ if (pdode->num_children == 0 || pdode->children_loaded == 0) {
+ ERR("error - number of devices is zero\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ ExConvertExclusiveToSharedLite(&pdode->child_lock);
+
+ vc = CONTAINING_RECORD(pdode->children.Flink, volume_child, list_entry);
+
+ readobj = vc->devobj;
+ readobjsize = vc->size;
+
+ vde->device->Characteristics &= ~FILE_DEVICE_SECURE_OPEN;
+ } else {
+ GET_LENGTH_INFORMATION gli;
+
+ vc = NULL;
+ readobj = DeviceToMount;
+
+ Status = dev_ioctl(readobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+ &gli, sizeof(gli), TRUE, NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ ERR("error reading length information: %08x\n", Status);
+ goto exit;
+ }
-// Status = NtfsHasFileSystem(DeviceToMount);
-// if (!NT_SUCCESS(Status))
-// {
-// goto ByeBye;
-// }
+ readobjsize = gli.Length.QuadPart;
+ }
- Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
- &piex, sizeof(piex), TRUE);
+ Status = IoCreateDevice(drvobj, sizeof(device_extension), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &NewDeviceObject);
if (!NT_SUCCESS(Status)) {
- ERR("error reading partition information: %08x\n", Status);
+ ERR("IoCreateDevice returned %08x\n", Status);
+ Status = STATUS_UNRECOGNIZED_VOLUME;
goto exit;
}
- Status = IoCreateDevice(drvobj,
- sizeof(device_extension),
- NULL,
- FILE_DEVICE_DISK_FILE_SYSTEM,
- 0,
- FALSE,
- &NewDeviceObject);
- if (!NT_SUCCESS(Status))
- goto exit;
-
-// TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
-
NewDeviceObject->Flags |= DO_DIRECT_IO;
+
+ // Some programs seem to expect that the sector size will be 512, for
+ // FILE_NO_INTERMEDIATE_BUFFERING and the like.
+ NewDeviceObject->SectorSize = min(DeviceToMount->SectorSize, 512);
+
Vcb = (PVOID)NewDeviceObject->DeviceExtension;
RtlZeroMemory(Vcb, sizeof(device_extension));
-
- InitializeListHead(&Vcb->tree_cache);
-
+ Vcb->type = VCB_TYPE_FS;
+ Vcb->vde = vde;
+
ExInitializeResourceLite(&Vcb->tree_lock);
- Vcb->tree_lock_counter = 0;
- Vcb->open_trees = 0;
- Vcb->write_trees = 0;
+ Vcb->need_write = FALSE;
ExInitializeResourceLite(&Vcb->fcb_lock);
- ExInitializeResourceLite(&Vcb->DirResource);
-
- ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
- InsertTailList(&VcbList, &Vcb->list_entry);
- ExReleaseResourceLite(&global_loading_lock);
+ ExInitializeResourceLite(&Vcb->chunk_lock);
+ ExInitializeResourceLite(&Vcb->dirty_fcbs_lock);
+ ExInitializeResourceLite(&Vcb->dirty_filerefs_lock);
+ ExInitializeResourceLite(&Vcb->dirty_subvols_lock);
+ ExInitializeResourceLite(&Vcb->scrub.stats_lock);
ExInitializeResourceLite(&Vcb->load_lock);
ExAcquireResourceExclusiveLite(&Vcb->load_lock, TRUE);
-// Vcb->Identifier.Type = NTFS_TYPE_VCB;
-// Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
-//
-// Status = NtfsGetVolumeData(DeviceToMount,
-// Vcb);
-// if (!NT_SUCCESS(Status))
-// goto ByeBye;
-
-// Vcb->device = DeviceToMount;
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+
DeviceToMount->Flags |= DO_DIRECT_IO;
-
-// Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
-// &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
-// if (!NT_SUCCESS(Status)) {
-// ERR("error reading disk geometry: %08x\n", Status);
-// goto exit;
-// } else {
-// TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
-// Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
-// Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
-// }
-
- Vcb->length = piex.PartitionLength.QuadPart;
- TRACE("partition length = %u\n", piex.PartitionLength);
-
- Status = read_superblock(Vcb, DeviceToMount);
+
+ Status = read_superblock(Vcb, readobj, readobjsize);
if (!NT_SUCCESS(Status)) {
- Status = STATUS_UNRECOGNIZED_VOLUME;
+ if (!IoIsErrorUserInduced(Status))
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ else if (Irp->Tail.Overlay.Thread)
+ IoSetHardErrorOrVerifyDevice(Irp, readobj);
+
goto exit;
}
- if (Vcb->superblock.magic != BTRFS_MAGIC) {
- ERR("not a BTRFS volume\n");
+ if (!vde && Vcb->superblock.num_devices > 1) {
+ ERR("cannot mount multi-device FS with non-PNP device\n");
Status = STATUS_UNRECOGNIZED_VOLUME;
goto exit;
- } else {
- TRACE("btrfs magic found\n");
}
- if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
- WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
- Status = STATUS_UNRECOGNIZED_VOLUME;
+ Status = registry_load_volume_options(Vcb);
+ if (!NT_SUCCESS(Status)) {
+ ERR("registry_load_volume_options returned %08x\n", Status);
goto exit;
}
-
- le = volumes.Flink;
- while (le != &volumes) {
- volume* v = CONTAINING_RECORD(le, volume, list_entry);
-
- if (RtlCompareMemory(&Vcb->superblock.uuid, &v->fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && v->devnum < Vcb->superblock.dev_item.dev_id) {
- // skipping over device in RAID which isn't the first one
- // FIXME - hide this in My Computer
- Status = STATUS_UNRECOGNIZED_VOLUME;
- goto exit;
- }
-
- le = le->Flink;
+
+ if (pdode && pdode->children_loaded < pdode->num_children && (!Vcb->options.allow_degraded || !finished_probing || degraded_wait)) {
+ ERR("could not mount as %u device(s) missing\n", pdode->num_children - pdode->children_loaded);
+ Status = STATUS_DEVICE_NOT_READY;
+ goto exit;
}
-
- // FIXME - remove this when we can
- if (Vcb->superblock.num_devices > 1) {
- WARN("not mounting - multiple devices not yet implemented\n");
+
+ if (Vcb->options.ignore) {
+ TRACE("ignoring volume\n");
+ Status = STATUS_UNRECOGNIZED_VOLUME;
+ goto exit;
+ }
+
+ if (Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED) {
+ WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb->superblock.incompat_flags & ~INCOMPAT_SUPPORTED);
Status = STATUS_UNRECOGNIZED_VOLUME;
goto exit;
}
-
+
Vcb->readonly = FALSE;
if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
Vcb->readonly = TRUE;
}
-
+
+ if (Vcb->options.readonly)
+ Vcb->readonly = TRUE;
+
Vcb->superblock.generation++;
Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
-
- Vcb->devices = ExAllocatePoolWithTag(PagedPool, sizeof(device) * Vcb->superblock.num_devices, ALLOC_TAG);
- if (!Vcb->devices) {
+
+ InitializeListHead(&Vcb->devices);
+ dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
+ if (!dev) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
-
- Vcb->devices[0].devobj = DeviceToMount;
- RtlCopyMemory(&Vcb->devices[0].devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
-
- if (Vcb->superblock.num_devices > 1)
- RtlZeroMemory(&Vcb->devices[1], sizeof(DEV_ITEM) * (Vcb->superblock.num_devices - 1));
-
- // FIXME - find other devices, if there are any
-
+
+ dev->devobj = readobj;
+ RtlCopyMemory(&dev->devitem, &Vcb->superblock.dev_item, sizeof(DEV_ITEM));
+
+ if (dev->devitem.num_bytes > readobjsize) {
+ WARN("device %llx: DEV_ITEM says %llx bytes, but Windows only reports %llx\n", dev->devitem.dev_id,
+ dev->devitem.num_bytes, readobjsize);
+
+ dev->devitem.num_bytes = readobjsize;
+ }
+
+ dev->seeding = Vcb->superblock.flags & BTRFS_SUPERBLOCK_FLAGS_SEEDING ? TRUE : FALSE;
+
+ init_device(Vcb, dev, TRUE);
+
+ InsertTailList(&Vcb->devices, &dev->list_entry);
+ Vcb->devices_loaded = 1;
+
+ if (DeviceToMount->Flags & DO_SYSTEM_BOOT_PARTITION)
+ Vcb->disallow_dismount = TRUE;
+
TRACE("DeviceToMount = %p\n", DeviceToMount);
- TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack->Parameters.MountVolume.Vpb);
+ TRACE("IrpSp->Parameters.MountVolume.Vpb = %p\n", IrpSp->Parameters.MountVolume.Vpb);
NewDeviceObject->StackSize = DeviceToMount->StackSize + 1;
NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
-
-// find_chunk_root(Vcb);
-// Vcb->chunk_root_phys_addr = Vcb->superblock.chunk_tree_addr; // FIXME - map from logical to physical (bootstrapped)
-
-// Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr);
-
- Vcb->roots = NULL;
+
+ InitializeListHead(&Vcb->roots);
+ InitializeListHead(&Vcb->drop_roots);
+
Vcb->log_to_phys_loaded = FALSE;
-
- Vcb->max_inline = Vcb->superblock.node_size / 2;
-
-// Vcb->write_trees = NULL;
- add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, NULL);
-
+ add_root(Vcb, BTRFS_ROOT_CHUNK, Vcb->superblock.chunk_tree_addr, Vcb->superblock.chunk_root_generation, NULL);
+
if (!Vcb->chunk_root) {
ERR("Could not load chunk root.\n");
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
-
+
InitializeListHead(&Vcb->sys_chunks);
Status = load_sys_chunks(Vcb);
if (!NT_SUCCESS(Status)) {
ERR("load_sys_chunks returned %08x\n", Status);
goto exit;
}
-
+
InitializeListHead(&Vcb->chunks);
InitializeListHead(&Vcb->trees);
-
+ InitializeListHead(&Vcb->trees_hash);
+ InitializeListHead(&Vcb->all_fcbs);
+ InitializeListHead(&Vcb->dirty_fcbs);
+ InitializeListHead(&Vcb->dirty_filerefs);
+ InitializeListHead(&Vcb->dirty_subvols);
+ InitializeListHead(&Vcb->send_ops);
+
InitializeListHead(&Vcb->DirNotifyList);
+ InitializeListHead(&Vcb->scrub.errors);
FsRtlNotifyInitializeSync(&Vcb->NotifySync);
-
- Status = load_chunk_root(Vcb);
+
+ ExInitializePagedLookasideList(&Vcb->tree_data_lookaside, NULL, NULL, 0, sizeof(tree_data), ALLOC_TAG, 0);
+ ExInitializePagedLookasideList(&Vcb->traverse_ptr_lookaside, NULL, NULL, 0, sizeof(traverse_ptr), ALLOC_TAG, 0);
+ ExInitializePagedLookasideList(&Vcb->batch_item_lookaside, NULL, NULL, 0, sizeof(batch_item), ALLOC_TAG, 0);
+ ExInitializePagedLookasideList(&Vcb->fileref_lookaside, NULL, NULL, 0, sizeof(file_ref), ALLOC_TAG, 0);
+ ExInitializePagedLookasideList(&Vcb->fcb_lookaside, NULL, NULL, 0, sizeof(fcb), ALLOC_TAG, 0);
+ ExInitializePagedLookasideList(&Vcb->name_bit_lookaside, NULL, NULL, 0, sizeof(name_bit), ALLOC_TAG, 0);
+ ExInitializeNPagedLookasideList(&Vcb->range_lock_lookaside, NULL, NULL, 0, sizeof(range_lock), ALLOC_TAG, 0);
+ ExInitializeNPagedLookasideList(&Vcb->fileref_np_lookaside, NULL, NULL, 0, sizeof(file_ref_nonpaged), ALLOC_TAG, 0);
+ ExInitializeNPagedLookasideList(&Vcb->fcb_np_lookaside, NULL, NULL, 0, sizeof(fcb_nonpaged), ALLOC_TAG, 0);
+ init_lookaside = TRUE;
+
+ Vcb->Vpb = IrpSp->Parameters.MountVolume.Vpb;
+
+ Status = load_chunk_root(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("load_chunk_root returned %08x\n", Status);
goto exit;
}
-
- add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, NULL);
-
+
+ if (Vcb->superblock.num_devices > 1) {
+ if (Vcb->devices_loaded < Vcb->superblock.num_devices && (!Vcb->options.allow_degraded || !finished_probing)) {
+ ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
+
+ IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
+
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ if (dev->readonly && !Vcb->readonly) {
+ Vcb->readonly = TRUE;
+
+ le = Vcb->devices.Flink;
+ while (le != &Vcb->devices) {
+ device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+
+ if (dev2->readonly && !dev2->seeding)
+ break;
+
+ if (!dev2->readonly) {
+ Vcb->readonly = FALSE;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (Vcb->readonly)
+ WARN("setting volume to readonly\n");
+ }
+ } else {
+ if (dev->readonly) {
+ WARN("setting volume to readonly as device is readonly\n");
+ Vcb->readonly = TRUE;
+ }
+ }
+
+ add_root(Vcb, BTRFS_ROOT_ROOT, Vcb->superblock.root_tree_addr, Vcb->superblock.generation - 1, NULL);
+
if (!Vcb->root_root) {
ERR("Could not load root of roots.\n");
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
-
- Status = look_for_roots(Vcb);
+
+ Status = look_for_roots(Vcb, Irp);
if (!NT_SUCCESS(Status)) {
ERR("look_for_roots returned %08x\n", Status);
goto exit;
}
-
- Status = find_chunk_usage(Vcb);
+
+ if (!Vcb->readonly) {
+ Status = find_chunk_usage(Vcb, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_chunk_usage returned %08x\n", Status);
+ goto exit;
+ }
+ }
+
+ InitializeListHead(&batchlist);
+
+ // We've already increased the generation by one
+ if (!Vcb->readonly && (
+ Vcb->options.clear_cache ||
+ (!(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE) && Vcb->superblock.generation - 1 != Vcb->superblock.cache_generation) ||
+ (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)))) {
+ if (Vcb->options.clear_cache)
+ WARN("ClearCache option was set, clearing cache...\n");
+ else if (Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID))
+ WARN("clearing free-space tree created by buggy Linux driver\n");
+ else
+ WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb->superblock.generation - 1, Vcb->superblock.cache_generation);
+
+ Status = clear_free_space_cache(Vcb, &batchlist, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("clear_free_space_cache returned %08x\n", Status);
+ clear_batch_list(Vcb, &batchlist);
+ goto exit;
+ }
+ }
+
+ Status = commit_batch_list(Vcb, &batchlist, Irp);
if (!NT_SUCCESS(Status)) {
- ERR("find_chunk_usage returned %08x\n", Status);
+ ERR("commit_batch_list returned %08x\n", Status);
goto exit;
}
-
- Vcb->volume_fcb = create_fcb();
+
+ Vcb->volume_fcb = create_fcb(Vcb, NonPagedPool);
if (!Vcb->volume_fcb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
-
+
Vcb->volume_fcb->Vcb = Vcb;
Vcb->volume_fcb->sd = NULL;
-
- Vcb->root_fcb = create_fcb();
- if (!Vcb->root_fcb) {
+
+ Vcb->dummy_fcb = create_fcb(Vcb, NonPagedPool);
+ if (!Vcb->dummy_fcb) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ Vcb->dummy_fcb->Vcb = Vcb;
+ Vcb->dummy_fcb->type = BTRFS_TYPE_DIRECTORY;
+ Vcb->dummy_fcb->inode = 2;
+ Vcb->dummy_fcb->subvol = Vcb->root_root;
+ Vcb->dummy_fcb->atts = FILE_ATTRIBUTE_DIRECTORY;
+ Vcb->dummy_fcb->inode_item.st_nlink = 1;
+ Vcb->dummy_fcb->inode_item.st_mode = __S_IFDIR;
+
+ Vcb->dummy_fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+ if (!Vcb->dummy_fcb->hash_ptrs) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256);
+
+ Vcb->dummy_fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG);
+ if (!Vcb->dummy_fcb->hash_ptrs_uc) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
-
- Vcb->root_fcb->Vcb = Vcb;
- Vcb->root_fcb->inode = SUBVOL_ROOT_INODE;
- Vcb->root_fcb->type = BTRFS_TYPE_DIRECTORY;
-
- Vcb->root_fcb->full_filename.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(WCHAR), ALLOC_TAG);
-
- if (!Vcb->root_fcb->full_filename.Buffer) {
+
+ RtlZeroMemory(Vcb->dummy_fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256);
+
+ root_fcb = create_fcb(Vcb, NonPagedPool);
+ if (!root_fcb) {
ERR("out of memory\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
-
- Vcb->root_fcb->full_filename.Buffer[0] = '\\';
- Vcb->root_fcb->full_filename.Length = Vcb->root_fcb->full_filename.MaximumLength = sizeof(WCHAR);
-
+
+ root_fcb->Vcb = Vcb;
+ root_fcb->inode = SUBVOL_ROOT_INODE;
+ root_fcb->type = BTRFS_TYPE_DIRECTORY;
+
#ifdef DEBUG_FCB_REFCOUNTS
WARN("volume FCB = %p\n", Vcb->volume_fcb);
- WARN("root FCB = %p\n", Vcb->root_fcb);
+ WARN("root FCB = %p\n", root_fcb);
#endif
-
- Vcb->root_fcb->subvol = find_default_subvol(Vcb);
- if (!Vcb->root_fcb->subvol) {
+ root_fcb->subvol = find_default_subvol(Vcb, Irp);
+
+ if (!root_fcb->subvol) {
ERR("could not find top subvol\n");
Status = STATUS_INTERNAL_ERROR;
goto exit;
}
-
- Vcb->fcbs = Vcb->root_fcb;
-
- searchkey.obj_id = Vcb->root_fcb->inode;
- searchkey.obj_type = TYPE_INODE_ITEM;
- searchkey.offset = 0xffffffffffffffff;
-
- Status = find_item(Vcb, Vcb->root_fcb->subvol, &tp, &searchkey, FALSE);
+
+ Status = load_dir_children(Vcb, root_fcb, TRUE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("load_dir_children returned %08x\n", Status);
+ goto exit;
+ }
+
+ searchkey.obj_id = root_fcb->inode;
+ searchkey.obj_type = TYPE_INODE_ITEM;
+ searchkey.offset = 0xffffffffffffffff;
+
+ Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, FALSE, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("error - find_item returned %08x\n", Status);
+ goto exit;
+ }
+
+ if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
+ ERR("couldn't find INODE_ITEM for root directory\n");
+ Status = STATUS_INTERNAL_ERROR;
+ goto exit;
+ }
+
+ if (tp.item->size > 0)
+ RtlCopyMemory(&root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
+
+ fcb_get_sd(root_fcb, NULL, TRUE, Irp);
+
+ root_fcb->atts = get_file_attributes(Vcb, root_fcb->subvol, root_fcb->inode, root_fcb->type, FALSE, FALSE, Irp);
+
+ Vcb->root_fileref = create_fileref(Vcb);
+ if (!Vcb->root_fileref) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ Vcb->root_fileref->fcb = root_fcb;
+ InsertTailList(&root_fcb->subvol->fcbs, &root_fcb->list_entry);
+ InsertTailList(&Vcb->all_fcbs, &root_fcb->list_entry_all);
+
+ root_fcb->fileref = Vcb->root_fileref;
+
+ root_ccb = ExAllocatePoolWithTag(PagedPool, sizeof(ccb), ALLOC_TAG);
+ if (!root_ccb) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ Vcb->root_file = IoCreateStreamFileObject(NULL, DeviceToMount);
+ Vcb->root_file->FsContext = root_fcb;
+ Vcb->root_file->SectionObjectPointer = &root_fcb->nonpaged->segment_object;
+ Vcb->root_file->Vpb = DeviceObject->Vpb;
+
+ RtlZeroMemory(root_ccb, sizeof(ccb));
+ root_ccb->NodeType = BTRFS_NODE_TYPE_CCB;
+ root_ccb->NodeSize = sizeof(ccb);
+
+ Vcb->root_file->FsContext2 = root_ccb;
+
+ _SEH2_TRY {
+ CcInitializeCacheMap(Vcb->root_file, (PCC_FILE_SIZES)(&root_fcb->Header.AllocationSize), FALSE, cache_callbacks, Vcb->root_file);
+ } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+ Status = _SEH2_GetExceptionCode();
+ goto exit;
+ } _SEH2_END;
+
+ le = Vcb->devices.Flink;
+ while (le != &Vcb->devices) {
+ device* dev2 = CONTAINING_RECORD(le, device, list_entry);
+
+ Status = find_disk_holes(Vcb, dev2, Irp);
+ if (!NT_SUCCESS(Status)) {
+ ERR("find_disk_holes returned %08x\n", Status);
+ goto exit;
+ }
+
+ le = le->Flink;
+ }
+
+ NewDeviceObject->Vpb = IrpSp->Parameters.MountVolume.Vpb;
+ IrpSp->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
+ IrpSp->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
+ NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
+ NewDeviceObject->Vpb->VolumeLabel[0] = '?';
+ NewDeviceObject->Vpb->VolumeLabel[1] = 0;
+ NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
+
+ KeInitializeEvent(&Vcb->flush_thread_finished, NotificationEvent, FALSE);
+
+ Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, NewDeviceObject);
+ if (!NT_SUCCESS(Status)) {
+ ERR("PsCreateSystemThread returned %08x\n", Status);
+ goto exit;
+ }
+
+ Status = create_calc_threads(NewDeviceObject);
+ if (!NT_SUCCESS(Status)) {
+ ERR("create_calc_threads returned %08x\n", Status);
+ goto exit;
+ }
+
+ Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
+ if (!NT_SUCCESS(Status))
+ WARN("registry_mark_volume_mounted returned %08x\n", Status);
+
+ Status = look_for_balance_item(Vcb);
+ if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
+ WARN("look_for_balance_item returned %08x\n", Status);
+
+ Status = STATUS_SUCCESS;
+
+ if (vde)
+ vde->mounted_device = NewDeviceObject;
+
+ ExInitializeResourceLite(&Vcb->send_load_lock);
+
+exit:
+ if (pdode)
+ ExReleaseResourceLite(&pdode->child_lock);
+
+exit2:
+ if (Vcb) {
+ ExReleaseResourceLite(&Vcb->tree_lock);
+ ExReleaseResourceLite(&Vcb->load_lock);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ if (Vcb) {
+ if (init_lookaside) {
+ ExDeletePagedLookasideList(&Vcb->tree_data_lookaside);
+ ExDeletePagedLookasideList(&Vcb->traverse_ptr_lookaside);
+ ExDeletePagedLookasideList(&Vcb->batch_item_lookaside);
+ ExDeletePagedLookasideList(&Vcb->fileref_lookaside);
+ ExDeletePagedLookasideList(&Vcb->fcb_lookaside);
+ ExDeletePagedLookasideList(&Vcb->name_bit_lookaside);
+ ExDeleteNPagedLookasideList(&Vcb->range_lock_lookaside);
+ ExDeleteNPagedLookasideList(&Vcb->fileref_np_lookaside);
+ ExDeleteNPagedLookasideList(&Vcb->fcb_np_lookaside);
+ }
+
+ if (Vcb->root_file)
+ ObDereferenceObject(Vcb->root_file);
+ else if (Vcb->root_fileref) {
+ acquire_fcb_lock_exclusive(Vcb);
+ free_fileref(Vcb, Vcb->root_fileref);
+ release_fcb_lock(Vcb);
+ } else if (root_fcb) {
+ acquire_fcb_lock_exclusive(Vcb);
+ free_fcb(Vcb, root_fcb);
+ release_fcb_lock(Vcb);
+ }
+
+ if (Vcb->volume_fcb) {
+ acquire_fcb_lock_exclusive(Vcb);
+ free_fcb(Vcb, Vcb->volume_fcb);
+ release_fcb_lock(Vcb);
+ }
+
+ ExDeleteResourceLite(&Vcb->tree_lock);
+ ExDeleteResourceLite(&Vcb->load_lock);
+ ExDeleteResourceLite(&Vcb->fcb_lock);
+ ExDeleteResourceLite(&Vcb->chunk_lock);
+ ExDeleteResourceLite(&Vcb->dirty_fcbs_lock);
+ ExDeleteResourceLite(&Vcb->dirty_filerefs_lock);
+ ExDeleteResourceLite(&Vcb->dirty_subvols_lock);
+ ExDeleteResourceLite(&Vcb->scrub.stats_lock);
+
+ if (Vcb->devices.Flink) {
+ while (!IsListEmpty(&Vcb->devices)) {
+ device* dev2 = CONTAINING_RECORD(RemoveHeadList(&Vcb->devices), device, list_entry);
+
+ ExFreePool(dev2);
+ }
+ }
+ }
+
+ if (NewDeviceObject)
+ IoDeleteDevice(NewDeviceObject);
+ } else {
+ ExAcquireResourceExclusiveLite(&global_loading_lock, TRUE);
+ InsertTailList(&VcbList, &Vcb->list_entry);
+ ExReleaseResourceLite(&global_loading_lock);
+
+ FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_MOUNT);
+ }
+
+ TRACE("mount_vol done (status: %lx)\n", Status);
+
+ return Status;
+}
+
+static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
+ NTSTATUS Status;
+ superblock* sb;
+ UINT32 crc32;
+ ULONG to_read, cc;
+
+ if (!dev->devobj)
+ return STATUS_WRONG_VOLUME;
+
+ if (dev->removable) {
+ IO_STATUS_BLOCK iosb;
+
+ Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), TRUE, &iosb);
+
+ if (IoIsErrorUserInduced(Status)) {
+ ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status);
+
+ if (Vcb->vde) {
+ pdo_device_extension* pdode = Vcb->vde->pdode;
+ LIST_ENTRY* le2;
+ BOOL changed = FALSE;
+
+ ExAcquireResourceExclusiveLite(&pdode->child_lock, TRUE);
+
+ le2 = pdode->children.Flink;
+ while (le2 != &pdode->children) {
+ volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+
+ if (vc->devobj == dev->devobj) {
+ TRACE("removing device\n");
+
+ remove_volume_child(Vcb->vde, vc, TRUE);
+ changed = TRUE;
+
+ break;
+ }
+
+ le2 = le2->Flink;
+ }
+
+ if (!changed)
+ ExReleaseResourceLite(&pdode->child_lock);
+ }
+ } else if (!NT_SUCCESS(Status)) {
+ ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status);
+ return Status;
+ } else if (iosb.Information < sizeof(ULONG)) {
+ ERR("iosb.Information was too short\n");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ dev->change_count = cc;
+ }
+
+ to_read = dev->devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), dev->devobj->SectorSize);
+
+ sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+ if (!sb) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = sync_read_phys(dev->devobj, superblock_addrs[0], to_read, (PUCHAR)sb, TRUE);
if (!NT_SUCCESS(Status)) {
- ERR("error - find_item returned %08x\n", Status);
- goto exit;
+ ERR("Failed to read superblock: %08x\n", Status);
+ ExFreePool(sb);
+ return Status;
}
-
- if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
- ERR("couldn't find INODE_ITEM for root directory\n");
- Status = STATUS_INTERNAL_ERROR;
- free_traverse_ptr(&tp);
- goto exit;
+
+ if (sb->magic != BTRFS_MAGIC) {
+ ERR("not a BTRFS volume\n");
+ ExFreePool(sb);
+ return STATUS_WRONG_VOLUME;
}
-
- if (tp.item->size > 0)
- RtlCopyMemory(&Vcb->root_fcb->inode_item, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size));
-
- free_traverse_ptr(&tp);
-
- fcb_get_sd(Vcb->root_fcb);
-
- Vcb->root_fcb->atts = get_file_attributes(Vcb, &Vcb->root_fcb->inode_item, Vcb->root_fcb->subvol, Vcb->root_fcb->inode, Vcb->root_fcb->type,
- FALSE, FALSE);
-
- for (i = 0; i < Vcb->superblock.num_devices; i++) {
- Status = find_disk_holes(Vcb, &Vcb->devices[i]);
- if (!NT_SUCCESS(Status)) {
- ERR("find_disk_holes returned %08x\n", Status);
- goto exit;
- }
+
+ crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+ TRACE("crc32 was %08x, expected %08x\n", crc32, *((UINT32*)sb->checksum));
+
+ if (crc32 != *((UINT32*)sb->checksum)) {
+ ERR("checksum error\n");
+ ExFreePool(sb);
+ return STATUS_WRONG_VOLUME;
}
-
-// root_test(Vcb);
-
-// Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
-// Vcb->StorageDevice);
-//
-// InitializeListHead(&Vcb->FcbListHead);
-//
-// Fcb = NtfsCreateFCB(NULL, Vcb);
-// if (Fcb == NULL)
-// {
-// Status = STATUS_INSUFFICIENT_RESOURCES;
-// goto ByeBye;
-// }
-//
-// Ccb = ExAllocatePoolWithTag(NonPagedPool,
-// sizeof(NTFS_CCB),
-// TAG_CCB);
-// if (Ccb == NULL)
-// {
-// Status = STATUS_INSUFFICIENT_RESOURCES;
-// goto ByeBye;
-// }
-//
-// RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
-//
-// Ccb->Identifier.Type = NTFS_TYPE_CCB;
-// Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
-//
-// Vcb->StreamFileObject->FsContext = Fcb;
-// Vcb->StreamFileObject->FsContext2 = Ccb;
-// Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
-// Vcb->StreamFileObject->PrivateCacheMap = NULL;
-// Vcb->StreamFileObject->Vpb = Vcb->Vpb;
-// Ccb->PtrFileObject = Vcb->StreamFileObject;
-// Fcb->FileObject = Vcb->StreamFileObject;
-// Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
-//
-// Fcb->Flags = FCB_IS_VOLUME_STREAM;
-//
-// Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
-// Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
-// Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
-//
-// CcInitializeCacheMap(Vcb->StreamFileObject,
-// (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
-// FALSE,
-// &(NtfsGlobalData->CacheMgrCallbacks),
-// Fcb);
-//
-// ExInitializeResourceLite(&Vcb->LogToPhysLock);
-
- KeInitializeSpinLock(&Vcb->FcbListLock);
-//
-// /* Get serial number */
-// NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
-//
-// /* Get volume label */
-// NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
-// RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
-// Vcb->NtfsInfo.VolumeLabel,
-// Vcb->NtfsInfo.VolumeLabelLength);
-
- Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, Vcb);
- if (!NT_SUCCESS(Status)) {
- ERR("PsCreateSystemThread returned %08x\n", Status);
- goto exit;
+
+ if (RtlCompareMemory(&sb->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)) != sizeof(BTRFS_UUID)) {
+ ERR("different UUIDs\n");
+ ExFreePool(sb);
+ return STATUS_WRONG_VOLUME;
}
- NewDeviceObject->Vpb = Stack->Parameters.MountVolume.Vpb;
- Stack->Parameters.MountVolume.Vpb->DeviceObject = NewDeviceObject;
- Stack->Parameters.MountVolume.Vpb->RealDevice = DeviceToMount;
- Stack->Parameters.MountVolume.Vpb->Flags |= VPB_MOUNTED;
- NewDeviceObject->Vpb->VolumeLabelLength = 4; // FIXME
- NewDeviceObject->Vpb->VolumeLabel[0] = '?';
- NewDeviceObject->Vpb->VolumeLabel[1] = 0;
- NewDeviceObject->Vpb->ReferenceCount++; // FIXME - should we deref this at any point?
-
- Status = STATUS_SUCCESS;
+ ExFreePool(sb);
-exit:
-// if (!NT_SUCCESS(Status))
-// {
-// /* Cleanup */
-// if (Vcb && Vcb->StreamFileObject)
-// ObDereferenceObject(Vcb->StreamFileObject);
-//
-// if (Fcb)
-// ExFreePool(Fcb);
-//
-// if (Ccb)
-// ExFreePool(Ccb);
-//
-// if (NewDeviceObject)
-// IoDeleteDevice(NewDeviceObject);
-// }
+ dev->devobj->Flags &= ~DO_VERIFY_VOLUME;
- if (Vcb) {
- ExReleaseResourceLite(&Vcb->load_lock);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS verify_volume(_In_ PDEVICE_OBJECT devobj) {
+ device_extension* Vcb = devobj->DeviceExtension;
+ NTSTATUS Status;
+ LIST_ENTRY* le;
+ UINT64 failed_devices = 0;
+ BOOL locked = FALSE, remove = FALSE;
+
+ if (!(Vcb->Vpb->Flags & VPB_MOUNTED))
+ return STATUS_WRONG_VOLUME;
+
+ if (!ExIsResourceAcquiredExclusive(&Vcb->tree_lock)) {
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+ locked = TRUE;
}
- if (!NT_SUCCESS(Status)) {
- if (Vcb) {
- if (Vcb->root_fcb)
- free_fcb(Vcb->root_fcb);
+ if (Vcb->removing) {
+ if (locked) ExReleaseResourceLite(&Vcb->tree_lock);
+ return STATUS_WRONG_VOLUME;
+ }
- if (Vcb->volume_fcb)
- free_fcb(Vcb->volume_fcb);
+ InterlockedIncrement(&Vcb->open_files); // so pnp_surprise_removal doesn't uninit the device while we're still using it
- ExDeleteResourceLite(&Vcb->tree_lock);
- ExDeleteResourceLite(&Vcb->load_lock);
- ExDeleteResourceLite(&Vcb->fcb_lock);
- ExDeleteResourceLite(&Vcb->DirResource);
+ le = Vcb->devices.Flink;
+ while (le != &Vcb->devices) {
+ device* dev = CONTAINING_RECORD(le, device, list_entry);
- if (Vcb->devices)
- ExFreePoolWithTag(Vcb->devices, ALLOC_TAG);
+ Status = verify_device(Vcb, dev);
+ if (!NT_SUCCESS(Status)) {
+ failed_devices++;
- RemoveEntryList(&Vcb->list_entry);
+ if (dev->devobj && Vcb->options.allow_degraded)
+ dev->devobj = NULL;
}
- if (NewDeviceObject)
- IoDeleteDevice(NewDeviceObject);
+ le = le->Flink;
}
- TRACE("mount_vol done (status: %lx)\n", Status);
+ InterlockedDecrement(&Vcb->open_files);
+
+ if (Vcb->removing && Vcb->open_files == 0)
+ remove = TRUE;
+
+ if (locked)
+ ExReleaseResourceLite(&Vcb->tree_lock);
+
+ if (remove) {
+ uninit(Vcb, FALSE);
+ return Status;
+ }
+
+ if (failed_devices == 0 || (Vcb->options.allow_degraded && failed_devices < Vcb->superblock.num_devices)) {
+ Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+
+ return STATUS_SUCCESS;
+ }
return Status;
}
-static NTSTATUS STDCALL drv_file_system_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_FILE_SYSTEM_CONTROL)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
PIO_STACK_LOCATION IrpSp;
- NTSTATUS status;
+ NTSTATUS Status;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level;
- TRACE("file system control\n");
-
FsRtlEnterFileSystem();
+ TRACE("file system control\n");
+
top_level = is_top_level(Irp);
-
- status = STATUS_NOT_IMPLEMENTED;
+
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_file_system_control(DeviceObject, Irp);
+ goto end;
+ } else if (!Vcb || (Vcb->type != VCB_TYPE_FS && Vcb->type != VCB_TYPE_CONTROL)) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ Status = STATUS_NOT_IMPLEMENTED;
IrpSp = IoGetCurrentIrpStackLocation( Irp );
-
+
Irp->IoStatus.Information = 0;
-
+
switch (IrpSp->MinorFunction) {
case IRP_MN_MOUNT_VOLUME:
TRACE("IRP_MN_MOUNT_VOLUME\n");
-
-// Irp->IoStatus.Status = STATUS_SUCCESS;
- status = mount_vol(DeviceObject, Irp);
-// IrpSp->Parameters.MountVolume.DeviceObject = 0x0badc0de;
-// IrpSp->Parameters.MountVolume.Vpb = 0xdeadbeef;
-
-// IoCompleteRequest( Irp, IO_DISK_INCREMENT );
-
-// return Irp->IoStatus.Status;
+
+ Status = mount_vol(DeviceObject, Irp);
break;
-
+
case IRP_MN_KERNEL_CALL:
TRACE("IRP_MN_KERNEL_CALL\n");
+
+ Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
break;
-
- case IRP_MN_LOAD_FILE_SYSTEM:
- TRACE("IRP_MN_LOAD_FILE_SYSTEM\n");
- break;
-
+
case IRP_MN_USER_FS_REQUEST:
TRACE("IRP_MN_USER_FS_REQUEST\n");
-
- status = fsctl_request(DeviceObject, Irp, IrpSp->Parameters.FileSystemControl.FsControlCode, TRUE);
+
+ Status = fsctl_request(DeviceObject, &Irp, IrpSp->Parameters.FileSystemControl.FsControlCode);
break;
-
+
case IRP_MN_VERIFY_VOLUME:
TRACE("IRP_MN_VERIFY_VOLUME\n");
+
+ Status = verify_volume(DeviceObject);
+
+ if (!NT_SUCCESS(Status) && Vcb->Vpb->Flags & VPB_MOUNTED) {
+ ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+ Vcb->removing = TRUE;
+ ExReleaseResourceLite(&Vcb->tree_lock);
+ }
+
break;
-
+
default:
- WARN("unknown minor %u\n", IrpSp->MinorFunction);
break;
-
}
- Irp->IoStatus.Status = status;
+end:
+ TRACE("returning %08x\n", Status);
- IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
- if (top_level)
+ if (Irp) {
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
- return status;
+ return Status;
}
-static NTSTATUS STDCALL drv_lock_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_LOCK_CONTROL)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
fcb* fcb = IrpSp->FileObject->FsContext;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level;
FsRtlEnterFileSystem();
top_level = is_top_level(Irp);
-
+
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_lock_control(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ goto exit;
+ }
+
TRACE("lock control\n");
-
+
Status = FsRtlProcessFileLock(&fcb->lock, Irp, NULL);
-
- if (top_level)
+
+ fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
+
+exit:
+ TRACE("returning %08x\n", Status);
+
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
-
+
return Status;
}
-static NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_SHUTDOWN)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
NTSTATUS Status;
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- PFILE_OBJECT FileObject = IrpSp->FileObject;
- device_extension* Vcb = DeviceObject->DeviceExtension;
- fcb* fcb;
BOOL top_level;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
- FIXME("STUB: device control\n");
-
FsRtlEnterFileSystem();
+ TRACE("shutdown\n");
+
top_level = is_top_level(Irp);
-
- Irp->IoStatus.Information = 0;
-
- WARN("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
-
- if (!FileObject) {
- ERR("FileObject was NULL\n");
- Status = STATUS_INVALID_PARAMETER;
+
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_shutdown(DeviceObject, Irp);
goto end;
-
}
-
- fcb = FileObject->FsContext;
-
- if (!fcb) {
- ERR("FCB was NULL\n");
- Status = STATUS_INVALID_PARAMETER;
- goto end;
+
+ Status = STATUS_SUCCESS;
+
+ shutting_down = TRUE;
+ KeSetEvent(&mountmgr_thread_event, 0, FALSE);
+
+ while (!IsListEmpty(&VcbList)) {
+ Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry);
+
+ TRACE("shutting down Vcb %p\n", Vcb);
+
+ uninit(Vcb, TRUE);
}
-
- if (fcb == Vcb->volume_fcb) {
- FIXME("FIXME - pass through\n");
- Status = STATUS_NOT_IMPLEMENTED;
- } else {
- TRACE("filename = %.*S\n", fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
-
- switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
- case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
- TRACE("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n");
- Status = STATUS_INVALID_PARAMETER;
- break;
- default:
- WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
- IrpSp->Parameters.DeviceIoControl.IoControlCode, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xff0000) >> 16,
- (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0xc000) >> 14, (IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3ffc) >> 2,
- IrpSp->Parameters.DeviceIoControl.IoControlCode & 0x3);
- Status = STATUS_INVALID_PARAMETER;
- break;
- }
+#ifdef _DEBUG
+ if (comfo) {
+ ObDereferenceObject(comfo);
+ comdo = NULL;
+ comfo = NULL;
}
-
+#endif
+
end:
Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
-
- if (top_level)
+
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
return Status;
}
-static NTSTATUS STDCALL drv_shutdown(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
+_Dispatch_type_(IRP_MJ_POWER)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
NTSTATUS Status;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level;
- ERR("shutdown\n");
-
FsRtlEnterFileSystem();
top_level = is_top_level(Irp);
-
- Status = STATUS_SUCCESS;
- while (!IsListEmpty(&VcbList)) {
- LIST_ENTRY* le = RemoveHeadList(&VcbList);
- device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
-
- TRACE("shutting down Vcb %p\n", Vcb);
-
- uninit(Vcb);
+ Irp->IoStatus.Information = 0;
+
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ Status = vol_power(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ goto exit;
+ } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
+ IoSkipCurrentIrpStackLocation(Irp);
+
+ Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
+
+ goto exit;
}
-
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Status = Status;
- Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
- IoCompleteRequest( Irp, IO_NO_INCREMENT );
-
- if (top_level)
+exit:
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
return Status;
}
-static NTSTATUS STDCALL drv_pnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- device_extension* Vcb = DeviceObject->DeviceExtension;
+_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
+_Function_class_(DRIVER_DISPATCH)
+static NTSTATUS drv_system_control(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
NTSTATUS Status;
+ device_extension* Vcb = DeviceObject->DeviceExtension;
BOOL top_level;
- FIXME("STUB: pnp\n");
-
FsRtlEnterFileSystem();
top_level = is_top_level(Irp);
-
- Status = STATUS_NOT_IMPLEMENTED;
-
- switch (IrpSp->MinorFunction) {
- case IRP_MN_CANCEL_REMOVE_DEVICE:
- TRACE(" IRP_MN_CANCEL_REMOVE_DEVICE\n");
- break;
- case IRP_MN_QUERY_REMOVE_DEVICE:
- TRACE(" IRP_MN_QUERY_REMOVE_DEVICE\n");
- break;
+ Irp->IoStatus.Information = 0;
- case IRP_MN_REMOVE_DEVICE:
- TRACE(" IRP_MN_REMOVE_DEVICE\n");
- break;
+ if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
+ volume_device_extension* vde = DeviceObject->DeviceExtension;
- case IRP_MN_START_DEVICE:
- TRACE(" IRP_MN_START_DEVICE\n");
- break;
+ IoSkipCurrentIrpStackLocation(Irp);
- case IRP_MN_SURPRISE_REMOVAL:
- TRACE(" IRP_MN_SURPRISE_REMOVAL\n");
- break;
-
- case IRP_MN_QUERY_DEVICE_RELATIONS:
- TRACE(" IRP_MN_QUERY_DEVICE_RELATIONS\n");
- break;
-
- default:
- WARN("Unrecognized minor function 0x%x\n", IrpSp->MinorFunction);
- break;
- }
+ Status = IoCallDriver(vde->pdo, Irp);
+
+ goto exit;
+ } else if (Vcb && Vcb->type == VCB_TYPE_FS) {
+ IoSkipCurrentIrpStackLocation(Irp);
+
+ Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
-// Irp->IoStatus.Status = Status;
-// Irp->IoStatus.Information = 0;
+ goto exit;
+ }
- IoSkipCurrentIrpStackLocation(Irp);
-
- Status = IoCallDriver(Vcb->devices[0].devobj, Irp);
+ Status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
-// IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
- if (top_level)
+exit:
+ if (top_level)
IoSetTopLevelIrp(NULL);
-
+
FsRtlExitFileSystem();
- return Status;
+ return Status;
+}
+
+BOOL is_file_name_valid(_In_ PUNICODE_STRING us, _In_ BOOL posix) {
+ ULONG i;
+
+ if (us->Length < sizeof(WCHAR))
+ return FALSE;
+
+ if (us->Length > 255 * sizeof(WCHAR))
+ return FALSE;
+
+ for (i = 0; i < us->Length / sizeof(WCHAR); i++) {
+ if (us->Buffer[i] == '/' || us->Buffer[i] == 0 ||
+ (!posix && (us->Buffer[i] == '<' || us->Buffer[i] == '>' || us->Buffer[i] == ':' || us->Buffer[i] == '"' ||
+ us->Buffer[i] == '|' || us->Buffer[i] == '?' || us->Buffer[i] == '*' || (us->Buffer[i] >= 1 && us->Buffer[i] <= 31))))
+ return FALSE;
+ }
+
+ if (us->Buffer[0] == '.' && (us->Length == sizeof(WCHAR) || (us->Length == 2 * sizeof(WCHAR) && us->Buffer[1] == '.')))
+ return FALSE;
+
+ return TRUE;
+}
+
+void chunk_lock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length) {
+ LIST_ENTRY* le;
+ BOOL locked;
+ range_lock* rl;
+
+ rl = ExAllocateFromNPagedLookasideList(&Vcb->range_lock_lookaside);
+ if (!rl) {
+ ERR("out of memory\n");
+ return;
+ }
+
+ rl->start = start;
+ rl->length = length;
+ rl->thread = PsGetCurrentThread();
+
+ while (TRUE) {
+ locked = FALSE;
+
+ ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
+
+ le = c->range_locks.Flink;
+ while (le != &c->range_locks) {
+ range_lock* rl2 = CONTAINING_RECORD(le, range_lock, list_entry);
+
+ if (rl2->start < start + length && rl2->start + rl2->length > start && rl2->thread != PsGetCurrentThread()) {
+ locked = TRUE;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!locked) {
+ InsertTailList(&c->range_locks, &rl->list_entry);
+
+ ExReleaseResourceLite(&c->range_locks_lock);
+ return;
+ }
+
+ KeClearEvent(&c->range_locks_event);
+
+ ExReleaseResourceLite(&c->range_locks_lock);
+
+ KeWaitForSingleObject(&c->range_locks_event, UserRequest, KernelMode, FALSE, NULL);
+ }
+}
+
+void chunk_unlock_range(_In_ device_extension* Vcb, _In_ chunk* c, _In_ UINT64 start, _In_ UINT64 length) {
+ LIST_ENTRY* le;
+
+ ExAcquireResourceExclusiveLite(&c->range_locks_lock, TRUE);
+
+ le = c->range_locks.Flink;
+ while (le != &c->range_locks) {
+ range_lock* rl = CONTAINING_RECORD(le, range_lock, list_entry);
+
+ if (rl->start == start && rl->length == length) {
+ RemoveEntryList(&rl->list_entry);
+ ExFreeToNPagedLookasideList(&Vcb->range_lock_lookaside, rl);
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ KeSetEvent(&c->range_locks_event, 0, FALSE);
+
+ ExReleaseResourceLite(&c->range_locks_lock);
+}
+
+void log_device_error(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ int error) {
+ dev->stats[error]++;
+ dev->stats_changed = TRUE;
+ Vcb->stats_changed = TRUE;
+}
+
+#ifdef _DEBUG
+_Function_class_(KSTART_ROUTINE)
+static void serial_thread(void* context) {
+ LARGE_INTEGER due_time;
+ KTIMER timer;
+
+ UNUSED(context);
+
+ KeInitializeTimer(&timer);
+
+ due_time.QuadPart = (UINT64)-10000000;
+
+ KeSetTimer(&timer, due_time, NULL);
+
+ while (TRUE) {
+ KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
+
+ init_serial(FALSE);
+
+ if (comdo)
+ break;
+
+ KeSetTimer(&timer, due_time, NULL);
+ }
+
+ KeCancelTimer(&timer);
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+
+ serial_thread_handle = NULL;
}
-#ifdef _DEBUG
-static void STDCALL init_serial() {
+static void init_serial(BOOL first_time) {
NTSTATUS Status;
-
+
Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
if (!NT_SUCCESS(Status)) {
ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+
+ if (first_time) {
+ NTSTATUS Status;
+
+ Status = PsCreateSystemThread(&serial_thread_handle, 0, NULL, NULL, NULL, serial_thread, NULL);
+ if (!NT_SUCCESS(Status)) {
+ ERR("PsCreateSystemThread returned %08x\n", Status);
+ return;
+ }
+ }
}
}
#endif
#ifndef __REACTOS__
-static void STDCALL check_cpu() {
+static void check_cpu() {
unsigned int cpuInfo[4];
#ifndef _MSC_VER
__get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
have_sse42 = cpuInfo[2] & bit_SSE4_2;
+ have_sse2 = cpuInfo[3] & bit_SSE2;
#else
__cpuid(cpuInfo, 1);
have_sse42 = cpuInfo[2] & (1 << 20);
+ have_sse2 = cpuInfo[3] & (1 << 26);
#endif
if (have_sse42)
TRACE("SSE4.2 is supported\n");
else
TRACE("SSE4.2 not supported\n");
-}
-#endif
-
-static void STDCALL read_registry(PUNICODE_STRING regpath) {
- HANDLE h;
- UNICODE_STRING us;
- OBJECT_ATTRIBUTES oa;
- ULONG dispos;
- NTSTATUS Status;
- WCHAR* path;
- ULONG kvfilen, retlen, i;
- KEY_VALUE_FULL_INFORMATION* kvfi;
-
- const WCHAR mappings[] = L"\\Mappings";
-#ifndef __REACTOS__
- static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
-#endif
-
- path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) * sizeof(WCHAR)), ALLOC_TAG);
- if (!path) {
- ERR("out of memory\n");
- return;
- }
-
- RtlCopyMemory(path, regpath->Buffer, regpath->Length);
- RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) * sizeof(WCHAR));
-
- us.Buffer = path;
- us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) * sizeof(WCHAR));
-
- InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
-
- // FIXME - keep open and do notify for changes
- Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
-
- if (!NT_SUCCESS(Status)) {
- ERR("ZwCreateKey returned %08x\n", Status);
- ExFreePool(path);
- return;
- }
-
- if (dispos == REG_OPENED_EXISTING_KEY) {
- kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;
- kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-
- if (!kvfi) {
- ERR("out of memory\n");
- ExFreePool(path);
- ZwClose(h);
- return;
- }
-
- i = 0;
- do {
- Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen);
-
- if (NT_SUCCESS(Status) && kvfi->DataLength > 0) {
- UINT32 val = 0;
-
- RtlCopyMemory(&val, (UINT8*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(UINT32)));
-
- TRACE("entry %u = %.*S = %u\n", i, kvfi->NameLength / sizeof(WCHAR), kvfi->Name, val);
-
- add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val);
- }
-
- i = i + 1;
- } while (Status != STATUS_NO_MORE_ENTRIES);
- }
-
- ZwClose(h);
-
- ExFreePool(path);
-
-#ifdef _DEBUG
- InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
-
- Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
-
- if (!NT_SUCCESS(Status)) {
- ERR("ZwCreateKey returned %08x\n", Status);
- return;
- }
-
- RtlInitUnicodeString(&us, L"DebugLogLevel");
-
- kvfi = NULL;
- kvfilen = 0;
- Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-
- if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
- kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-
- if (!kvfi) {
- ERR("out of memory\n");
- ZwClose(h);
- return;
- }
-
- Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-
- if (NT_SUCCESS(Status)) {
- if (kvfi->Type == REG_DWORD && kvfi->DataLength >= sizeof(UINT32)) {
- RtlCopyMemory(&debug_log_level, ((UINT8*)kvfi) + kvfi->DataOffset, sizeof(UINT32));
- } else {
- Status = ZwDeleteValueKey(h, &us);
- if (!NT_SUCCESS(Status)) {
- ERR("ZwDeleteValueKey returned %08x\n", Status);
- }
-
- Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
- if (!NT_SUCCESS(Status)) {
- ERR("ZwSetValueKey reutrned %08x\n", Status);
- }
- }
- }
-
- ExFreePool(kvfi);
- } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- Status = ZwSetValueKey(h, &us, 0, REG_DWORD, &debug_log_level, sizeof(debug_log_level));
-
- if (!NT_SUCCESS(Status)) {
- ERR("ZwSetValueKey reutrned %08x\n", Status);
- }
- } else {
- ERR("ZwQueryValueKey returned %08x\n", Status);
- }
-
- RtlInitUnicodeString(&us, L"LogDevice");
-
- kvfi = NULL;
- kvfilen = 0;
- Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-
- if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
- kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-
- if (!kvfi) {
- ERR("out of memory\n");
- ZwClose(h);
- return;
- }
-
- Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-
- if (NT_SUCCESS(Status)) {
- if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
- log_device.Length = log_device.MaximumLength = kvfi->DataLength;
- log_device.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG);
-
- if (!log_device.Buffer) {
- ERR("out of memory\n");
- ExFreePool(kvfi);
- ZwClose(h);
- return;
- }
-
- RtlCopyMemory(log_device.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength);
-
- if (log_device.Buffer[(log_device.Length / sizeof(WCHAR)) - 1] == 0)
- log_device.Length -= sizeof(WCHAR);
- } else {
- ERR("LogDevice was type %u, length %u\n", kvfi->Type, kvfi->DataLength);
-
- Status = ZwDeleteValueKey(h, &us);
- if (!NT_SUCCESS(Status)) {
- ERR("ZwDeleteValueKey returned %08x\n", Status);
- }
- }
- }
-
- ExFreePool(kvfi);
- } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
- ERR("ZwQueryValueKey returned %08x\n", Status);
- }
-
- RtlInitUnicodeString(&us, L"LogFile");
-
- kvfi = NULL;
- kvfilen = 0;
- Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-
- if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
- kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
-
- if (!kvfi) {
- ERR("out of memory\n");
- ZwClose(h);
- return;
- }
-
- Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
-
- if (NT_SUCCESS(Status)) {
- if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
- log_file.Length = log_file.MaximumLength = kvfi->DataLength;
- log_file.Buffer = ExAllocatePoolWithTag(PagedPool, kvfi->DataLength, ALLOC_TAG);
-
- if (!log_file.Buffer) {
- ERR("out of memory\n");
- ExFreePool(kvfi);
- ZwClose(h);
- return;
- }
- RtlCopyMemory(log_file.Buffer, ((UINT8*)kvfi) + kvfi->DataOffset, kvfi->DataLength);
-
- if (log_file.Buffer[(log_file.Length / sizeof(WCHAR)) - 1] == 0)
- log_file.Length -= sizeof(WCHAR);
- } else {
- ERR("LogFile was type %u, length %u\n", kvfi->Type, kvfi->DataLength);
-
- Status = ZwDeleteValueKey(h, &us);
- if (!NT_SUCCESS(Status)) {
- ERR("ZwDeleteValueKey returned %08x\n", Status);
- }
- }
- }
-
- ExFreePool(kvfi);
- } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
- Status = ZwSetValueKey(h, &us, 0, REG_SZ, def_log_file, (wcslen(def_log_file) + 1) * sizeof(WCHAR));
-
- if (!NT_SUCCESS(Status)) {
- ERR("ZwSetValueKey returned %08x\n", Status);
- }
- } else {
- ERR("ZwQueryValueKey returned %08x\n", Status);
- }
-
- if (log_file.Length == 0) {
- log_file.Length = log_file.MaximumLength = wcslen(def_log_file) * sizeof(WCHAR);
- log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG);
-
- if (!log_file.Buffer) {
- ERR("out of memory\n");
- ZwClose(h);
- return;
- }
-
- RtlCopyMemory(log_file.Buffer, def_log_file, log_file.Length);
- }
-
- ZwClose(h);
-#endif
+ if (have_sse2)
+ TRACE("SSE2 is supported\n");
+ else
+ TRACE("SSE2 is not supported\n");
}
+#endif
#ifdef _DEBUG
static void init_logging() {
+ ExAcquireResourceExclusiveLite(&log_lock, TRUE);
+
if (log_device.Length > 0)
- init_serial();
+ init_serial(TRUE);
else if (log_file.Length > 0) {
NTSTATUS Status;
OBJECT_ATTRIBUTES oa;
char* dateline;
LARGE_INTEGER time;
TIME_FIELDS tf;
-
+
InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
-
+
Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
-
+
if (!NT_SUCCESS(Status)) {
ERR("ZwCreateFile returned %08x\n", Status);
- return;
+ goto end;
}
-
+
if (iosb.Information == FILE_OPENED) { // already exists
FILE_STANDARD_INFORMATION fsi;
FILE_POSITION_INFORMATION fpi;
-
+
static char delim[] = "\n---\n";
-
+
// move to end of file
-
+
Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
-
+
if (!NT_SUCCESS(Status)) {
ERR("ZwQueryInformationFile returned %08x\n", Status);
- return;
+ goto end;
}
-
+
fpi.CurrentByteOffset = fsi.EndOfFile;
-
+
Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
-
+
if (!NT_SUCCESS(Status)) {
ERR("ZwSetInformationFile returned %08x\n", Status);
- return;
+ goto end;
}
- Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim, strlen(delim), NULL, NULL);
-
+ Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim, (ULONG)strlen(delim), NULL, NULL);
+
if (!NT_SUCCESS(Status)) {
ERR("ZwWriteFile returned %08x\n", Status);
- return;
+ goto end;
}
}
-
+
dateline = ExAllocatePoolWithTag(PagedPool, 256, ALLOC_TAG);
-
+
if (!dateline) {
ERR("out of memory\n");
- return;
+ goto end;
}
-
+
KeQuerySystemTime(&time);
-
+
RtlTimeToTimeFields(&time, &tf);
-
- sprintf(dateline, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
- Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, strlen(dateline), NULL, NULL);
-
+ sprintf(dateline, "Starting logging at %04i-%02i-%02i %02i:%02i:%02i\n", tf.Year, tf.Month, tf.Day, tf.Hour, tf.Minute, tf.Second);
+
+ Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, dateline, (ULONG)strlen(dateline), NULL, NULL);
+
+ ExFreePool(dateline);
+
if (!NT_SUCCESS(Status)) {
ERR("ZwWriteFile returned %08x\n", Status);
- return;
+ goto end;
}
-
- ExFreePool(dateline);
}
+
+end:
+ ExReleaseResourceLite(&log_lock);
}
#endif
-NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
+_Function_class_(KSTART_ROUTINE)
+#ifdef __REACTOS__
+static void NTAPI degraded_wait_thread(_In_ void* context) {
+#else
+static void degraded_wait_thread(_In_ void* context) {
+#endif
+ KTIMER timer;
+ LARGE_INTEGER delay;
+
+ UNUSED(context);
+
+ KeInitializeTimer(&timer);
+
+ delay.QuadPart = -30000000; // wait three seconds
+ KeSetTimer(&timer, delay, NULL);
+ KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
+
+ TRACE("timer expired\n");
+
+ degraded_wait = FALSE;
+
+ ZwClose(degraded_wait_handle);
+ degraded_wait_handle = NULL;
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
+#else
+NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) {
+#endif
+ LIST_ENTRY* le;
+ NTSTATUS Status;
+ UNICODE_STRING volname;
+ ULONG i, j;
+ pdo_device_extension* pdode = NULL;
+ PDEVICE_OBJECT voldev;
+ volume_device_extension* vde;
+
+ TRACE("(%p, %p)\n", DriverObject, PhysicalDeviceObject);
+
+ ExAcquireResourceSharedLite(&pdo_list_lock, TRUE);
+
+ le = pdo_list.Flink;
+ while (le != &pdo_list) {
+ pdo_device_extension* pdode2 = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+
+ if (pdode2->pdo == PhysicalDeviceObject) {
+ pdode = pdode2;
+ break;
+ }
+
+ le = le->Flink;
+ }
+
+ if (!pdode) {
+ WARN("unrecognized PDO %p\n", PhysicalDeviceObject);
+ Status = STATUS_NOT_SUPPORTED;
+ goto end;
+ }
+
+ ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
+
+ volname.Length = volname.MaximumLength = (USHORT)((wcslen(BTRFS_VOLUME_PREFIX) + 36 + 1) * sizeof(WCHAR));
+ volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG); // FIXME - when do we free this?
+
+ if (!volname.Buffer) {
+ ERR("out of memory\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto end2;
+ }
+
+ RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR));
+
+ j = (ULONG)wcslen(BTRFS_VOLUME_PREFIX);
+ for (i = 0; i < 16; i++) {
+ volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] >> 4); j++;
+ volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] & 0xf); j++;
+
+ if (i == 3 || i == 5 || i == 7 || i == 9) {
+ volname.Buffer[j] = '-';
+ j++;
+ }
+ }
+
+ volname.Buffer[j] = '}';
+
+ Status = IoCreateDevice(drvobj, sizeof(volume_device_extension), &volname, FILE_DEVICE_DISK,
+ RtlIsNtDdiVersionAvailable(NTDDI_WIN8) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL : 0, FALSE, &voldev);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoCreateDevice returned %08x\n", Status);
+ goto end2;
+ }
+
+ voldev->SectorSize = PhysicalDeviceObject->SectorSize;
+ voldev->Flags |= DO_DIRECT_IO;
+
+ vde = voldev->DeviceExtension;
+ vde->type = VCB_TYPE_VOLUME;
+ vde->name = volname;
+ vde->device = voldev;
+ vde->mounted_device = NULL;
+ vde->pdo = PhysicalDeviceObject;
+ vde->pdode = pdode;
+ vde->removing = FALSE;
+ vde->open_count = 0;
+
+ Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
+ if (!NT_SUCCESS(Status))
+ WARN("IoRegisterDeviceInterface returned %08x\n", Status);
+
+ vde->attached_device = IoAttachDeviceToDeviceStack(voldev, PhysicalDeviceObject);
+
+ pdode->vde = vde;
+
+ if (pdode->removable)
+ voldev->Characteristics |= FILE_REMOVABLE_MEDIA;
+
+ voldev->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ Status = IoSetDeviceInterfaceState(&vde->bus_name, TRUE);
+ if (!NT_SUCCESS(Status))
+ WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+
+ Status = STATUS_SUCCESS;
+
+end2:
+ ExReleaseResourceLite(&pdode->child_lock);
+
+end:
+ ExReleaseResourceLite(&pdo_list_lock);
+
+ return Status;
+}
+
+_Function_class_(DRIVER_INITIALIZE)
+#ifdef __REACTOS__
+NTSTATUS NTAPI DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
+#else
+NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
+#endif
NTSTATUS Status;
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING device_nameW;
UNICODE_STRING dosdevice_nameW;
-
+ control_device_extension* cde;
+ HANDLE regh;
+ OBJECT_ATTRIBUTES oa;
+ ULONG dispos;
+
InitializeListHead(&uid_map_list);
-
+ InitializeListHead(&gid_map_list);
+
+#ifdef _DEBUG
+ ExInitializeResourceLite(&log_lock);
+#endif
+ ExInitializeResourceLite(&mapping_lock);
+
log_device.Buffer = NULL;
log_device.Length = log_device.MaximumLength = 0;
log_file.Buffer = NULL;
log_file.Length = log_file.MaximumLength = 0;
-
- read_registry(RegistryPath);
-
+
+ registry_path.Length = registry_path.MaximumLength = RegistryPath->Length;
+ registry_path.Buffer = ExAllocatePoolWithTag(PagedPool, registry_path.Length, ALLOC_TAG);
+
+ if (!registry_path.Buffer) {
+ ERR("out of memory\n");
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(registry_path.Buffer, RegistryPath->Buffer, registry_path.Length);
+
+ read_registry(®istry_path, FALSE);
+
#ifdef _DEBUG
if (debug_log_level > 0)
init_logging();
-
+
log_started = TRUE;
#endif
TRACE("DriverEntry\n");
-
+
#ifndef __REACTOS__
check_cpu();
#endif
-
-// TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
-
+
+ if (RtlIsNtDdiVersionAvailable(NTDDI_WIN8)) {
+ UNICODE_STRING name;
+ tPsIsDiskCountersEnabled fPsIsDiskCountersEnabled;
+
+ RtlInitUnicodeString(&name, L"PsIsDiskCountersEnabled");
+ fPsIsDiskCountersEnabled = (tPsIsDiskCountersEnabled)MmGetSystemRoutineAddress(&name);
+
+ if (fPsIsDiskCountersEnabled) {
+ diskacc = fPsIsDiskCountersEnabled();
+
+ RtlInitUnicodeString(&name, L"PsUpdateDiskCounters");
+ fPsUpdateDiskCounters = (tPsUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
+
+ if (!fPsUpdateDiskCounters)
+ diskacc = FALSE;
+
+ RtlInitUnicodeString(&name, L"FsRtlUpdateDiskCounters");
+ fFsRtlUpdateDiskCounters = (tFsRtlUpdateDiskCounters)MmGetSystemRoutineAddress(&name);
+ }
+
+ RtlInitUnicodeString(&name, L"CcCopyReadEx");
+ fCcCopyReadEx = (tCcCopyReadEx)MmGetSystemRoutineAddress(&name);
+
+ RtlInitUnicodeString(&name, L"CcCopyWriteEx");
+ fCcCopyWriteEx = (tCcCopyWriteEx)MmGetSystemRoutineAddress(&name);
+
+ RtlInitUnicodeString(&name, L"CcSetAdditionalCacheAttributesEx");
+ fCcSetAdditionalCacheAttributesEx = (tCcSetAdditionalCacheAttributesEx)MmGetSystemRoutineAddress(&name);
+ } else {
+ fPsUpdateDiskCounters = NULL;
+ fCcCopyReadEx = NULL;
+ fCcCopyWriteEx = NULL;
+ fCcSetAdditionalCacheAttributesEx = NULL;
+ fFsRtlUpdateDiskCounters = NULL;
+ }
+
drvobj = DriverObject;
DriverObject->DriverUnload = DriverUnload;
+ DriverObject->DriverExtension->AddDevice = AddDevice;
+
DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)drv_create;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)drv_close;
DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)drv_read;
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)drv_flush_buffers;
DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_query_volume_information;
DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)drv_set_volume_information;
- DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)drv_cleanup;
DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)drv_directory_control;
DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)drv_file_system_control;
- DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)drv_lock_control;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)drv_device_control;
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)drv_shutdown;
- DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)drv_pnp;
+ DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)drv_lock_control;
+ DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)drv_cleanup;
DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = (PDRIVER_DISPATCH)drv_query_security;
DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = (PDRIVER_DISPATCH)drv_set_security;
+ DriverObject->MajorFunction[IRP_MJ_POWER] = (PDRIVER_DISPATCH)drv_power;
+ DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)drv_system_control;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)drv_pnp;
init_fast_io_dispatch(&DriverObject->FastIoDispatch);
dosdevice_nameW.Buffer = dosdevice_name;
dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = (USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
- Status = IoCreateDevice(DriverObject, 0, &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, &DeviceObject);
+ Status = IoCreateDevice(DriverObject, sizeof(control_device_extension), &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM,
+ FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
if (!NT_SUCCESS(Status)) {
ERR("IoCreateDevice returned %08x\n", Status);
return Status;
}
-
- devobj = DeviceObject;
-
+
+ master_devobj = DeviceObject;
+ cde = (control_device_extension*)master_devobj->DeviceExtension;
+
+ RtlZeroMemory(cde, sizeof(control_device_extension));
+
+ cde->type = VCB_TYPE_CONTROL;
+
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
ERR("IoCreateSymbolicLink returned %08x\n", Status);
return Status;
}
-
+
Status = init_cache();
if (!NT_SUCCESS(Status)) {
ERR("init_cache returned %08x\n", Status);
return Status;
}
- InitializeListHead(&volumes);
- look_for_vols(&volumes);
-
InitializeListHead(&VcbList);
ExInitializeResourceLite(&global_loading_lock);
-
+ ExInitializeResourceLite(&pdo_list_lock);
+
+ InitializeListHead(&pdo_list);
+
+ InitializeObjectAttributes(&oa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+ Status = ZwCreateKey(®h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
+ /* ReactOS specific hack: allow BtrFS driver to start in 1st stage with no hive */
+#ifndef __REACTOS__
+ if (!NT_SUCCESS(Status)) {
+ ERR("ZwCreateKey returned %08x\n", Status);
+ return Status;
+ }
+
+ watch_registry(regh);
+#else
+ if (NT_SUCCESS(Status)) {
+ watch_registry(regh);
+ }
+#endif
+
+ Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF,
+ NULL, NULL, 0, &cde->buspdo);
+ if (!NT_SUCCESS(Status)) {
+ ERR("IoReportDetectedDevice returned %08x\n", Status);
+ return Status;
+ }
+
+ Status = IoRegisterDeviceInterface(cde->buspdo, &BtrfsBusInterface, NULL, &cde->bus_name);
+ if (!NT_SUCCESS(Status))
+ WARN("IoRegisterDeviceInterface returned %08x\n", Status);
+
+ cde->attached_device = IoAttachDeviceToDeviceStack(DeviceObject, cde->buspdo);
+
+ Status = IoSetDeviceInterfaceState(&cde->bus_name, TRUE);
+ if (!NT_SUCCESS(Status))
+ WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+
+ DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ IoInvalidateDeviceRelations(cde->buspdo, BusRelations);
+
+ Status = PsCreateSystemThread(°raded_wait_handle, 0, NULL, NULL, NULL, degraded_wait_thread, NULL);
+ if (!NT_SUCCESS(Status))
+ WARN("PsCreateSystemThread returned %08x\n", Status);
+
+ Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+ (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, ¬ification_entry2);
+ if (!NT_SUCCESS(Status))
+ ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+
+ Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+ (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, ¬ification_entry3);
+ if (!NT_SUCCESS(Status))
+ ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+
+ Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+ (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, pnp_notification, DriverObject, ¬ification_entry);
+ if (!NT_SUCCESS(Status))
+ ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+
+ finished_probing = TRUE;
+
+ KeInitializeEvent(&mountmgr_thread_event, NotificationEvent, FALSE);
+
+#ifndef __REACTOS__
+ Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, NULL, NULL, NULL, mountmgr_thread, NULL);
+ if (!NT_SUCCESS(Status))
+ WARN("PsCreateSystemThread returned %08x\n", Status);
+#endif
+
IoRegisterFileSystem(DeviceObject);
return STATUS_SUCCESS;