mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
[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:
parent
22d8c0fd54
commit
29d1938258
14 changed files with 319 additions and 115 deletions
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue