[BTRFS][UBTRFS][SHELLBTRFS] Upgrade to 1.8.1 (#4729)

CORE-18322

v1.8.1 (2022-08-23):
- Fixed use-after-free when flushing
- Fixed crash when opening volume when AppLocker installed
- Compression now disabled for no-COW files, as on Linux
- Flushing now scales better on very fast drives
- Fixed small files getting padded to 4,096 bytes by lazy writer
- Added NoDataCOW registry option
This commit is contained in:
Johannes Obermayr 2022-09-28 18:08:10 +02:00 committed by GitHub
parent 22d8c0fd54
commit 29d1938258
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 319 additions and 115 deletions

View file

@ -597,6 +597,16 @@ void BtrfsPropSheet::change_inode_flag(HWND hDlg, uint64_t flag, UINT state) {
flags_set = ~flag;
}
if (flags & BTRFS_INODE_NODATACOW && flags_set & BTRFS_INODE_NODATACOW) {
EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS), false);
EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS_TYPE), false);
} else {
EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS), true);
EnableWindow(GetDlgItem(hDlg, IDC_COMPRESS_TYPE), flags & BTRFS_INODE_COMPRESS && flags_set & BTRFS_INODE_COMPRESS);
}
EnableWindow(GetDlgItem(hDlg, IDC_NODATACOW), !(flags & BTRFS_INODE_COMPRESS) || !(flags_set & BTRFS_INODE_COMPRESS));
flags_changed = true;
SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
@ -995,6 +1005,11 @@ void BtrfsPropSheet::init_propsheet(HWND hwndDlg) {
set_check_box(hwndDlg, IDC_NODATACOW, min_flags & BTRFS_INODE_NODATACOW, max_flags & BTRFS_INODE_NODATACOW);
set_check_box(hwndDlg, IDC_COMPRESS, min_flags & BTRFS_INODE_COMPRESS, max_flags & BTRFS_INODE_COMPRESS);
if (min_flags & BTRFS_INODE_NODATACOW || max_flags & BTRFS_INODE_NODATACOW)
EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), false);
else if (min_flags & BTRFS_INODE_COMPRESS || max_flags & BTRFS_INODE_COMPRESS)
EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), false);
comptype = GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE);
while (SendMessageW(comptype, CB_GETCOUNT, 0, 0) > 0) {

View file

@ -61,8 +61,8 @@ IDI_ICON1 ICON "subvol.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,8,0,0
PRODUCTVERSION 1,8,0,0
FILEVERSION 1,8,1,0
PRODUCTVERSION 1,8,1,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -78,12 +78,12 @@ BEGIN
BLOCK "080904b0"
BEGIN
VALUE "FileDescription", "WinBtrfs shell extension"
VALUE "FileVersion", "1.8.0"
VALUE "FileVersion", "1.8.1"
VALUE "InternalName", "btrfs"
VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-22"
VALUE "OriginalFilename", "shellbtrfs.dll"
VALUE "ProductName", "WinBtrfs"
VALUE "ProductVersion", "1.8.0"
VALUE "ProductVersion", "1.8.1"
END
END
BLOCK "VarFileInfo"

View file

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,8,0,0
PRODUCTVERSION 1,8,0,0
FILEVERSION 1,8,1,0
PRODUCTVERSION 1,8,1,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -68,12 +68,12 @@ BEGIN
BLOCK "080904b0"
BEGIN
VALUE "FileDescription", "Btrfs utility DLL"
VALUE "FileVersion", "1.8.0"
VALUE "FileVersion", "1.8.1"
VALUE "InternalName", "ubtrfs"
VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-22"
VALUE "OriginalFilename", "ubtrfs.dll"
VALUE "ProductName", "WinBtrfs"
VALUE "ProductVersion", "1.8.0"
VALUE "ProductVersion", "1.8.1"
END
END
BLOCK "VarFileInfo"

View file

@ -83,6 +83,7 @@ uint32_t mount_clear_cache = 0;
uint32_t mount_allow_degraded = 0;
uint32_t mount_readonly = 0;
uint32_t mount_no_root_dir = 0;
uint32_t mount_nodatacow = 0;
uint32_t no_pnp = 0;
bool log_started = false;
UNICODE_STRING log_device, log_file, registry_path;
@ -645,6 +646,36 @@ static void calculate_total_space(_In_ device_extension* Vcb, _Out_ uint64_t* to
}
#ifndef __REACTOS__
// simplified version of FsRtlAreNamesEqual, which can be a bottleneck!
static bool compare_strings(const UNICODE_STRING* us1, const UNICODE_STRING* us2) {
if (us1->Length != us2->Length)
return false;
WCHAR* s1 = us1->Buffer;
WCHAR* s2 = us2->Buffer;
for (unsigned int i = 0; i < us1->Length; i++) {
WCHAR c1 = *s1;
WCHAR c2 = *s2;
if (c1 != c2) {
if (c1 >= 'a' && c1 <= 'z')
c1 = c1 - 'a' + 'A';
if (c2 >= 'a' && c2 <= 'z')
c2 = c2 - 'a' + 'A';
if (c1 != c2)
return false;
}
s1++;
s2++;
}
return true;
}
#define INIT_UNICODE_STRING(var, val) UNICODE_STRING us##var; us##var.Buffer = (WCHAR*)val; us##var.Length = us##var.MaximumLength = sizeof(val) - sizeof(WCHAR);
// This function exists because we have to lie about our FS type in certain situations.
@ -707,7 +738,7 @@ static bool lie_about_fs_type() {
name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usmpr.Length) / sizeof(WCHAR)];
name.Length = name.MaximumLength = usmpr.Length;
blacklist = FsRtlAreNamesEqual(&name, &usmpr, true, NULL);
blacklist = compare_strings(&name, &usmpr);
}
if (!blacklist && entry->FullDllName.Length >= uscmd.Length) {
@ -716,7 +747,7 @@ static bool lie_about_fs_type() {
name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - uscmd.Length) / sizeof(WCHAR)];
name.Length = name.MaximumLength = uscmd.Length;
blacklist = FsRtlAreNamesEqual(&name, &uscmd, true, NULL);
blacklist = compare_strings(&name, &uscmd);
}
if (!blacklist && entry->FullDllName.Length >= usfsutil.Length) {
@ -725,7 +756,7 @@ static bool lie_about_fs_type() {
name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usfsutil.Length) / sizeof(WCHAR)];
name.Length = name.MaximumLength = usfsutil.Length;
blacklist = FsRtlAreNamesEqual(&name, &usfsutil, true, NULL);
blacklist = compare_strings(&name, &usfsutil);
}
if (!blacklist && entry->FullDllName.Length >= usstorsvc.Length) {
@ -734,7 +765,7 @@ static bool lie_about_fs_type() {
name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usstorsvc.Length) / sizeof(WCHAR)];
name.Length = name.MaximumLength = usstorsvc.Length;
blacklist = FsRtlAreNamesEqual(&name, &usstorsvc, true, NULL);
blacklist = compare_strings(&name, &usstorsvc);
}
if (!blacklist && entry->FullDllName.Length >= usifstest.Length) {
@ -743,7 +774,7 @@ static bool lie_about_fs_type() {
name.Buffer = &entry->FullDllName.Buffer[(entry->FullDllName.Length - usifstest.Length) / sizeof(WCHAR)];
name.Length = name.MaximumLength = usifstest.Length;
blacklist = FsRtlAreNamesEqual(&name, &usifstest, true, NULL);
blacklist = compare_strings(&name, &usifstest);
}
if (blacklist) {
@ -5943,60 +5974,33 @@ static void init_serial(bool first_time) {
#if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
static void check_cpu() {
bool have_sse2 = false, have_sse42 = false, have_avx2 = false;
int cpu_info[4];
#ifndef _MSC_VER
{
uint32_t eax, ebx, ecx, edx;
__cpuid(cpu_info, 1);
have_sse42 = cpu_info[2] & (1 << 20);
have_sse2 = cpu_info[3] & (1 << 26);
__cpuid(1, eax, ebx, ecx, edx);
__cpuidex(cpu_info, 7, 0);
have_avx2 = cpu_info[1] & (1 << 5);
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
have_sse42 = ecx & bit_SSE4_2;
have_sse2 = edx & bit_SSE2;
}
if (have_avx2) {
// check Windows has enabled AVX2 - Windows 10 doesn't immediately
if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx))
have_avx2 = ebx & bit_AVX2;
if (__readcr4() & (1 << 18)) {
uint32_t xcr0;
if (have_avx2) {
// check Windows has enabled AVX2 - Windows 10 doesn't immediately
if (__readcr4() & (1 << 18)) {
uint32_t xcr0;
__asm__("xgetbv" : "=a" (xcr0) : "c" (0) : "edx" );
if ((xcr0 & 6) != 6)
have_avx2 = false;
} else
have_avx2 = false;
}
}
#ifdef _MSC_VER
xcr0 = (uint32_t)_xgetbv(0);
#else
{
unsigned int cpu_info[4];
__cpuid(cpu_info, 1);
have_sse42 = cpu_info[2] & (1 << 20);
have_sse2 = cpu_info[3] & (1 << 26);
__cpuidex(cpu_info, 7, 0);
have_avx2 = cpu_info[1] & (1 << 5);
if (have_avx2) {
// check Windows has enabled AVX2 - Windows 10 doesn't immediately
if (__readcr4() & (1 << 18)) {
uint32_t xcr0 = (uint32_t)_xgetbv(0);
if ((xcr0 & 6) != 6)
have_avx2 = false;
} else
have_avx2 = false;
}
}
__asm__("xgetbv" : "=a" (xcr0) : "c" (0) : "edx");
#endif
if ((xcr0 & 6) != 6)
have_avx2 = false;
} else
have_avx2 = false;
}
if (have_sse42) {
TRACE("SSE4.2 is supported\n");
calc_crc32c = calc_crc32c_hw;

View file

@ -10,7 +10,7 @@ Signature = "$Windows NT$"
Class = Volume
ClassGuid = {71a27cdd-812a-11d0-bec7-08002be2092f}
Provider = %Me%
DriverVer = 03/12/2022,1.8.0
DriverVer = 08/23/2022,1.8.1
CatalogFile = btrfs.cat
[DestinationDirs]

View file

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,8,0,0
PRODUCTVERSION 1,8,0,0
FILEVERSION 1,8,1,0
PRODUCTVERSION 1,8,1,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -68,12 +68,12 @@ BEGIN
BLOCK "080904b0"
BEGIN
VALUE "FileDescription", "WinBtrfs"
VALUE "FileVersion", "1.8.0"
VALUE "FileVersion", "1.8.1"
VALUE "InternalName", "btrfs"
VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-22"
VALUE "OriginalFilename", "btrfs.sys"
VALUE "ProductName", "WinBtrfs"
VALUE "ProductVersion", "1.8.0"
VALUE "ProductVersion", "1.8.1"
END
END
BLOCK "VarFileInfo"

View file

@ -492,8 +492,15 @@ typedef struct {
} batch_item;
typedef struct {
root* r;
KEY key;
LIST_ENTRY items;
unsigned int num_items;
LIST_ENTRY list_entry;
} batch_item_ind;
typedef struct {
root* r;
LIST_ENTRY items_ind;
LIST_ENTRY list_entry;
} batch_root;
@ -674,6 +681,7 @@ typedef struct {
bool clear_cache;
bool allow_degraded;
bool no_root_dir;
bool nodatacow;
} mount_options;
#define VCB_TYPE_FS 1
@ -1182,6 +1190,7 @@ extern uint32_t mount_clear_cache;
extern uint32_t mount_allow_degraded;
extern uint32_t mount_readonly;
extern uint32_t mount_no_root_dir;
extern uint32_t mount_nodatacow;
extern uint32_t no_pnp;
#ifndef __GNUC__
@ -1468,8 +1477,6 @@ NTSTATUS do_tree_writes(device_extension* Vcb, LIST_ENTRY* tree_writes, bool no_
void add_checksum_entry(device_extension* Vcb, uint64_t address, ULONG length, void* csum, PIRP Irp);
bool find_metadata_address_in_chunk(device_extension* Vcb, chunk* c, uint64_t* address);
void add_trim_entry_avoid_sb(device_extension* Vcb, device* dev, uint64_t address, uint64_t size);
NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, uint64_t objid, uint8_t objtype, uint64_t offset,
_In_opt_ _When_(return >= 0, __drv_aliasesMem) void* data, uint16_t datalen, enum batch_operation operation);
NTSTATUS flush_partial_stripe(device_extension* Vcb, chunk* c, partial_stripe* ps);
NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp);
void calc_tree_checksum(device_extension* Vcb, tree_header* th);
@ -1697,6 +1704,9 @@ static __inline void print_open_trees(device_extension* Vcb) {
}
static __inline bool write_fcb_compressed(fcb* fcb) {
if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)
return false;
// make sure we don't accidentally write the cache inodes or pagefile compressed
if (fcb->subvol->id == BTRFS_ROOT_ROOT || fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE)
return false;

View file

@ -2319,19 +2319,24 @@ static NTSTATUS file_create2(_In_ PIRP Irp, _Requires_exclusive_lock_held_(_Curr
fcb->inode_item.flags = BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM | BTRFS_INODE_NOCOMPRESS;
} else {
// inherit nodatacow flag from parent directory
if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW) {
if (parfileref->fcb->inode_item.flags & BTRFS_INODE_NODATACOW || Vcb->options.nodatacow) {
fcb->inode_item.flags |= BTRFS_INODE_NODATACOW;
if (type != BTRFS_TYPE_DIRECTORY)
fcb->inode_item.flags |= BTRFS_INODE_NODATASUM;
}
if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS)
if (parfileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS &&
!(fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
fcb->inode_item.flags |= BTRFS_INODE_COMPRESS;
}
}
fcb->prop_compression = parfileref->fcb->prop_compression;
fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
if (!(fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) {
fcb->prop_compression = parfileref->fcb->prop_compression;
fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None;
} else
fcb->prop_compression = PropCompression_None;
fcb->inode_item_changed = true;
@ -2838,7 +2843,7 @@ static NTSTATUS create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
fcb->adsmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - (sizeof(DIR_ITEM) - 1);
if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) {
WARN("not enough room for new DIR_ITEM (%Iu + %lu > %lu)", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
WARN("not enough room for new DIR_ITEM (%Iu + %lu > %lu)\n", utf8len + sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
reap_fcb(fcb);
free_fileref(parfileref);
return STATUS_DISK_FULL;
@ -4551,7 +4556,7 @@ static NTSTATUS open_file(PDEVICE_OBJECT DeviceObject, _Requires_lock_held_(_Cur
uint64_t inode;
if (!related) {
WARN("cannot open by short file ID unless related fileref also provided");
WARN("cannot open by short file ID unless related fileref also provided"\n);
Status = STATUS_INVALID_PARAMETER;
goto exit;
}

View file

@ -3248,6 +3248,7 @@ static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFI
LIST_ENTRY rollback;
bool set_size = false;
ULONG filter;
uint64_t new_end_of_file;
if (!fileref) {
ERR("fileref is NULL\n");
@ -3306,35 +3307,43 @@ static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFI
TRACE("FileObject: AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x\n",
fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
TRACE("setting new end to %I64x bytes (currently %I64x)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size);
new_end_of_file = feofi->EndOfFile.QuadPart;
if ((uint64_t)feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) {
/* The lazy writer sometimes tries to round files to the next page size through CcSetValidData -
* ignore these. See fastfat!FatSetEndOfFileInfo, where Microsoft does the same as we're
* doing below. */
if (advance_only && new_end_of_file >= (uint64_t)fcb->Header.FileSize.QuadPart)
new_end_of_file = fcb->Header.FileSize.QuadPart;
TRACE("setting new end to %I64x bytes (currently %I64x)\n", new_end_of_file, fcb->inode_item.st_size);
if (new_end_of_file < fcb->inode_item.st_size) {
if (advance_only) {
Status = STATUS_SUCCESS;
goto end;
}
TRACE("truncating file to %I64x bytes\n", feofi->EndOfFile.QuadPart);
TRACE("truncating file to %I64x bytes\n", new_end_of_file);
if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) {
Status = STATUS_USER_MAPPED_FILE;
goto end;
}
Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback);
Status = truncate_file(fcb, new_end_of_file, Irp, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - truncate_file failed\n");
goto end;
}
} else if ((uint64_t)feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) {
TRACE("extending file to %I64x bytes\n", feofi->EndOfFile.QuadPart);
} else if (new_end_of_file > fcb->inode_item.st_size) {
TRACE("extending file to %I64x bytes\n", new_end_of_file);
Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, prealloc, NULL, &rollback);
Status = extend_file(fcb, fileref, new_end_of_file, prealloc, NULL, &rollback);
if (!NT_SUCCESS(Status)) {
ERR("error - extend_file failed\n");
goto end;
}
} else if ((uint64_t)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) {
} else if (new_end_of_file == fcb->inode_item.st_size && advance_only) {
Status = STATUS_SUCCESS;
goto end;
}
@ -5502,6 +5511,11 @@ NTSTATUS __stdcall drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
goto end;
}
if (fcb == fcb->Vcb->volume_fcb) {
Status = STATUS_INVALID_PARAMETER;
goto end;
}
ccb = FileObject->FsContext2;
if (!ccb) {
@ -5753,6 +5767,11 @@ NTSTATUS __stdcall drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
goto end;
}
if (fcb == fcb->Vcb->volume_fcb) {
Status = STATUS_INVALID_PARAMETER;
goto end;
}
ccb = FileObject->FsContext2;
if (!ccb) {

View file

@ -29,6 +29,8 @@
// #define DEBUG_WRITE_LOOPS
#define BATCH_ITEM_LIMIT 1000
typedef struct {
KEVENT Event;
IO_STATUS_BLOCK iosb;
@ -49,6 +51,10 @@ typedef struct {
static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp);
static NTSTATUS update_tree_extents(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback);
static NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, uint64_t objid,
uint8_t objtype, uint64_t offset, _In_opt_ _When_(return >= 0, __drv_aliasesMem) void* data,
uint16_t datalen, enum batch_operation operation);
_Function_class_(IO_COMPLETION_ROUTINE)
static NTSTATUS __stdcall write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
write_context* context = conptr;
@ -2923,7 +2929,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY*
#ifdef DEBUG_PARANOID
if (bgi->used & 0x8000000000000000) {
ERR("refusing to write BLOCK_GROUP_ITEM with negative usage value (%I64x)", bgi->used);
ERR("refusing to write BLOCK_GROUP_ITEM with negative usage value (%I64x)\n", bgi->used);
int3;
}
#endif
@ -4429,12 +4435,88 @@ static NTSTATUS insert_sparse_extent(fcb* fcb, LIST_ENTRY* batchlist, uint64_t s
return STATUS_SUCCESS;
}
static NTSTATUS split_batch_item_list(batch_item_ind* bii) {
LIST_ENTRY* le;
unsigned int i = 0;
LIST_ENTRY* midpoint = NULL;
batch_item_ind* bii2;
batch_item* midpoint_item;
LIST_ENTRY* before_midpoint;
le = bii->items.Flink;
while (le != &bii->items) {
if (i >= bii->num_items / 2) {
midpoint = le;
break;
}
i++;
le = le->Flink;
}
if (!midpoint)
return STATUS_SUCCESS;
// make sure items on either side of split don't have same key
while (midpoint->Blink != &bii->items) {
batch_item* item = CONTAINING_RECORD(midpoint, batch_item, list_entry);
batch_item* prev = CONTAINING_RECORD(midpoint->Blink, batch_item, list_entry);
if (item->key.obj_id != prev->key.obj_id)
break;
if (item->key.obj_type != prev->key.obj_type)
break;
if (item->key.offset != prev->key.offset)
break;
midpoint = midpoint->Blink;
i--;
}
if (midpoint->Blink == &bii->items)
return STATUS_SUCCESS;
bii2 = ExAllocatePoolWithTag(PagedPool, sizeof(batch_item_ind), ALLOC_TAG);
if (!bii2) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
midpoint_item = CONTAINING_RECORD(midpoint, batch_item, list_entry);
bii2->key.obj_id = midpoint_item->key.obj_id;
bii2->key.obj_type = midpoint_item->key.obj_type;
bii2->key.offset = midpoint_item->key.offset;
bii2->num_items = bii->num_items - i;
bii->num_items = i;
before_midpoint = midpoint->Blink;
bii2->items.Flink = midpoint;
midpoint->Blink = &bii2->items;
bii2->items.Blink = bii->items.Blink;
bii->items.Blink->Flink = &bii2->items;
bii->items.Blink = before_midpoint;
before_midpoint->Flink = &bii->items;
InsertHeadList(&bii->list_entry, &bii2->list_entry);
return STATUS_SUCCESS;
}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(suppress: 28194)
#endif
NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, uint64_t objid, uint8_t objtype, uint64_t offset,
_In_opt_ _When_(return >= 0, __drv_aliasesMem) void* data, uint16_t datalen, enum batch_operation operation) {
static NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, uint64_t objid,
uint8_t objtype, uint64_t offset, _In_opt_ _When_(return >= 0, __drv_aliasesMem) void* data,
uint16_t datalen, enum batch_operation operation) {
LIST_ENTRY* le;
batch_root* br = NULL;
batch_item* bi;
@ -4459,10 +4541,27 @@ NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, ro
}
br->r = r;
InitializeListHead(&br->items);
InitializeListHead(&br->items_ind);
InsertTailList(batchlist, &br->list_entry);
}
if (IsListEmpty(&br->items_ind)) {
batch_item_ind* bii;
bii = ExAllocatePoolWithTag(PagedPool, sizeof(batch_item_ind), ALLOC_TAG);
if (!bii) {
ERR("out of memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
bii->key.obj_id = 0;
bii->key.obj_type = 0;
bii->key.offset = 0;
InitializeListHead(&bii->items);
bii->num_items = 0;
InsertTailList(&br->items_ind, &bii->list_entry);
}
bi = ExAllocateFromPagedLookasideList(&Vcb->batch_item_lookaside);
if (!bi) {
ERR("out of memory\n");
@ -4476,22 +4575,41 @@ NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, ro
bi->datalen = datalen;
bi->operation = operation;
le = br->items.Blink;
while (le != &br->items) {
batch_item* bi2 = CONTAINING_RECORD(le, batch_item, list_entry);
int cmp = keycmp(bi2->key, bi->key);
le = br->items_ind.Blink;
while (le != &br->items_ind) {
LIST_ENTRY* le2;
batch_item_ind* bii = CONTAINING_RECORD(le, batch_item_ind, list_entry);
if (cmp == -1 || (cmp == 0 && bi->operation >= bi2->operation)) {
InsertHeadList(&bi2->list_entry, &bi->list_entry);
return STATUS_SUCCESS;
if (keycmp(bii->key, bi->key) == 1) {
le = le->Blink;
continue;
}
le = le->Blink;
le2 = bii->items.Blink;
while (le2 != &bii->items) {
batch_item* bi2 = CONTAINING_RECORD(le2, batch_item, list_entry);
int cmp = keycmp(bi2->key, bi->key);
if (cmp == -1 || (cmp == 0 && bi->operation >= bi2->operation)) {
InsertHeadList(&bi2->list_entry, &bi->list_entry);
bii->num_items++;
goto end;
}
le2 = le2->Blink;
}
InsertHeadList(&bii->items, &bi->list_entry);
bii->num_items++;
end:
if (bii->num_items > BATCH_ITEM_LIMIT)
return split_batch_item_list(bii);
return STATUS_SUCCESS;
}
InsertHeadList(&br->items, &bi->list_entry);
return STATUS_SUCCESS;
return STATUS_INTERNAL_ERROR;
}
#ifdef _MSC_VER
#pragma warning(pop)

View file

@ -1892,16 +1892,16 @@ static NTSTATUS update_chunk_cache_tree(device_extension* Vcb, chunk* c, PIRP Ir
fsi_count++;
ExFreePool(s);
RemoveHeadList(&space_list);
ExFreePool(s);
continue;
} else if (s->address == tp.item->key.obj_id && s->size == tp.item->key.offset) {
// unchanged entry
fsi_count++;
ExFreePool(s);
RemoveHeadList(&space_list);
ExFreePool(s);
} else {
// remove entry

View file

@ -1363,6 +1363,10 @@ static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length
return STATUS_ACCESS_DENIED;
}
// nocow and compression are mutually exclusive
if (bsii->flags_changed && bsii->flags & BTRFS_INODE_NODATACOW && bsii->flags & BTRFS_INODE_COMPRESS)
return STATUS_INVALID_PARAMETER;
ExAcquireResourceExclusiveLite(fcb->Header.Resource, true);
if (bsii->flags_changed) {

View file

@ -38,7 +38,7 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
mount_options* options = &Vcb->options;
UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus,
maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus, clearcacheus, allowdegradedus, zstdlevelus,
norootdirus;
norootdirus, nodatacowus;
OBJECT_ATTRIBUTES oa;
NTSTATUS Status;
ULONG i, j, kvfilen, index, retlen;
@ -59,6 +59,8 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
options->clear_cache = mount_clear_cache;
options->allow_degraded = mount_allow_degraded;
options->subvol_id = 0;
options->no_root_dir = mount_no_root_dir;
options->nodatacow = mount_nodatacow;
path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
@ -123,6 +125,7 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
RtlInitUnicodeString(&allowdegradedus, L"AllowDegraded");
RtlInitUnicodeString(&zstdlevelus, L"ZstdLevel");
RtlInitUnicodeString(&norootdirus, L"NoRootDir");
RtlInitUnicodeString(&nodatacowus, L"NoDataCOW");
do {
Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen);
@ -199,6 +202,10 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
options->no_root_dir = *val;
} else if (FsRtlAreNamesEqual(&nodatacowus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
options->nodatacow = *val;
}
} else if (Status != STATUS_NO_MORE_ENTRIES) {
ERR("ZwEnumerateValueKey returned %08lx\n", Status);
@ -813,6 +820,7 @@ void read_registry(PUNICODE_STRING regpath, bool refresh) {
get_registry_value(h, L"Readonly", REG_DWORD, &mount_readonly, sizeof(mount_readonly));
get_registry_value(h, L"ZstdLevel", REG_DWORD, &mount_zstd_level, sizeof(mount_zstd_level));
get_registry_value(h, L"NoRootDir", REG_DWORD, &mount_no_root_dir, sizeof(mount_no_root_dir));
get_registry_value(h, L"NoDataCOW", REG_DWORD, &mount_nodatacow, sizeof(mount_nodatacow));
if (!refresh)
get_registry_value(h, L"NoPNP", REG_DWORD, &no_pnp, sizeof(no_pnp));

View file

@ -1254,11 +1254,16 @@ void clear_batch_list(device_extension* Vcb, LIST_ENTRY* batchlist) {
LIST_ENTRY* le = RemoveHeadList(batchlist);
batch_root* br = CONTAINING_RECORD(le, batch_root, list_entry);
while (!IsListEmpty(&br->items)) {
LIST_ENTRY* le2 = RemoveHeadList(&br->items);
batch_item* bi = CONTAINING_RECORD(le2, batch_item, list_entry);
while (!IsListEmpty(&br->items_ind)) {
batch_item_ind* bii = CONTAINING_RECORD(RemoveHeadList(&br->items_ind), batch_item_ind, list_entry);
ExFreeToPagedLookasideList(&Vcb->batch_item_lookaside, bi);
while (!IsListEmpty(&bii->items)) {
batch_item* bi = CONTAINING_RECORD(RemoveHeadList(&bii->items), batch_item, list_entry);
ExFreeToPagedLookasideList(&Vcb->batch_item_lookaside, bi);
}
ExFreePool(bii);
}
ExFreePool(br);
@ -1901,15 +1906,31 @@ static NTSTATUS handle_batch_collision(device_extension* Vcb, batch_item* bi, tr
__attribute__((nonnull(1,2)))
static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tree_lock) device_extension* Vcb, batch_root* br, PIRP Irp) {
LIST_ENTRY items;
LIST_ENTRY* le;
NTSTATUS Status;
TRACE("root: %I64x\n", br->r->id);
le = br->items.Flink;
while (le != &br->items) {
InitializeListHead(&items);
// move sub-lists into one big list
while (!IsListEmpty(&br->items_ind)) {
batch_item_ind* bii = CONTAINING_RECORD(RemoveHeadList(&br->items_ind), batch_item_ind, list_entry);
items.Blink->Flink = bii->items.Flink;
bii->items.Flink->Blink = items.Blink;
items.Blink = bii->items.Blink;
bii->items.Blink->Flink = &items;
ExFreePool(bii);
}
le = items.Flink;
while (le != &items) {
batch_item* bi = CONTAINING_RECORD(le, batch_item, list_entry);
LIST_ENTRY *le2;
LIST_ENTRY* le2;
traverse_ptr tp;
KEY tree_end;
bool no_end;
@ -2174,7 +2195,7 @@ static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tr
if (td)
InsertHeadList(tp.item->list_entry.Blink, &td->list_entry);
} else {
Status = handle_batch_collision(Vcb, bi, tp.tree, tp.item, td, &br->items, &ignore);
Status = handle_batch_collision(Vcb, bi, tp.tree, tp.item, td, &items, &ignore);
if (!NT_SUCCESS(Status)) {
ERR("handle_batch_collision returned %08lx\n", Status);
#ifdef _DEBUG
@ -2192,7 +2213,7 @@ static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tr
}
if (bi->operation == Batch_DeleteInodeRef && cmp != 0 && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
add_delete_inode_extref(Vcb, bi, &br->items);
add_delete_inode_extref(Vcb, bi, &items);
}
if (!ignore && td) {
@ -2214,7 +2235,7 @@ static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tr
}
le2 = le->Flink;
while (le2 != &br->items) {
while (le2 != &items) {
batch_item* bi2 = CONTAINING_RECORD(le2, batch_item, list_entry);
if (bi2->operation == Batch_DeleteInode || bi2->operation == Batch_DeleteExtentData || bi2->operation == Batch_DeleteFreeSpace)
@ -2255,10 +2276,10 @@ static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tr
InsertHeadList(le3->Blink, &td->list_entry);
inserted = true;
} else if (bi2->operation == Batch_DeleteInodeRef && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
add_delete_inode_extref(Vcb, bi2, &br->items);
add_delete_inode_extref(Vcb, bi2, &items);
}
} else {
Status = handle_batch_collision(Vcb, bi2, tp.tree, td2, td, &br->items, &ignore);
Status = handle_batch_collision(Vcb, bi2, tp.tree, td2, td, &items, &ignore);
if (!NT_SUCCESS(Status)) {
ERR("handle_batch_collision returned %08lx\n", Status);
#ifdef _DEBUG
@ -2275,7 +2296,7 @@ static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tr
InsertHeadList(le3->Blink, &td->list_entry);
inserted = true;
} else if (bi2->operation == Batch_DeleteInodeRef && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
add_delete_inode_extref(Vcb, bi2, &br->items);
add_delete_inode_extref(Vcb, bi2, &items);
}
break;
}
@ -2294,7 +2315,7 @@ static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tr
listhead = td;
}
} else if (!inserted && bi2->operation == Batch_DeleteInodeRef && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF) {
add_delete_inode_extref(Vcb, bi2, &br->items);
add_delete_inode_extref(Vcb, bi2, &items);
}
while (listhead->list_entry.Blink != &tp.tree->itemlist) {
@ -2330,8 +2351,8 @@ static NTSTATUS commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_->tr
}
// FIXME - remove as we are going along
while (!IsListEmpty(&br->items)) {
batch_item* bi = CONTAINING_RECORD(RemoveHeadList(&br->items), batch_item, list_entry);
while (!IsListEmpty(&items)) {
batch_item* bi = CONTAINING_RECORD(RemoveHeadList(&items), batch_item, list_entry);
if ((bi->operation == Batch_DeleteDirItem || bi->operation == Batch_DeleteInodeRef ||
bi->operation == Batch_DeleteInodeExtRef || bi->operation == Batch_DeleteXattr) && bi->data)