https://git.reactos.org/?p=reactos.git;a=commitdiff;h=29d1938258cc83423d422…
commit 29d1938258cc83423d42281d87d58ffe8cc63bc2
Author: Johannes Obermayr <johannesobermayr(a)gmx.de>
AuthorDate: Wed Sep 28 18:08:10 2022 +0200
Commit: GitHub <noreply(a)github.com>
CommitDate: Wed Sep 28 18:08:10 2022 +0200
[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
---
dll/shellext/shellbtrfs/propsheet.cpp | 15 ++++
dll/shellext/shellbtrfs/shellbtrfs.rc | 8 +-
dll/win32/ubtrfs/ubtrfs.rc | 8 +-
drivers/filesystems/btrfs/btrfs.c | 102 +++++++++++-----------
drivers/filesystems/btrfs/btrfs.inf | 2 +-
drivers/filesystems/btrfs/btrfs.rc | 8 +-
drivers/filesystems/btrfs/btrfs_drv.h | 16 +++-
drivers/filesystems/btrfs/create.c | 17 ++--
drivers/filesystems/btrfs/fileinfo.c | 35 ++++++--
drivers/filesystems/btrfs/flushthread.c | 148 ++++++++++++++++++++++++++++----
drivers/filesystems/btrfs/free-space.c | 4 +-
drivers/filesystems/btrfs/fsctl.c | 4 +
drivers/filesystems/btrfs/registry.c | 10 ++-
drivers/filesystems/btrfs/treefuncs.c | 53 ++++++++----
14 files changed, 317 insertions(+), 113 deletions(-)
diff --git a/dll/shellext/shellbtrfs/propsheet.cpp
b/dll/shellext/shellbtrfs/propsheet.cpp
index f31ce2eb85f..3730038567f 100644
--- a/dll/shellext/shellbtrfs/propsheet.cpp
+++ b/dll/shellext/shellbtrfs/propsheet.cpp
@@ -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) {
diff --git a/dll/shellext/shellbtrfs/shellbtrfs.rc
b/dll/shellext/shellbtrfs/shellbtrfs.rc
index b83d7288147..5140d8d3498 100644
--- a/dll/shellext/shellbtrfs/shellbtrfs.rc
+++ b/dll/shellext/shellbtrfs/shellbtrfs.rc
@@ -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"
diff --git a/dll/win32/ubtrfs/ubtrfs.rc b/dll/win32/ubtrfs/ubtrfs.rc
index e723033ba8e..014cd9e6dba 100644
--- a/dll/win32/ubtrfs/ubtrfs.rc
+++ b/dll/win32/ubtrfs/ubtrfs.rc
@@ -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"
diff --git a/drivers/filesystems/btrfs/btrfs.c b/drivers/filesystems/btrfs/btrfs.c
index 866f80a9205..f6d9ea0c196 100644
--- a/drivers/filesystems/btrfs/btrfs.c
+++ b/drivers/filesystems/btrfs/btrfs.c
@@ -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,59 +5974,32 @@ 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(1, eax, ebx, ecx, edx);
-
- if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
- have_sse42 = ecx & bit_SSE4_2;
- have_sse2 = edx & bit_SSE2;
- }
-
- if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx))
- have_avx2 = ebx & bit_AVX2;
+ __cpuid(cpu_info, 1);
+ have_sse42 = cpu_info[2] & (1 << 20);
+ have_sse2 = cpu_info[3] & (1 << 26);
- if (have_avx2) {
- // check Windows has enabled AVX2 - Windows 10 doesn't immediately
+ __cpuidex(cpu_info, 7, 0);
+ have_avx2 = cpu_info[1] & (1 << 5);
- if (__readcr4() & (1 << 18)) {
- uint32_t xcr0;
+ if (have_avx2) {
+ // check Windows has enabled AVX2 - Windows 10 doesn't immediately
- __asm__("xgetbv" : "=a" (xcr0) : "c" (0) :
"edx" );
+ if (__readcr4() & (1 << 18)) {
+ uint32_t xcr0;
- 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);
+ __asm__("xgetbv" : "=a" (xcr0) : "c" (0) :
"edx");
+#endif
- if ((xcr0 & 6) != 6)
- have_avx2 = false;
- } else
+ if ((xcr0 & 6) != 6)
have_avx2 = false;
- }
+ } else
+ have_avx2 = false;
}
-#endif
if (have_sse42) {
TRACE("SSE4.2 is supported\n");
diff --git a/drivers/filesystems/btrfs/btrfs.inf b/drivers/filesystems/btrfs/btrfs.inf
index 18dc5cd25e9..fd3512b4f2f 100644
--- a/drivers/filesystems/btrfs/btrfs.inf
+++ b/drivers/filesystems/btrfs/btrfs.inf
@@ -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]
diff --git a/drivers/filesystems/btrfs/btrfs.rc b/drivers/filesystems/btrfs/btrfs.rc
index 59f9d2ccb1f..085aedd131b 100644
--- a/drivers/filesystems/btrfs/btrfs.rc
+++ b/drivers/filesystems/btrfs/btrfs.rc
@@ -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"
diff --git a/drivers/filesystems/btrfs/btrfs_drv.h
b/drivers/filesystems/btrfs/btrfs_drv.h
index e06770adfac..46e62e04d05 100644
--- a/drivers/filesystems/btrfs/btrfs_drv.h
+++ b/drivers/filesystems/btrfs/btrfs_drv.h
@@ -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;
diff --git a/drivers/filesystems/btrfs/create.c b/drivers/filesystems/btrfs/create.c
index c5d27d1ce27..c87a85b5015 100644
--- a/drivers/filesystems/btrfs/create.c
+++ b/drivers/filesystems/btrfs/create.c
@@ -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;
}
diff --git a/drivers/filesystems/btrfs/fileinfo.c b/drivers/filesystems/btrfs/fileinfo.c
index 16febd612d6..5b51b8ac56b 100644
--- a/drivers/filesystems/btrfs/fileinfo.c
+++ b/drivers/filesystems/btrfs/fileinfo.c
@@ -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) {
diff --git a/drivers/filesystems/btrfs/flushthread.c
b/drivers/filesystems/btrfs/flushthread.c
index bdaf15bf69e..a39e174313f 100644
--- a/drivers/filesystems/btrfs/flushthread.c
+++ b/drivers/filesystems/btrfs/flushthread.c
@@ -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;
+ }
- InsertHeadList(&br->items, &bi->list_entry);
+ le2 = le2->Blink;
+ }
- return STATUS_SUCCESS;
+ 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;
+ }
+
+ return STATUS_INTERNAL_ERROR;
}
#ifdef _MSC_VER
#pragma warning(pop)
diff --git a/drivers/filesystems/btrfs/free-space.c
b/drivers/filesystems/btrfs/free-space.c
index 50b8141ee6f..5e6350af444 100644
--- a/drivers/filesystems/btrfs/free-space.c
+++ b/drivers/filesystems/btrfs/free-space.c
@@ -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
diff --git a/drivers/filesystems/btrfs/fsctl.c b/drivers/filesystems/btrfs/fsctl.c
index 356031e395f..c1f12ecd57e 100644
--- a/drivers/filesystems/btrfs/fsctl.c
+++ b/drivers/filesystems/btrfs/fsctl.c
@@ -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) {
diff --git a/drivers/filesystems/btrfs/registry.c b/drivers/filesystems/btrfs/registry.c
index 3f70cf83b14..63b51ea9ae4 100644
--- a/drivers/filesystems/btrfs/registry.c
+++ b/drivers/filesystems/btrfs/registry.c
@@ -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));
diff --git a/drivers/filesystems/btrfs/treefuncs.c
b/drivers/filesystems/btrfs/treefuncs.c
index 3b9483a7b08..157e453b29a 100644
--- a/drivers/filesystems/btrfs/treefuncs.c
+++ b/drivers/filesystems/btrfs/treefuncs.c
@@ -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)