https://git.reactos.org/?p=reactos.git;a=commitdiff;h=eb7fbc253fcb0d2bc5344…
commit eb7fbc253fcb0d2bc534412b4965bf2f736eac05
Author:     Pierre Schweitzer <pierre(a)reactos.org>
AuthorDate: Sun Dec 16 12:03:16 2018 +0100
Commit:     Pierre Schweitzer <pierre(a)reactos.org>
CommitDate: Sun Dec 16 12:06:46 2018 +0100
    [BTRFS] Upgrade to 1.1
    CORE-15452
---
 drivers/filesystems/btrfs/CMakeLists.txt           |   16 +
 drivers/filesystems/btrfs/balance.c                |   50 +-
 drivers/filesystems/btrfs/btrfs.c                  |  188 +-
 drivers/filesystems/btrfs/btrfs.h                  |    3 +-
 drivers/filesystems/btrfs/btrfs.rc                 |    8 +-
 drivers/filesystems/btrfs/btrfs_drv.h              |   47 +-
 drivers/filesystems/btrfs/btrfsioctl.h             |   20 +
 drivers/filesystems/btrfs/compress.c               |  268 +-
 drivers/filesystems/btrfs/create.c                 |  420 +-
 drivers/filesystems/btrfs/dirctrl.c                |   63 +-
 drivers/filesystems/btrfs/fastio.c                 |   21 +-
 drivers/filesystems/btrfs/fileinfo.c               |  214 +-
 drivers/filesystems/btrfs/flushthread.c            |  175 +-
 drivers/filesystems/btrfs/free-space.c             |   26 +-
 drivers/filesystems/btrfs/fsctl.c                  |  112 +-
 drivers/filesystems/btrfs/pnp.c                    |   18 +-
 drivers/filesystems/btrfs/read.c                   |   49 +-
 drivers/filesystems/btrfs/registry.c               |   51 +-
 drivers/filesystems/btrfs/reparse.c                |  190 +-
 drivers/filesystems/btrfs/scrub.c                  |    4 +-
 drivers/filesystems/btrfs/search.c                 |   10 +-
 drivers/filesystems/btrfs/send.c                   |   50 +-
 drivers/filesystems/btrfs/treefuncs.c              |   12 +-
 drivers/filesystems/btrfs/volume.c                 |   12 +-
 drivers/filesystems/btrfs/write.c                  |   67 +-
 drivers/filesystems/btrfs/zstd/bitstream.h         |  455 +++
 drivers/filesystems/btrfs/zstd/compiler.h          |  133 +
 drivers/filesystems/btrfs/zstd/cpu.h               |  215 ++
 drivers/filesystems/btrfs/zstd/debug.h             |  123 +
 drivers/filesystems/btrfs/zstd/entropy_common.c    |  236 ++
 drivers/filesystems/btrfs/zstd/error_private.c     |   48 +
 drivers/filesystems/btrfs/zstd/error_private.h     |   76 +
 drivers/filesystems/btrfs/zstd/fse.h               |  708 ++++
 drivers/filesystems/btrfs/zstd/fse_compress.c      |  724 ++++
 drivers/filesystems/btrfs/zstd/fse_decompress.c    |  313 ++
 drivers/filesystems/btrfs/zstd/hist.c              |  195 +
 drivers/filesystems/btrfs/zstd/hist.h              |   92 +
 drivers/filesystems/btrfs/zstd/huf.h               |  334 ++
 drivers/filesystems/btrfs/zstd/huf_compress.c      |  796 ++++
 drivers/filesystems/btrfs/zstd/huf_decompress.c    | 1096 ++++++
 drivers/filesystems/btrfs/zstd/mem.h               |  376 ++
 drivers/filesystems/btrfs/zstd/xxhash.c            |  887 +++++
 drivers/filesystems/btrfs/zstd/xxhash.h            |  305 ++
 drivers/filesystems/btrfs/zstd/zstd.h              | 1526 ++++++++
 drivers/filesystems/btrfs/zstd/zstd_common.c       |   72 +
 drivers/filesystems/btrfs/zstd/zstd_compress.c     | 4040 ++++++++++++++++++++
 .../btrfs/zstd/zstd_compress_internal.h            |  798 ++++
 drivers/filesystems/btrfs/zstd/zstd_decompress.c   | 3108 +++++++++++++++
 drivers/filesystems/btrfs/zstd/zstd_double_fast.c  |  499 +++
 drivers/filesystems/btrfs/zstd/zstd_double_fast.h  |   38 +
 drivers/filesystems/btrfs/zstd/zstd_errors.h       |   92 +
 drivers/filesystems/btrfs/zstd/zstd_fast.c         |  391 ++
 drivers/filesystems/btrfs/zstd/zstd_fast.h         |   37 +
 drivers/filesystems/btrfs/zstd/zstd_internal.h     |  257 ++
 drivers/filesystems/btrfs/zstd/zstd_lazy.c         | 1099 ++++++
 drivers/filesystems/btrfs/zstd/zstd_lazy.h         |   67 +
 drivers/filesystems/btrfs/zstd/zstd_ldm.c          |  646 ++++
 drivers/filesystems/btrfs/zstd/zstd_ldm.h          |  109 +
 drivers/filesystems/btrfs/zstd/zstd_opt.c          | 1132 ++++++
 drivers/filesystems/btrfs/zstd/zstd_opt.h          |   48 +
 media/doc/README.FSD                               |    2 +-
 61 files changed, 22588 insertions(+), 579 deletions(-)
diff --git a/drivers/filesystems/btrfs/CMakeLists.txt
b/drivers/filesystems/btrfs/CMakeLists.txt
index 65e61d255a..59fe9565e6 100644
--- a/drivers/filesystems/btrfs/CMakeLists.txt
+++ b/drivers/filesystems/btrfs/CMakeLists.txt
@@ -33,6 +33,22 @@ list(APPEND SOURCE
     volume.c
     worker-thread.c
     write.c
+    zstd/entropy_common.c
+    zstd/fse_compress.c
+    zstd/hist.c
+    zstd/huf_decompress.c
+    zstd/zstd_common.c
+    zstd/zstd_decompress.c
+    zstd/zstd_fast.c
+    zstd/zstd_ldm.c
+    zstd/error_private.c
+    zstd/fse_decompress.c
+    zstd/huf_compress.c
+    zstd/xxhash.c
+    zstd/zstd_compress.c
+    zstd/zstd_double_fast.c
+    zstd/zstd_lazy.c
+    zstd/zstd_opt.c
     btrfs_drv.h)
 add_library(btrfs SHARED ${SOURCE} btrfs.rc)
diff --git a/drivers/filesystems/btrfs/balance.c b/drivers/filesystems/btrfs/balance.c
index 3e36489c31..fe641f4f41 100644
--- a/drivers/filesystems/btrfs/balance.c
+++ b/drivers/filesystems/btrfs/balance.c
@@ -107,13 +107,13 @@ static NTSTATUS
add_metadata_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_l
         c = get_chunk_from_address(Vcb, tp->item->key.obj_id);
     if (c) {
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, Vcb);
         c->used -= Vcb->superblock.node_size;
         space_list_add(c, tp->item->key.obj_id, Vcb->superblock.node_size,
rollback);
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, Vcb);
     }
     ei = (EXTENT_ITEM*)tp->item->data;
@@ -747,7 +747,7 @@ static NTSTATUS
write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                     flags = Vcb->metadata_flags;
                 if (newchunk) {
-                    ExAcquireResourceExclusiveLite(&newchunk->lock, TRUE);
+                    acquire_chunk_lock(newchunk, Vcb);
                     if (newchunk->chunk_item->type == flags &&
find_metadata_address_in_chunk(Vcb, newchunk, &mr->new_address)) {
                         newchunk->used += Vcb->superblock.node_size;
@@ -755,7 +755,7 @@ static NTSTATUS
write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                         done = TRUE;
                     }
-                    ExReleaseResourceLite(&newchunk->lock);
+                    release_chunk_lock(newchunk, Vcb);
                 }
                 if (!done) {
@@ -766,20 +766,20 @@ static NTSTATUS
write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                         chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
                         if (!c2->readonly && !c2->reloc && c2 !=
newchunk && c2->chunk_item->type == flags) {
-                            ExAcquireResourceExclusiveLite(&c2->lock, TRUE);
+                            acquire_chunk_lock(c2, Vcb);
                             if ((c2->chunk_item->size - c2->used) >=
Vcb->superblock.node_size) {
                                 if (find_metadata_address_in_chunk(Vcb, c2,
&mr->new_address)) {
                                     c2->used += Vcb->superblock.node_size;
                                     space_list_subtract(c2, FALSE, mr->new_address,
Vcb->superblock.node_size, rollback);
-                                    ExReleaseResourceLite(&c2->lock);
+                                    release_chunk_lock(c2, Vcb);
                                     newchunk = c2;
                                     done = TRUE;
                                     break;
                                 }
                             }
-                            ExReleaseResourceLite(&c2->lock);
+                            release_chunk_lock(c2, Vcb);
                         }
                         le2 = le2->Flink;
@@ -795,12 +795,12 @@ static NTSTATUS
write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                             goto end;
                         }
-                        ExAcquireResourceExclusiveLite(&newchunk->lock, TRUE);
+                        acquire_chunk_lock(newchunk, Vcb);
                         newchunk->balance_num = Vcb->balance.balance_num;
                         if (!find_metadata_address_in_chunk(Vcb, newchunk,
&mr->new_address)) {
-                            ExReleaseResourceLite(&newchunk->lock);
+                            release_chunk_lock(newchunk, Vcb);
                             ExReleaseResourceLite(&Vcb->chunk_lock);
                             ERR("could not find address in new chunk\n");
                             Status = STATUS_DISK_FULL;
@@ -810,7 +810,7 @@ static NTSTATUS
write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                             space_list_subtract(newchunk, FALSE, mr->new_address,
Vcb->superblock.node_size, rollback);
                         }
-                        ExReleaseResourceLite(&newchunk->lock);
+                        release_chunk_lock(newchunk, Vcb);
                     }
                     ExReleaseResourceLite(&Vcb->chunk_lock);
@@ -1340,13 +1340,13 @@ static NTSTATUS
add_data_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
         c = get_chunk_from_address(Vcb, tp->item->key.obj_id);
     if (c) {
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, Vcb);
         c->used -= tp->item->key.offset;
         space_list_add(c, tp->item->key.obj_id, tp->item->key.offset,
rollback);
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, Vcb);
     }
     ei = (EXTENT_ITEM*)tp->item->data;
@@ -1756,7 +1756,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c,
BOOL* change
         ULONG runlength, index, lastoff;
         if (newchunk) {
-            ExAcquireResourceExclusiveLite(&newchunk->lock, TRUE);
+            acquire_chunk_lock(newchunk, Vcb);
             if (find_data_address_in_chunk(Vcb, newchunk, dr->size,
&dr->new_address)) {
                 newchunk->used += dr->size;
@@ -1764,7 +1764,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c,
BOOL* change
                 done = TRUE;
             }
-            ExReleaseResourceLite(&newchunk->lock);
+            release_chunk_lock(newchunk, Vcb);
         }
         if (!done) {
@@ -1775,20 +1775,20 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk*
c, BOOL* change
                 chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry);
                 if (!c2->readonly && !c2->reloc && c2 != newchunk
&& c2->chunk_item->type == Vcb->data_flags) {
-                    ExAcquireResourceExclusiveLite(&c2->lock, TRUE);
+                    acquire_chunk_lock(c2, Vcb);
                     if ((c2->chunk_item->size - c2->used) >= dr->size) {
                         if (find_data_address_in_chunk(Vcb, c2, dr->size,
&dr->new_address)) {
                             c2->used += dr->size;
                             space_list_subtract(c2, FALSE, dr->new_address,
dr->size, &rollback);
-                            ExReleaseResourceLite(&c2->lock);
+                            release_chunk_lock(c2, Vcb);
                             newchunk = c2;
                             done = TRUE;
                             break;
                         }
                     }
-                    ExReleaseResourceLite(&c2->lock);
+                    release_chunk_lock(c2, Vcb);
                 }
                 le2 = le2->Flink;
@@ -1804,12 +1804,12 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk*
c, BOOL* change
                     goto end;
                 }
-                ExAcquireResourceExclusiveLite(&newchunk->lock, TRUE);
+                acquire_chunk_lock(newchunk, Vcb);
                 newchunk->balance_num = Vcb->balance.balance_num;
                 if (!find_data_address_in_chunk(Vcb, newchunk, dr->size,
&dr->new_address)) {
-                    ExReleaseResourceLite(&newchunk->lock);
+                    release_chunk_lock(newchunk, Vcb);
                     ExReleaseResourceLite(&Vcb->chunk_lock);
                     ERR("could not find address in new chunk\n");
                     Status = STATUS_DISK_FULL;
@@ -1819,7 +1819,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c,
BOOL* change
                     space_list_subtract(newchunk, FALSE, dr->new_address, dr->size,
&rollback);
                 }
-                ExReleaseResourceLite(&newchunk->lock);
+                release_chunk_lock(newchunk, Vcb);
             }
             ExReleaseResourceLite(&Vcb->chunk_lock);
@@ -2957,6 +2957,8 @@ static NTSTATUS try_consolidation(device_extension* Vcb, UINT64
flags, chunk** n
             ERR("do_write returned %08x\n", Status);
             return Status;
         }
+
+        free_trees(Vcb);
     }
     ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
@@ -3108,7 +3110,7 @@ void NTAPI balance_thread(void* context) {
         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
         UINT8 sort;
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, Vcb);
         if (c->chunk_item->type & BLOCK_FLAG_DATA)
             sort = BALANCE_OPTS_DATA;
@@ -3118,7 +3120,7 @@ void NTAPI balance_thread(void* context) {
             sort = BALANCE_OPTS_SYSTEM;
         else {
             ERR("unexpected chunk type %llx\n", c->chunk_item->type);
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
             break;
         }
@@ -3142,13 +3144,13 @@ void NTAPI balance_thread(void* context) {
             if (!NT_SUCCESS(Status)) {
                 ERR("load_cache_chunk returned %08x\n", Status);
                 Vcb->balance.status = Status;
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 goto end;
             }
         }
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, Vcb);
         le = le->Flink;
     }
diff --git a/drivers/filesystems/btrfs/btrfs.c b/drivers/filesystems/btrfs/btrfs.c
index bdc2988145..2582ee0b3f 100644
--- a/drivers/filesystems/btrfs/btrfs.c
+++ b/drivers/filesystems/btrfs/btrfs.c
@@ -48,11 +48,12 @@
 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF |
BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | \
                             BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO |
BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_RAID56 | \
-                            BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF |
BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
+                            BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF |
BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES | \
+                            BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD)
 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE |
BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
-static WCHAR device_name[] =
{'\\','B','t','r','f','s',0};
-static WCHAR dosdevice_name[] =
{'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
+static const WCHAR device_name[] =
{'\\','B','t','r','f','s',0};
+static const WCHAR dosdevice_name[] =
{'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
 DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x69, 0x17,
0x9a, 0x7d, 0x1d);
@@ -70,6 +71,7 @@ UINT32 mount_compress = 0;
 UINT32 mount_compress_force = 0;
 UINT32 mount_compress_type = 0;
 UINT32 mount_zlib_level = 3;
+UINT32 mount_zstd_level = 3;
 UINT32 mount_flush_interval = 30;
 UINT32 mount_max_inline = 2048;
 UINT32 mount_skip_balance = 0;
@@ -268,7 +270,7 @@ static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
 #endif
     UNICODE_STRING dosdevice_nameW;
-    ERR("DriverUnload\n");
+    TRACE("(%p)\n");
     free_cache();
@@ -295,8 +297,8 @@ static void DriverUnload(_In_ PDRIVER_OBJECT DriverObject) {
         IoUnregisterPlugPlayNotificationEx(notification_entry);
 #endif
-    dosdevice_nameW.Buffer = dosdevice_name;
-    dosdevice_nameW.Length = dosdevice_nameW.MaximumLength =
(USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
+    dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
+    dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) -
sizeof(WCHAR);
     IoDeleteSymbolicLink(&dosdevice_nameW);
     IoDeleteDevice(DriverObject->DeviceObject);
@@ -621,22 +623,32 @@ static BOOL lie_about_fs_type() {
     PPEB peb;
     LIST_ENTRY* le;
     ULONG retlen;
+#ifdef _AMD64_
+    ULONG_PTR wow64info;
+#endif
-    static WCHAR mpr[] = L"MPR.DLL";
-    static WCHAR cmd[] = L"CMD.EXE";
-    static WCHAR fsutil[] = L"FSUTIL.EXE";
+    static const WCHAR mpr[] = L"MPR.DLL";
+    static const WCHAR cmd[] = L"CMD.EXE";
+    static const WCHAR fsutil[] = L"FSUTIL.EXE";
     UNICODE_STRING mprus, cmdus, fsutilus;
-    mprus.Buffer = mpr;
-    mprus.Length = mprus.MaximumLength = (USHORT)(wcslen(mpr) * sizeof(WCHAR));
-    cmdus.Buffer = cmd;
-    cmdus.Length = cmdus.MaximumLength = (USHORT)(wcslen(cmd) * sizeof(WCHAR));
-    fsutilus.Buffer = fsutil;
-    fsutilus.Length = fsutilus.MaximumLength = (USHORT)(wcslen(fsutil) * sizeof(WCHAR));
+    mprus.Buffer = (WCHAR*)mpr;
+    mprus.Length = mprus.MaximumLength = sizeof(mpr) - sizeof(WCHAR);
+    cmdus.Buffer = (WCHAR*)cmd;
+    cmdus.Length = cmdus.MaximumLength = sizeof(cmd) - sizeof(WCHAR);
+    fsutilus.Buffer = (WCHAR*)fsutil;
+    fsutilus.Length = fsutilus.MaximumLength = sizeof(fsutil) - sizeof(WCHAR);
     if (!PsGetCurrentProcess())
         return FALSE;
+#ifdef _AMD64_
+    Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information,
&wow64info, sizeof(wow64info), NULL);
+
+    if (NT_SUCCESS(Status) && wow64info != 0)
+        return TRUE;
+#endif
+
     Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation,
&pbi, sizeof(pbi), &retlen);
     if (!NT_SUCCESS(Status)) {
@@ -750,13 +762,24 @@ static NTSTATUS drv_query_volume_information(_In_ PDEVICE_OBJECT
DeviceObject, _
             FILE_FS_ATTRIBUTE_INFORMATION* data = Irp->AssociatedIrp.SystemBuffer;
             BOOL overflow = FALSE;
 #ifndef __REACTOS__
-            WCHAR* fs_name = (Irp->RequestorMode == UserMode &&
lie_about_fs_type()) ? L"NTFS" : L"Btrfs";
-            ULONG fs_name_len = (ULONG)wcslen(fs_name) * sizeof(WCHAR);
+            static const WCHAR ntfs[] = L"NTFS";
+#endif
+            static const WCHAR btrfs[] = L"Btrfs";
+            const WCHAR* fs_name;
+            ULONG fs_name_len, orig_fs_name_len;
+
+#ifndef __REACTOS__
+            if (Irp->RequestorMode == UserMode && lie_about_fs_type()) {
+                fs_name = ntfs;
+                orig_fs_name_len = fs_name_len = sizeof(ntfs) - sizeof(WCHAR);
+            } else {
+                fs_name = btrfs;
+                orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
+            }
 #else
-            WCHAR* fs_name = L"Btrfs";
-            ULONG fs_name_len = 5 * sizeof(WCHAR);
+            fs_name = btrfs;
+            orig_fs_name_len = fs_name_len = sizeof(btrfs) - sizeof(WCHAR);
 #endif
-            ULONG orig_fs_name_len = fs_name_len;
             TRACE("FileFsAttributeInformation\n");
@@ -1705,7 +1728,7 @@ static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP
Irp) {
     CcUninitializeCacheMap(FileObject, NULL, NULL);
     if (open_files == 0 && fcb->Vcb->removing) {
-        uninit(fcb->Vcb, FALSE);
+        uninit(fcb->Vcb);
         return STATUS_SUCCESS;
     }
@@ -1726,8 +1749,9 @@ static NTSTATUS close_file(_In_ PFILE_OBJECT FileObject, _In_ PIRP
Irp) {
     return STATUS_SUCCESS;
 }
-void uninit(_In_ device_extension* Vcb, _In_ BOOL flush) {
+void uninit(_In_ device_extension* Vcb) {
     UINT64 i;
+    KIRQL irql;
     NTSTATUS Status;
     LIST_ENTRY* le;
     LARGE_INTEGER time;
@@ -1738,6 +1762,11 @@ void uninit(_In_ device_extension* Vcb, _In_ BOOL flush) {
         ExReleaseResourceLite(&Vcb->tree_lock);
     }
+    IoAcquireVpbSpinLock(&irql);
+    Vcb->Vpb->Flags &= ~VPB_MOUNTED;
+    Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
+    IoReleaseVpbSpinLock(irql);
+
     RemoveEntryList(&Vcb->list_entry);
     if (Vcb->balance.thread) {
@@ -1787,20 +1816,6 @@ void uninit(_In_ device_extension* Vcb, _In_ BOOL flush) {
     if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
         WARN("registry_mark_volume_unmounted returned %08x\n", Status);
-    if (flush) {
-        ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
-
-        if (Vcb->need_write && !Vcb->readonly) {
-            Status = do_write(Vcb, NULL);
-            if (!NT_SUCCESS(Status))
-                ERR("do_write returned %08x\n", Status);
-        }
-
-        free_trees(Vcb);
-
-        ExReleaseResourceLite(&Vcb->tree_lock);
-    }
-
     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
         Vcb->calcthreads.threads[i].quit = TRUE;
     }
@@ -3599,7 +3614,7 @@ _Ret_maybenull_
 static root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock)
device_extension* Vcb, _In_opt_ PIRP Irp) {
     LIST_ENTRY* le;
-    static char fn[] = "default";
+    static const char fn[] = "default";
     static UINT32 crc32 = 0x8dbfc2d2;
     if (Vcb->options.subvol_id != 0) {
@@ -3764,8 +3779,8 @@ static BOOL is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
         return FALSE;
     }
-    if (mdn2->NameLength > wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR) &&
-        RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX)
* sizeof(WCHAR)) == wcslen(BTRFS_VOLUME_PREFIX) * sizeof(WCHAR)) {
+    if (mdn2->NameLength > (sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) &&
+        RtlCompareMemory(mdn2->Name, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX)
- sizeof(WCHAR)) == sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)) {
         ExFreePool(mdn2);
         return TRUE;
     }
@@ -4773,7 +4788,7 @@ static NTSTATUS verify_volume(_In_ PDEVICE_OBJECT devobj) {
         ExReleaseResourceLite(&Vcb->tree_lock);
     if (remove) {
-        uninit(Vcb, FALSE);
+        uninit(Vcb);
         return Status;
     }
@@ -4924,9 +4939,7 @@ static NTSTATUS drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject, _In_
PIRP Irp) {
     NTSTATUS Status;
     BOOL top_level;
     device_extension* Vcb = DeviceObject->DeviceExtension;
-#ifdef __REACTOS__
-    LIST_ENTRY *Vcble, *le;
-#endif
+    LIST_ENTRY* le;
     FsRtlEnterFileSystem();
@@ -4944,79 +4957,34 @@ static NTSTATUS drv_shutdown(_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp) {
     shutting_down = TRUE;
     KeSetEvent(&mountmgr_thread_event, 0, FALSE);
-#ifndef __REACTOS__
-    while (!IsListEmpty(&VcbList)) {
-        Vcb = CONTAINING_RECORD(VcbList.Flink, device_extension, list_entry);
+    le = VcbList.Flink;
+    while (le != &VcbList) {
+        BOOL open_files;
+        LIST_ENTRY* le2 = le->Flink;
-        TRACE("shutting down Vcb %p\n", Vcb);
-
-        uninit(Vcb, TRUE);
-    }
-#else
-    Vcble = VcbList.Flink;
-    while (Vcble != &VcbList) {
-        Vcb = CONTAINING_RECORD(Vcble, device_extension, list_entry);
+        Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
         TRACE("shutting down Vcb %p\n", Vcb);
-        if (Vcb->balance.thread) {
-            Vcb->balance.paused = FALSE;
-            Vcb->balance.stopping = TRUE;
-            KeSetEvent(&Vcb->balance.event, 0, FALSE);
-            KeWaitForSingleObject(&Vcb->balance.finished, Executive, KernelMode,
FALSE, NULL);
-        }
-
-        if (Vcb->scrub.thread) {
-            Vcb->scrub.paused = FALSE;
-            Vcb->scrub.stopping = TRUE;
-            KeSetEvent(&Vcb->scrub.event, 0, FALSE);
-            KeWaitForSingleObject(&Vcb->scrub.finished, Executive, KernelMode,
FALSE, NULL);
-        }
-
-        if (Vcb->running_sends != 0) {
-            BOOL send_cancelled = FALSE;
-
-            ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
-
-            le = Vcb->send_ops.Flink;
-            while (le != &Vcb->send_ops) {
-                send_info* send = CONTAINING_RECORD(le, send_info, list_entry);
-
-                if (!send->cancelling) {
-                    send->cancelling = TRUE;
-                    send_cancelled = TRUE;
-                    send->ccb = NULL;
-                    KeSetEvent(&send->cleared_event, 0, FALSE);
-                }
-
-                le = le->Flink;
-            }
-
-            ExReleaseResourceLite(&Vcb->send_load_lock);
-
-            if (send_cancelled) {
-                while (Vcb->running_sends != 0) {
-                    ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, TRUE);
-                    ExReleaseResourceLite(&Vcb->send_load_lock);
-                }
-            }
-        }
-
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
+        Vcb->removing = TRUE;
+        open_files = Vcb->open_files > 0;
         if (Vcb->need_write && !Vcb->readonly) {
             Status = do_write(Vcb, Irp);
-
             if (!NT_SUCCESS(Status))
                 ERR("do_write returned %08x\n", Status);
         }
-        Vcb->removing = TRUE;
+        free_trees(Vcb);
         ExReleaseResourceLite(&Vcb->tree_lock);
-        Vcble = Vcble->Flink;
+
+        if (!open_files)
+            uninit(Vcb);
+
+        le = le2;
     }
-#endif
 #ifdef _DEBUG
     if (comfo) {
@@ -5333,7 +5301,7 @@ static void init_logging() {
             FILE_STANDARD_INFORMATION fsi;
             FILE_POSITION_INFORMATION fpi;
-            static char delim[] = "\n---\n";
+            static const char delim[] = "\n---\n";
             // move to end of file
@@ -5353,7 +5321,7 @@ static void init_logging() {
                 goto end;
             }
-            Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, delim,
(ULONG)strlen(delim), NULL, NULL);
+            Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, (void*)delim,
sizeof(delim) - 1, NULL, NULL);
             if (!NT_SUCCESS(Status)) {
                 ERR("ZwWriteFile returned %08x\n", Status);
@@ -5453,7 +5421,7 @@ NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT
PhysicalDeviceObj
     ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
-    volname.Length = volname.MaximumLength = (USHORT)((wcslen(BTRFS_VOLUME_PREFIX) + 36 +
1) * sizeof(WCHAR));
+    volname.Length = volname.MaximumLength = (sizeof(BTRFS_VOLUME_PREFIX) -
sizeof(WCHAR)) + ((36 + 1) * sizeof(WCHAR));
     volname.Buffer = ExAllocatePoolWithTag(PagedPool, volname.MaximumLength, ALLOC_TAG);
// FIXME - when do we free this?
     if (!volname.Buffer) {
@@ -5462,9 +5430,9 @@ NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT
PhysicalDeviceObj
         goto end2;
     }
-    RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, wcslen(BTRFS_VOLUME_PREFIX) *
sizeof(WCHAR));
+    RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) -
sizeof(WCHAR));
-    j = (ULONG)wcslen(BTRFS_VOLUME_PREFIX);
+    j = (sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1;
     for (i = 0; i < 16; i++) {
         volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] >> 4); j++;
         volname.Buffer[j] = hex_digit(pdode->uuid.uuid[i] & 0xf); j++;
@@ -5670,10 +5638,10 @@ NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_
PUNICODE_STRING Regi
     init_fast_io_dispatch(&DriverObject->FastIoDispatch);
-    device_nameW.Buffer = device_name;
-    device_nameW.Length = device_nameW.MaximumLength = (USHORT)wcslen(device_name) *
sizeof(WCHAR);
-    dosdevice_nameW.Buffer = dosdevice_name;
-    dosdevice_nameW.Length = dosdevice_nameW.MaximumLength =
(USHORT)wcslen(dosdevice_name) * sizeof(WCHAR);
+    device_nameW.Buffer = (WCHAR*)device_name;
+    device_nameW.Length = device_nameW.MaximumLength = sizeof(device_name) -
sizeof(WCHAR);
+    dosdevice_nameW.Buffer = (WCHAR*)dosdevice_name;
+    dosdevice_nameW.Length = dosdevice_nameW.MaximumLength = sizeof(dosdevice_name) -
sizeof(WCHAR);
     Status = IoCreateDevice(DriverObject, sizeof(control_device_extension),
&device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM,
                             FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
diff --git a/drivers/filesystems/btrfs/btrfs.h b/drivers/filesystems/btrfs/btrfs.h
index 27e9b63f51..eede405985 100644
--- a/drivers/filesystems/btrfs/btrfs.h
+++ b/drivers/filesystems/btrfs/btrfs.h
@@ -58,6 +58,7 @@ static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000,
0x4000000000, 0x4
 #define BTRFS_COMPRESSION_NONE  0
 #define BTRFS_COMPRESSION_ZLIB  1
 #define BTRFS_COMPRESSION_LZO   2
+#define BTRFS_COMPRESSION_ZSTD  3
 #define BTRFS_ENCRYPTION_NONE   0
@@ -103,7 +104,7 @@ static const UINT64 superblock_addrs[] = { 0x10000, 0x4000000,
0x4000000000, 0x4
 #define BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL     0x0002
 #define BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS       0x0004
 #define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO       0x0008
-#define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZOV2     0x0010
+#define BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD      0x0010
 #define BTRFS_INCOMPAT_FLAGS_BIG_METADATA       0x0020
 #define BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF      0x0040
 #define BTRFS_INCOMPAT_FLAGS_RAID56             0x0080
diff --git a/drivers/filesystems/btrfs/btrfs.rc b/drivers/filesystems/btrfs/btrfs.rc
index 50f7463328..957792add5 100644
--- a/drivers/filesystems/btrfs/btrfs.rc
+++ b/drivers/filesystems/btrfs/btrfs.rc
@@ -53,8 +53,8 @@ END
 //
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,0,2,0
- PRODUCTVERSION 1,0,2,0
+ FILEVERSION 1,1,0,0
+ PRODUCTVERSION 1,1,0,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -70,12 +70,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs"
-            VALUE "FileVersion", "1.0.2"
+            VALUE "FileVersion", "1.1"
             VALUE "InternalName", "btrfs"
             VALUE "LegalCopyright", "Copyright (c) Mark Harmstone
2016-18"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.0.2"
+            VALUE "ProductVersion", "1.1"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/drivers/filesystems/btrfs/btrfs_drv.h b/drivers/filesystems/btrfs/btrfs_drv.h
index 857555f755..e6ea5891f3 100644
--- a/drivers/filesystems/btrfs/btrfs_drv.h
+++ b/drivers/filesystems/btrfs/btrfs_drv.h
@@ -74,6 +74,7 @@
 // #define DEBUG_LONG_MESSAGES
 // #define DEBUG_FLUSH_TIMES
 // #define DEBUG_STATS
+// #define DEBUG_CHUNK_LOCKS
 #define DEBUG_PARANOID
 #endif
@@ -110,6 +111,11 @@
 #define IO_REPARSE_TAG_LXSS_SYMLINK 0xa000001d // undocumented?
+#define IO_REPARSE_TAG_LXSS_SOCKET      0x80000023
+#define IO_REPARSE_TAG_LXSS_FIFO        0x80000024
+#define IO_REPARSE_TAG_LXSS_CHARDEV     0x80000025
+#define IO_REPARSE_TAG_LXSS_BLOCKDEV    0x80000026
+
 #define BTRFS_VOLUME_PREFIX L"\\Device\\Btrfs{"
 #ifdef _MSC_VER
@@ -234,7 +240,8 @@ typedef struct {
 enum prop_compression_type {
     PropCompression_None,
     PropCompression_Zlib,
-    PropCompression_LZO
+    PropCompression_LZO,
+    PropCompression_ZSTD
 };
 typedef struct {
@@ -606,6 +613,7 @@ typedef struct {
     UINT8 compress_type;
     BOOL readonly;
     UINT32 zlib_level;
+    UINT32 zstd_level;
     UINT32 flush_interval;
     UINT32 max_inline;
     UINT64 subvol_id;
@@ -716,6 +724,9 @@ typedef struct _device_extension {
     LIST_ENTRY devices;
 #ifdef DEBUG_STATS
     debug_stats stats;
+#endif
+#ifdef DEBUG_CHUNK_LOCKS
+    LONG chunk_locks_held;
 #endif
     UINT64 devices_loaded;
     superblock superblock;
@@ -955,10 +966,10 @@ static __inline UINT64 unix_time_to_win(BTRFS_TIME* t) {
 }
 static __inline void win_time_to_unix(LARGE_INTEGER t, BTRFS_TIME* out) {
-    ULONGLONG l = t.QuadPart - 116444736000000000;
+    ULONGLONG l = (ULONGLONG)t.QuadPart - 116444736000000000;
     out->seconds = l / 10000000;
-    out->nanoseconds = (l % 10000000) * 100;
+    out->nanoseconds = (UINT32)((l % 10000000) * 100);
 }
 _Post_satisfies_(*stripe>=0&&*stripe<num_stripes)
@@ -1083,13 +1094,21 @@ void protect_superblocks(_Inout_ chunk* c);
 BOOL is_top_level(_In_ PIRP Irp);
 NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock)
device_extension* Vcb, _In_ UINT64 id,
                      _Out_ root** rootptr, _In_ BOOL no_tree, _In_ UINT64 offset,
_In_opt_ PIRP Irp);
-void uninit(_In_ device_extension* Vcb, _In_ BOOL flush);
+void uninit(_In_ device_extension* Vcb);
 NTSTATUS dev_ioctl(_In_ PDEVICE_OBJECT DeviceObject, _In_ ULONG ControlCode,
_In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize,
                    _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, _In_
ULONG OutputBufferSize, _In_ BOOLEAN Override, _Out_opt_ IO_STATUS_BLOCK* iosb);
 BOOL is_file_name_valid(_In_ PUNICODE_STRING us, _In_ BOOL posix);
 void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_
ULONG action, _In_opt_ PUNICODE_STRING stream);
 void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_match, _In_ ULONG
action, _In_opt_ PUNICODE_STRING stream);
+#ifdef DEBUG_CHUNK_LOCKS
+#define acquire_chunk_lock(c, Vcb) { ExAcquireResourceExclusiveLite(&c->lock,
TRUE); InterlockedIncrement(&Vcb->chunk_locks_held); }
+#define release_chunk_lock(c, Vcb) { InterlockedDecrement(&Vcb->chunk_locks_held);
ExReleaseResourceLite(&c->lock); }
+#else
+#define acquire_chunk_lock(c, Vcb) ExAcquireResourceExclusiveLite(&(c)->lock,
TRUE)
+#define release_chunk_lock(c, Vcb) ExReleaseResourceLite(&(c)->lock)
+#endif
+
 _Ret_z_
 WCHAR* file_desc(_In_ PFILE_OBJECT FileObject);
 WCHAR* file_desc_fileref(_In_ file_ref* fileref);
@@ -1123,6 +1142,7 @@ extern UINT32 mount_compress;
 extern UINT32 mount_compress_force;
 extern UINT32 mount_compress_type;
 extern UINT32 mount_zlib_level;
+extern UINT32 mount_zstd_level;
 extern UINT32 mount_flush_interval;
 extern UINT32 mount_max_inline;
 extern UINT32 mount_skip_balance;
@@ -1208,6 +1228,18 @@ typedef struct {
     LIST_ENTRY list_entry;
 } rollback_item;
+typedef struct {
+    ANSI_STRING name;
+    ANSI_STRING value;
+    UCHAR flags;
+    LIST_ENTRY list_entry;
+} ea_item;
+
+static const char lxuid[] = "$LXUID";
+static const char lxgid[] = "$LXGID";
+static const char lxmod[] = "$LXMOD";
+static const char lxdev[] = "$LXDEV";
+
 // in treefuncs.c
 NTSTATUS find_item(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb,
_In_ root* r, _Out_ traverse_ptr* tp,
                    _In_ const KEY* searchkey, _In_ BOOL ignore, _In_opt_ PIRP Irp);
@@ -1307,6 +1339,7 @@ _Function_class_(DRIVER_DISPATCH)
 NTSTATUS NTAPI drv_directory_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 ULONG get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
ULONG atts, BOOL lxss, PIRP Irp);
+ULONG get_reparse_tag_fcb(fcb* fcb);
 // in security.c
@@ -1353,6 +1386,7 @@ void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc);
 // in reparse.c
 NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, void*
buffer, DWORD buflen, ULONG_PTR* retlen);
+NTSTATUS set_reparse_point2(fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, ccb* ccb,
file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback);
 NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 NTSTATUS delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp);
@@ -1479,6 +1513,7 @@ void watch_registry(HANDLE regh);
 // in compress.c
 NTSTATUS zlib_decompress(UINT8* inbuf, UINT32 inlen, UINT8* outbuf, UINT32 outlen);
 NTSTATUS lzo_decompress(UINT8* inbuf, UINT32 inlen, UINT8* outbuf, UINT32 outlen, UINT32
inpageoff);
+NTSTATUS zstd_decompress(UINT8* inbuf, UINT32 inlen, UINT8* outbuf, UINT32 outlen);
 NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data,
BOOL* compressed, PIRP Irp, LIST_ENTRY* rollback);
 // in galois.c
@@ -1720,6 +1755,10 @@ static __inline void do_xor(UINT8* buf1, UINT8* buf2, UINT32 len) {
 #define S_ISVTX 0001000
 #endif
+// based on functions in sys/sysmacros.h
+#define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((UINT32)((rdev) >> 32)
& ~0xFFF))
+#define minor(rdev) (((rdev) & 0xFF) | ((UINT32)((rdev) >> 12) & ~0xFF))
+
 static __inline UINT64 fcb_alloc_size(fcb* fcb) {
     if (S_ISDIR(fcb->inode_item.st_mode))
         return 0;
diff --git a/drivers/filesystems/btrfs/btrfsioctl.h
b/drivers/filesystems/btrfs/btrfsioctl.h
index 9c57c9cb60..a7f53b3317 100644
--- a/drivers/filesystems/btrfs/btrfsioctl.h
+++ b/drivers/filesystems/btrfs/btrfsioctl.h
@@ -63,6 +63,7 @@ typedef struct {
 #define BTRFS_COMPRESSION_ANY   0
 #define BTRFS_COMPRESSION_ZLIB  1
 #define BTRFS_COMPRESSION_LZO   2
+#define BTRFS_COMPRESSION_ZSTD  3
 typedef struct {
     UINT64 subvol;
@@ -79,6 +80,25 @@ typedef struct {
     UINT8 compression_type;
 } btrfs_inode_info;
+typedef struct {
+    UINT64 subvol;
+    UINT64 inode;
+    BOOL top;
+    UINT8 type;
+    UINT32 st_uid;
+    UINT32 st_gid;
+    UINT32 st_mode;
+    UINT64 st_rdev;
+    UINT64 flags;
+    UINT32 inline_length;
+    UINT64 disk_size_uncompressed;
+    UINT64 disk_size_zlib;
+    UINT64 disk_size_lzo;
+    UINT8 compression_type;
+    UINT64 disk_size_zstd;
+    UINT64 sparse_size;
+} btrfs_inode_info2;
+
 typedef struct {
     UINT64 flags;
     BOOL flags_changed;
diff --git a/drivers/filesystems/btrfs/compress.c b/drivers/filesystems/btrfs/compress.c
index 0a8a1f7269..598171b678 100644
--- a/drivers/filesystems/btrfs/compress.c
+++ b/drivers/filesystems/btrfs/compress.c
@@ -39,6 +39,10 @@
 #include <zlib.h>
 #endif
+#define ZSTD_STATIC_LINKING_ONLY
+
+#include "zstd/zstd.h"
+
 #define LINUX_PAGE_SIZE 4096
 typedef struct {
@@ -82,6 +86,16 @@ typedef struct {
 #define LZO_BYTE(x) ((unsigned char) (x))
+#define ZSTD_ALLOC_TAG 0x6474737a // "zstd"
+
+// needs to be the same as Linux (fs/btrfs/zstd.c)
+#define ZSTD_BTRFS_MAX_WINDOWLOG 17
+
+static void* zstd_malloc(void* opaque, size_t size);
+static void zstd_free(void* opaque, void* address);
+
+ZSTD_customMem zstd_mem = { .customAlloc = zstd_malloc, .customFree = zstd_free, .opaque
= NULL };
+
 static UINT8 lzo_nextbyte(lzo_stream* stream) {
     UINT8 c;
@@ -449,7 +463,7 @@ static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data,
UINT64 en
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (!c->readonly && !c->reloc) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, fcb->Vcb);
             if (c->chunk_item->type == fcb->Vcb->data_flags &&
(c->chunk_item->size - c->used) >= comp_length) {
                 if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length,
FALSE, comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
@@ -462,7 +476,7 @@ static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data,
UINT64 en
                 }
             }
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, fcb->Vcb);
         }
         le = le->Flink;
@@ -486,7 +500,7 @@ static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data,
UINT64 en
     }
     if (c) {
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, fcb->Vcb);
         if (c->chunk_item->type == fcb->Vcb->data_flags &&
(c->chunk_item->size - c->used) >= comp_length) {
             if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE,
comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
@@ -497,7 +511,7 @@ static NTSTATUS zlib_write_compressed_bit(fcb* fcb, UINT64 start_data,
UINT64 en
             }
         }
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, fcb->Vcb);
     }
     WARN("couldn't find any data chunks with %llx bytes free\n",
comp_length);
@@ -847,7 +861,7 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64 start_data,
UINT64 end
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (!c->readonly && !c->reloc) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, fcb->Vcb);
             if (c->chunk_item->type == fcb->Vcb->data_flags &&
(c->chunk_item->size - c->used) >= comp_length) {
                 if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length,
FALSE, comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
@@ -860,7 +874,7 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64 start_data,
UINT64 end
                 }
             }
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, fcb->Vcb);
         }
         le = le->Flink;
@@ -884,7 +898,7 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64 start_data,
UINT64 end
     }
     if (c) {
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, fcb->Vcb);
         if (c->chunk_item->type == fcb->Vcb->data_flags &&
(c->chunk_item->size - c->used) >= comp_length) {
             if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE,
comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
@@ -895,7 +909,173 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, UINT64
start_data, UINT64 end
             }
         }
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, fcb->Vcb);
+    }
+
+    WARN("couldn't find any data chunks with %llx bytes free\n",
comp_length);
+
+    if (compression != BTRFS_COMPRESSION_NONE)
+        ExFreePool(comp_data);
+
+    return STATUS_DISK_FULL;
+}
+
+static NTSTATUS zstd_write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64 end_data,
void* data, BOOL* compressed, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    UINT8 compression;
+    UINT32 comp_length;
+    UINT8* comp_data;
+    UINT32 out_left;
+    LIST_ENTRY* le;
+    chunk* c;
+    ZSTD_CStream* stream;
+    size_t init_res, written;
+    ZSTD_inBuffer input;
+    ZSTD_outBuffer output;
+    ZSTD_parameters params;
+
+    comp_data = ExAllocatePoolWithTag(PagedPool, (UINT32)(end_data - start_data),
ALLOC_TAG);
+    if (!comp_data) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("excise_extents returned %08x\n", Status);
+        ExFreePool(comp_data);
+        return Status;
+    }
+
+    stream = ZSTD_createCStream_advanced(zstd_mem);
+
+    if (!stream) {
+        ERR("ZSTD_createCStream failed.\n");
+        ExFreePool(comp_data);
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    params = ZSTD_getParams(fcb->Vcb->options.zstd_level, (UINT32)(end_data -
start_data), 0);
+
+    if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG)
+        params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG;
+
+    init_res = ZSTD_initCStream_advanced(stream, NULL, 0, params, (UINT32)(end_data -
start_data));
+
+    if (ZSTD_isError(init_res)) {
+        ERR("ZSTD_initCStream_advanced failed: %s\n",
ZSTD_getErrorName(init_res));
+        ZSTD_freeCStream(stream);
+        ExFreePool(comp_data);
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    input.src = data;
+    input.size = (UINT32)(end_data - start_data);
+    input.pos = 0;
+
+    output.dst = comp_data;
+    output.size = (UINT32)(end_data - start_data);
+    output.pos = 0;
+
+    while (input.pos < input.size && output.pos < output.size) {
+        written = ZSTD_compressStream(stream, &output, &input);
+
+        if (ZSTD_isError(written)) {
+            ERR("ZSTD_compressStream failed: %s\n",
ZSTD_getErrorName(written));
+            ZSTD_freeCStream(stream);
+            ExFreePool(comp_data);
+            return STATUS_INTERNAL_ERROR;
+        }
+    }
+
+    written = ZSTD_endStream(stream, &output);
+    if (ZSTD_isError(written)) {
+        ERR("ZSTD_endStream failed: %s\n", ZSTD_getErrorName(written));
+        ZSTD_freeCStream(stream);
+        ExFreePool(comp_data);
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    ZSTD_freeCStream(stream);
+
+    out_left = output.size - output.pos;
+
+    if (out_left < fcb->Vcb->superblock.sector_size) { // compressed extent
would be larger than or same size as uncompressed extent
+        ExFreePool(comp_data);
+
+        comp_length = (UINT32)(end_data - start_data);
+        comp_data = data;
+        compression = BTRFS_COMPRESSION_NONE;
+
+        *compressed = FALSE;
+    } else {
+        UINT32 cl;
+
+        compression = BTRFS_COMPRESSION_ZSTD;
+        cl = (UINT32)(end_data - start_data - out_left);
+        comp_length = (UINT32)sector_align(cl, fcb->Vcb->superblock.sector_size);
+
+        RtlZeroMemory(comp_data + cl, comp_length - cl);
+
+        *compressed = TRUE;
+    }
+
+    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
+
+    le = fcb->Vcb->chunks.Flink;
+    while (le != &fcb->Vcb->chunks) {
+        c = CONTAINING_RECORD(le, chunk, list_entry);
+
+        if (!c->readonly && !c->reloc) {
+            acquire_chunk_lock(c, fcb->Vcb);
+
+            if (c->chunk_item->type == fcb->Vcb->data_flags &&
(c->chunk_item->size - c->used) >= comp_length) {
+                if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length,
FALSE, comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
+                    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+                    if (compression != BTRFS_COMPRESSION_NONE)
+                        ExFreePool(comp_data);
+
+                    return STATUS_SUCCESS;
+                }
+            }
+
+            release_chunk_lock(c, fcb->Vcb);
+        }
+
+        le = le->Flink;
+    }
+
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
+
+    Status = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags, &c, FALSE);
+
+    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+
+    if (!NT_SUCCESS(Status)) {
+        ERR("alloc_chunk returned %08x\n", Status);
+
+        if (compression != BTRFS_COMPRESSION_NONE)
+            ExFreePool(comp_data);
+
+        return Status;
+    }
+
+    if (c) {
+        acquire_chunk_lock(c, fcb->Vcb);
+
+        if (c->chunk_item->type == fcb->Vcb->data_flags &&
(c->chunk_item->size - c->used) >= comp_length) {
+            if (insert_extent_chunk(fcb->Vcb, fcb, c, start_data, comp_length, FALSE,
comp_data, Irp, rollback, compression, end_data - start_data, FALSE, 0)) {
+                if (compression != BTRFS_COMPRESSION_NONE)
+                    ExFreePool(comp_data);
+
+                return STATUS_SUCCESS;
+            }
+        }
+
+        release_chunk_lock(c, fcb->Vcb);
     }
     WARN("couldn't find any data chunks with %llx bytes free\n",
comp_length);
@@ -912,18 +1092,82 @@ NTSTATUS write_compressed_bit(fcb* fcb, UINT64 start_data, UINT64
end_data, void
     if (fcb->Vcb->options.compress_type != 0 && fcb->prop_compression ==
PropCompression_None)
         type = fcb->Vcb->options.compress_type;
     else {
-        if (!(fcb->Vcb->superblock.incompat_flags &
BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO) && fcb->prop_compression ==
PropCompression_LZO) {
-            fcb->Vcb->superblock.incompat_flags |=
BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
+        if (!(fcb->Vcb->superblock.incompat_flags &
BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD) && fcb->prop_compression ==
PropCompression_ZSTD)
+            type = BTRFS_COMPRESSION_ZSTD;
+        else if (fcb->Vcb->superblock.incompat_flags &
BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD && fcb->prop_compression !=
PropCompression_Zlib && fcb->prop_compression != PropCompression_LZO)
+            type = BTRFS_COMPRESSION_ZSTD;
+        else if (!(fcb->Vcb->superblock.incompat_flags &
BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO) && fcb->prop_compression ==
PropCompression_LZO)
             type = BTRFS_COMPRESSION_LZO;
-        } else if (fcb->Vcb->superblock.incompat_flags &
BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO && fcb->prop_compression !=
PropCompression_Zlib)
+        else if (fcb->Vcb->superblock.incompat_flags &
BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO && fcb->prop_compression !=
PropCompression_Zlib)
             type = BTRFS_COMPRESSION_LZO;
         else
             type = BTRFS_COMPRESSION_ZLIB;
     }
-    if (type == BTRFS_COMPRESSION_LZO) {
+    if (type == BTRFS_COMPRESSION_ZSTD) {
+        fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD;
+        return zstd_write_compressed_bit(fcb, start_data, end_data, data, compressed,
Irp, rollback);
+    } else if (type == BTRFS_COMPRESSION_LZO) {
         fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
         return lzo_write_compressed_bit(fcb, start_data, end_data, data, compressed, Irp,
rollback);
     } else
         return zlib_write_compressed_bit(fcb, start_data, end_data, data, compressed,
Irp, rollback);
 }
+
+static void* zstd_malloc(void* opaque, size_t size) {
+    UNUSED(opaque);
+
+    return ExAllocatePoolWithTag(PagedPool, size, ZSTD_ALLOC_TAG);
+}
+
+static void zstd_free(void* opaque, void* address) {
+    UNUSED(opaque);
+
+    ExFreePool(address);
+}
+
+NTSTATUS zstd_decompress(UINT8* inbuf, UINT32 inlen, UINT8* outbuf, UINT32 outlen) {
+    NTSTATUS Status;
+    ZSTD_DStream* stream;
+    size_t init_res, read;
+    ZSTD_inBuffer input;
+    ZSTD_outBuffer output;
+
+    stream = ZSTD_createDStream_advanced(zstd_mem);
+
+    if (!stream) {
+        ERR("ZSTD_createDStream failed.\n");
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    init_res = ZSTD_initDStream(stream);
+
+    if (ZSTD_isError(init_res)) {
+        ERR("ZSTD_initDStream failed: %s\n", ZSTD_getErrorName(init_res));
+        Status = STATUS_INTERNAL_ERROR;
+        goto end;
+    }
+
+    input.src = inbuf;
+    input.size = inlen;
+    input.pos = 0;
+
+    output.dst = outbuf;
+    output.size = outlen;
+    output.pos = 0;
+
+    read = ZSTD_decompressStream(stream, &output, &input);
+
+    if (ZSTD_isError(read)) {
+        ERR("ZSTD_decompressStream failed: %s\n", ZSTD_getErrorName(read));
+        Status = STATUS_INTERNAL_ERROR;
+        goto end;
+    }
+
+    Status = STATUS_SUCCESS;
+
+end:
+    ZSTD_freeDStream(stream);
+
+    return Status;
+}
diff --git a/drivers/filesystems/btrfs/create.c b/drivers/filesystems/btrfs/create.c
index 4c4c5dd984..1b4417e802 100644
--- a/drivers/filesystems/btrfs/create.c
+++ b/drivers/filesystems/btrfs/create.c
@@ -23,7 +23,24 @@
 extern PDEVICE_OBJECT master_devobj;
-static WCHAR datastring[] = L"::$DATA";
+static const WCHAR datastring[] = L"::$DATA";
+
+// Windows 10
+#define ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED   0x0002
+#define ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT               0x0100
+#define ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET        0x0002
+
+typedef struct _ATOMIC_CREATE_ECP_CONTEXT {
+    USHORT Size;
+    USHORT InFlags;
+    USHORT OutFlags;
+    USHORT ReparseBufferLength;
+    PREPARSE_DATA_BUFFER ReparseBuffer;
+    LONGLONG FileSize;
+    LONGLONG ValidDataLength;
+} ATOMIC_CREATE_ECP_CONTEXT, *PATOMIC_CREATE_ECP_CONTEXT;
+
+static const GUID GUID_ECP_ATOMIC_CREATE = { 0x4720bd83, 0x52ac, 0x4104, { 0xa1, 0x30,
0xd1, 0xec, 0x6a, 0x8c, 0xc8, 0xe5 } };
 fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) {
     fcb* fcb;
@@ -308,11 +325,11 @@ static NTSTATUS split_path(device_extension* Vcb, PUNICODE_STRING
path, LIST_ENT
     InsertTailList(parts, &nb->list_entry);
     if (has_stream) {
-        static WCHAR datasuf[] =
{':','$','D','A','T','A',0};
+        static const WCHAR datasuf[] =
{':','$','D','A','T','A',0};
         UNICODE_STRING dsus;
-        dsus.Buffer = datasuf;
-        dsus.Length = dsus.MaximumLength = (UINT16)wcslen(datasuf) * sizeof(WCHAR);
+        dsus.Buffer = (WCHAR*)datasuf;
+        dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
         for (i = 0; i < nb->us.Length / sizeof(WCHAR); i++) {
             if (nb->us.Buffer[i] == ':') {
@@ -799,7 +816,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
             ULONG len;
             DIR_ITEM* di;
-            static char xapref[] = "user.";
+            static const char xapref[] = "user.";
             if (tp.item->size < offsetof(DIR_ITEM, name[0])) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n",
tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
tp.item->size, offsetof(DIR_ITEM, name[0]));
@@ -813,7 +830,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
                 if (len < offsetof(DIR_ITEM, name[0]) + di->m + di->n)
                     break;
-                if (tp.item->key.offset == EA_REPARSE_HASH && di->n ==
strlen(EA_REPARSE) && RtlCompareMemory(EA_REPARSE, di->name, di->n) ==
di->n) {
+                if (tp.item->key.offset == EA_REPARSE_HASH && di->n ==
sizeof(EA_REPARSE) - 1 && RtlCompareMemory(EA_REPARSE, di->name, di->n) ==
di->n) {
                     if (di->m > 0) {
                         fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool,
di->m, ALLOC_TAG);
                         if (!fcb->reparse_xattr.Buffer) {
@@ -827,7 +844,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
                         fcb->reparse_xattr.Buffer = NULL;
                     fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength =
di->m;
-                } else if (tp.item->key.offset == EA_EA_HASH && di->n ==
strlen(EA_EA) && RtlCompareMemory(EA_EA, di->name, di->n) == di->n) {
+                } else if (tp.item->key.offset == EA_EA_HASH && di->n ==
sizeof(EA_EA) - 1 && RtlCompareMemory(EA_EA, di->name, di->n) == di->n) {
                     if (di->m > 0) {
                         ULONG offset;
@@ -863,7 +880,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
                             } while (TRUE);
                         }
                     }
-                } else if (tp.item->key.offset == EA_DOSATTRIB_HASH &&
di->n == strlen(EA_DOSATTRIB) && RtlCompareMemory(EA_DOSATTRIB, di->name,
di->n) == di->n) {
+                } else if (tp.item->key.offset == EA_DOSATTRIB_HASH &&
di->n == sizeof(EA_DOSATTRIB) - 1 && RtlCompareMemory(EA_DOSATTRIB,
di->name, di->n) == di->n) {
                     if (di->m > 0) {
                         if (get_file_attributes_from_xattr(&di->name[di->n],
di->m, &fcb->atts)) {
                             atts_set = TRUE;
@@ -884,7 +901,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
                             }
                         }
                     }
-                } else if (tp.item->key.offset == EA_NTACL_HASH && di->n ==
strlen(EA_NTACL) && RtlCompareMemory(EA_NTACL, di->name, di->n) == di->n)
{
+                } else if (tp.item->key.offset == EA_NTACL_HASH && di->n ==
sizeof(EA_NTACL) - 1 && RtlCompareMemory(EA_NTACL, di->name, di->n) ==
di->n) {
                     if (di->m > 0) {
                         fcb->sd = ExAllocatePoolWithTag(PagedPool, di->m,
ALLOC_TAG);
                         if (!fcb->sd) {
@@ -902,23 +919,26 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
                         else
                             sd_set = TRUE;
                     }
-                } else if (tp.item->key.offset == EA_PROP_COMPRESSION_HASH &&
di->n == strlen(EA_PROP_COMPRESSION) && RtlCompareMemory(EA_PROP_COMPRESSION,
di->name, di->n) == di->n) {
+                } else if (tp.item->key.offset == EA_PROP_COMPRESSION_HASH &&
di->n == sizeof(EA_PROP_COMPRESSION) - 1 &&
RtlCompareMemory(EA_PROP_COMPRESSION, di->name, di->n) == di->n) {
                     if (di->m > 0) {
-                        const char lzo[] = "lzo";
-                        const char zlib[] = "zlib";
+                        static const char lzo[] = "lzo";
+                        static const char zlib[] = "zlib";
+                        static const char zstd[] = "zstd";
-                        if (di->m == strlen(lzo) &&
RtlCompareMemory(&di->name[di->n], lzo, di->m) == di->m)
+                        if (di->m == sizeof(lzo) - 1 &&
RtlCompareMemory(&di->name[di->n], lzo, di->m) == di->m)
                             fcb->prop_compression = PropCompression_LZO;
-                        else if (di->m == strlen(zlib) &&
RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m)
+                        else if (di->m == sizeof(zlib) - 1 &&
RtlCompareMemory(&di->name[di->n], zlib, di->m) == di->m)
                             fcb->prop_compression = PropCompression_Zlib;
+                        else if (di->m == sizeof(zstd) - 1 &&
RtlCompareMemory(&di->name[di->n], zstd, di->m) == di->m)
+                            fcb->prop_compression = PropCompression_ZSTD;
                         else
                             fcb->prop_compression = PropCompression_None;
                     }
-                } else if (di->n > strlen(xapref) &&
RtlCompareMemory(xapref, di->name, strlen(xapref)) == strlen(xapref)) {
+                } else if (di->n > sizeof(xapref) - 1 &&
RtlCompareMemory(xapref, di->name, sizeof(xapref) - 1) == sizeof(xapref) - 1) {
                     dir_child* dc;
                     ULONG utf16len;
-                    Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len,
&di->name[strlen(xapref)], di->n - (ULONG)strlen(xapref));
+                    Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len,
&di->name[sizeof(xapref) - 1], di->n + 1 - sizeof(xapref));
                     if (!NT_SUCCESS(Status)) {
                         ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status);
                         free_fcb(Vcb, fcb);
@@ -934,7 +954,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
                     RtlZeroMemory(dc, sizeof(dir_child));
-                    dc->utf8.MaximumLength = dc->utf8.Length = di->n -
(UINT16)strlen(xapref);
+                    dc->utf8.MaximumLength = dc->utf8.Length = di->n + 1 -
sizeof(xapref);
                     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool,
dc->utf8.MaximumLength, ALLOC_TAG);
                     if (!dc->utf8.Buffer) {
                         ERR("out of memory\n");
@@ -943,7 +963,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusive_lo
                         return STATUS_INSUFFICIENT_RESOURCES;
                     }
-                    RtlCopyMemory(dc->utf8.Buffer, &di->name[strlen(xapref)],
dc->utf8.Length);
+                    RtlCopyMemory(dc->utf8.Buffer, &di->name[sizeof(xapref) -
1], dc->utf8.Length);
                     dc->name.MaximumLength = dc->name.Length = (UINT16)utf16len;
                     dc->name.Buffer = ExAllocatePoolWithTag(PagedPool,
dc->name.MaximumLength, ALLOC_TAG);
@@ -1113,11 +1133,11 @@ static NTSTATUS
open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Require
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp;
-    static char xapref[] = "user.";
+    static const char xapref[] = "user.";
     ANSI_STRING xattr;
     UINT32 crc32;
-    xattr.Length = (UINT16)strlen(xapref) + dc->utf8.Length;
+    xattr.Length = sizeof(xapref) - 1 + dc->utf8.Length;
     xattr.MaximumLength = xattr.Length + 1;
     xattr.Buffer = ExAllocatePoolWithTag(PagedPool, xattr.MaximumLength, ALLOC_TAG);
     if (!xattr.Buffer) {
@@ -1125,8 +1145,8 @@ static NTSTATUS
open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Require
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    RtlCopyMemory(xattr.Buffer, xapref, strlen(xapref));
-    RtlCopyMemory(&xattr.Buffer[strlen(xapref)], dc->utf8.Buffer,
dc->utf8.Length);
+    RtlCopyMemory(xattr.Buffer, xapref, sizeof(xapref) - 1);
+    RtlCopyMemory(&xattr.Buffer[sizeof(xapref) - 1], dc->utf8.Buffer,
dc->utf8.Length);
     xattr.Buffer[xattr.Length] = 0;
     fcb = create_fcb(Vcb, PagedPool);
@@ -1454,7 +1474,7 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock)
_Requires_exclusiv
     InitializeListHead(&parts);
     if (fnus->Length != 0 &&
-        (fnus->Length != wcslen(datastring) * sizeof(WCHAR) ||
RtlCompareMemory(fnus->Buffer, datastring, wcslen(datastring) * sizeof(WCHAR)) !=
wcslen(datastring) * sizeof(WCHAR))) {
+        (fnus->Length != sizeof(datastring) - sizeof(WCHAR) ||
RtlCompareMemory(fnus->Buffer, datastring, sizeof(datastring) - sizeof(WCHAR)) !=
sizeof(datastring) - sizeof(WCHAR))) {
         Status = split_path(Vcb, &fnus2, &parts, &has_stream);
         if (!NT_SUCCESS(Status)) {
             ERR("split_path returned %08x\n", Status);
@@ -1649,6 +1669,218 @@ UINT32 inherit_mode(fcb* parfcb, BOOL is_dir) {
     return mode;
 }
+static NTSTATUS file_create_parse_ea(fcb* fcb, FILE_FULL_EA_INFORMATION* ea) {
+    NTSTATUS Status;
+    LIST_ENTRY ealist, *le;
+    UINT16 size = 0;
+    char* buf;
+
+    InitializeListHead(&ealist);
+
+    do {
+        STRING s;
+        BOOL found = FALSE;
+
+        s.Length = s.MaximumLength = ea->EaNameLength;
+        s.Buffer = ea->EaName;
+
+        RtlUpperString(&s, &s);
+
+        le = ealist.Flink;
+        while (le != &ealist) {
+            ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
+
+            if (item->name.Length == s.Length &&
RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) {
+                item->flags = ea->Flags;
+                item->value.Length = item->value.MaximumLength =
ea->EaValueLength;
+                item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
+                found = TRUE;
+                break;
+            }
+
+            le = le->Flink;
+        }
+
+        if (!found) {
+            ea_item* item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG);
+            if (!item) {
+                ERR("out of memory\n");
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end;
+            }
+
+            item->name.Length = item->name.MaximumLength = ea->EaNameLength;
+            item->name.Buffer = ea->EaName;
+
+            item->value.Length = item->value.MaximumLength = ea->EaValueLength;
+            item->value.Buffer = &ea->EaName[ea->EaNameLength + 1];
+
+            item->flags = ea->Flags;
+
+            InsertTailList(&ealist, &item->list_entry);
+        }
+
+        if (ea->NextEntryOffset == 0)
+            break;
+
+        ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
+    } while (TRUE);
+
+    // handle LXSS values
+    le = ealist.Flink;
+    while (le != &ealist) {
+        LIST_ENTRY* le2 = le->Flink;
+        ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
+
+        if (item->name.Length == sizeof(lxuid) - 1 &&
RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) ==
item->name.Length) {
+            if (item->value.Length < sizeof(UINT32)) {
+                ERR("uid value was shorter than expected\n");
+                Status = STATUS_INVALID_PARAMETER;
+                goto end;
+            }
+
+            RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer,
sizeof(UINT32));
+            fcb->sd_dirty = TRUE;
+            fcb->sd_deleted = FALSE;
+
+            RemoveEntryList(&item->list_entry);
+            ExFreePool(item);
+        } else if (item->name.Length == sizeof(lxgid) - 1 &&
RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) ==
item->name.Length) {
+            if (item->value.Length < sizeof(UINT32)) {
+                ERR("gid value was shorter than expected\n");
+                Status = STATUS_INVALID_PARAMETER;
+                goto end;
+            }
+
+            RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer,
sizeof(UINT32));
+
+            RemoveEntryList(&item->list_entry);
+            ExFreePool(item);
+        } else if (item->name.Length == sizeof(lxmod) - 1 &&
RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) ==
item->name.Length) {
+            UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID;
+            UINT32 val;
+
+            if (item->value.Length < sizeof(UINT32)) {
+                ERR("mode value was shorter than expected\n");
+                Status = STATUS_INVALID_PARAMETER;
+                goto end;
+            }
+
+            RtlCopyMemory(&val, item->value.Buffer, sizeof(UINT32));
+
+            if (fcb->type != BTRFS_TYPE_DIRECTORY)
+                allowed |= __S_IFIFO | __S_IFCHR | __S_IFBLK | __S_IFSOCK;
+
+            fcb->inode_item.st_mode &= ~allowed;
+            fcb->inode_item.st_mode |= val & allowed;
+
+            if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+                if ((fcb->inode_item.st_mode & __S_IFCHR) == __S_IFCHR)
+                    fcb->type = BTRFS_TYPE_CHARDEV;
+                else if ((fcb->inode_item.st_mode & __S_IFBLK) == __S_IFBLK)
+                    fcb->type = BTRFS_TYPE_BLOCKDEV;
+                else if ((fcb->inode_item.st_mode & __S_IFIFO) == __S_IFIFO)
+                    fcb->type = BTRFS_TYPE_FIFO;
+                else if ((fcb->inode_item.st_mode & __S_IFSOCK) == __S_IFSOCK)
+                    fcb->type = BTRFS_TYPE_SOCKET;
+            }
+
+            RemoveEntryList(&item->list_entry);
+            ExFreePool(item);
+        } else if (item->name.Length == sizeof(lxdev) - 1 &&
RtlCompareMemory(item->name.Buffer, lxdev, item->name.Length) ==
item->name.Length) {
+            UINT32 major, minor;
+
+            if (item->value.Length < sizeof(UINT64)) {
+                ERR("dev value was shorter than expected\n");
+                Status = STATUS_INVALID_PARAMETER;
+                goto end;
+            }
+
+            major = *(UINT32*)item->value.Buffer;
+            minor = *(UINT32*)&item->value.Buffer[sizeof(UINT32)];
+
+            fcb->inode_item.st_rdev = (minor & 0xFFFFF) | ((major &
0xFFFFFFFFFFF) << 20);
+
+            RemoveEntryList(&item->list_entry);
+            ExFreePool(item);
+        }
+
+        le = le2;
+    }
+
+    if (fcb->type != BTRFS_TYPE_CHARDEV && fcb->type !=
BTRFS_TYPE_BLOCKDEV)
+        fcb->inode_item.st_rdev = 0;
+
+    if (IsListEmpty(&ealist))
+        return STATUS_SUCCESS;
+
+    le = ealist.Flink;
+    while (le != &ealist) {
+        ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
+
+        if (size % 4 > 0)
+            size += 4 - (size % 4);
+
+        size += (UINT16)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) +
item->name.Length + 1 + item->value.Length;
+
+        le = le->Flink;
+    }
+
+    buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);
+    if (!buf) {
+        ERR("out of memory\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto end;
+    }
+
+    fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size;
+    fcb->ea_xattr.Buffer = buf;
+
+    fcb->ealen = 4;
+    ea = NULL;
+
+    le = ealist.Flink;
+    while (le != &ealist) {
+        ea_item* item = CONTAINING_RECORD(le, ea_item, list_entry);
+
+        if (ea) {
+            ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0])
+ ea->EaNameLength + ea->EaValueLength;
+
+            if (ea->NextEntryOffset % 4 > 0)
+                ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4);
+
+            ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset);
+        } else
+            ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer;
+
+        ea->NextEntryOffset = 0;
+        ea->Flags = item->flags;
+        ea->EaNameLength = (UCHAR)item->name.Length;
+        ea->EaValueLength = item->value.Length;
+
+        RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
+        ea->EaName[item->name.Length] = 0;
+        RtlCopyMemory(&ea->EaName[item->name.Length + 1],
item->value.Buffer, item->value.Length);
+
+        fcb->ealen += 5 + item->name.Length + item->value.Length;
+
+        le = le->Flink;
+    }
+
+    fcb->ea_changed = TRUE;
+
+    Status = STATUS_SUCCESS;
+
+end:
+    while (!IsListEmpty(&ealist)) {
+        ea_item* item = CONTAINING_RECORD(RemoveHeadList(&ealist), ea_item,
list_entry);
+
+        ExFreePool(item);
+    }
+
+    return Status;
+}
+
 static NTSTATUS file_create2(_In_ PIRP Irp,
_Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb, _In_
PUNICODE_STRING fpus,
                              _In_ file_ref* parfileref, _In_ ULONG options,
_In_reads_bytes_opt_(ealen) FILE_FULL_EA_INFORMATION* ea, _In_ ULONG ealen,
                              _Out_ file_ref** pfr, _In_ LIST_ENTRY* rollback) {
@@ -1843,44 +2075,17 @@ static NTSTATUS file_create2(_In_ PIRP Irp,
_Requires_exclusive_lock_held_(_Curr
     fcb->sd_dirty = TRUE;
     if (ea && ealen > 0) {
-        FILE_FULL_EA_INFORMATION* eainfo;
-
-        fcb->ealen = 4;
-
-        // capitalize EA names
-        eainfo = ea;
-        do {
-            STRING s;
-
-            s.Length = s.MaximumLength = eainfo->EaNameLength;
-            s.Buffer = eainfo->EaName;
-
-            RtlUpperString(&s, &s);
-
-            fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength;
-
-            if (eainfo->NextEntryOffset == 0)
-                break;
-
-            eainfo = (FILE_FULL_EA_INFORMATION*)(((UINT8*)eainfo) +
eainfo->NextEntryOffset);
-        } while (TRUE);
-
-        fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(pool_type, ealen, ALLOC_TAG);
-        if (!fcb->ea_xattr.Buffer) {
-            ERR("out of memory\n");
+        Status = file_create_parse_ea(fcb, ea);
+        if (!NT_SUCCESS(Status)) {
+            ERR("file_create_parse_ea returned %08x\n", Status);
             free_fcb(Vcb, fcb);
             ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
             parfileref->fcb->inode_item.st_size -= utf8len * 2;
             ExReleaseResourceLite(parfileref->fcb->Header.Resource);
-            return STATUS_INSUFFICIENT_RESOURCES;
+            return Status;
         }
-
-        fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = (UINT16)ealen;
-        RtlCopyMemory(fcb->ea_xattr.Buffer, ea, fcb->ea_xattr.Length);
-
-        fcb->ea_changed = TRUE;
     }
     fileref = create_fileref(Vcb);
@@ -1989,11 +2194,10 @@ static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     file_ref *fileref, *newpar, *parfileref;
     fcb* fcb;
-    static char xapref[] = "user.";
-    static WCHAR DOSATTRIB[] = L"DOSATTRIB";
-    static WCHAR EA[] = L"EA";
-    static WCHAR reparse[] = L"reparse";
-    UINT16 xapreflen = (UINT16)strlen(xapref);
+    static const char xapref[] = "user.";
+    static const WCHAR DOSATTRIB[] = L"DOSATTRIB";
+    static const WCHAR EA[] = L"EA";
+    static const WCHAR reparse[] = L"reparse";
     LARGE_INTEGER time;
     BTRFS_TIME now;
     ULONG utf8len, overhead;
@@ -2086,9 +2290,9 @@ static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
SeUnlockSubjectContext(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
-    if ((stream->Length == wcslen(DOSATTRIB) * sizeof(WCHAR) &&
RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
-        (stream->Length == wcslen(EA) * sizeof(WCHAR) &&
RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
-        (stream->Length == wcslen(reparse) * sizeof(WCHAR) &&
RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length)) {
+    if ((stream->Length == sizeof(DOSATTRIB) - sizeof(WCHAR) &&
RtlCompareMemory(stream->Buffer, DOSATTRIB, stream->Length) == stream->Length) ||
+        (stream->Length == sizeof(EA) - sizeof(WCHAR) &&
RtlCompareMemory(stream->Buffer, EA, stream->Length) == stream->Length) ||
+        (stream->Length == sizeof(reparse) - sizeof(WCHAR) &&
RtlCompareMemory(stream->Buffer, reparse, stream->Length) == stream->Length)) {
         return STATUS_OBJECT_NAME_INVALID;
     }
@@ -2124,7 +2328,7 @@ static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
         return Status;
     }
-    fcb->adsxattr.Length = (UINT16)utf8len + xapreflen;
+    fcb->adsxattr.Length = (UINT16)utf8len + sizeof(xapref) - 1;
     fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1;
     fcb->adsxattr.Buffer = ExAllocatePoolWithTag(pool_type,
fcb->adsxattr.MaximumLength, ALLOC_TAG);
     if (!fcb->adsxattr.Buffer) {
@@ -2133,9 +2337,9 @@ static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    RtlCopyMemory(fcb->adsxattr.Buffer, xapref, xapreflen);
+    RtlCopyMemory(fcb->adsxattr.Buffer, xapref, sizeof(xapref) - 1);
-    Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[xapreflen], utf8len,
&utf8len, stream->Buffer, stream->Length);
+    Status = RtlUnicodeToUTF8N(&fcb->adsxattr.Buffer[sizeof(xapref) - 1], utf8len,
&utf8len, stream->Buffer, stream->Length);
     if (!NT_SUCCESS(Status)) {
         ERR("RtlUnicodeToUTF8N 2 returned %08x\n", Status);
         free_fcb(Vcb, fcb);
@@ -2167,12 +2371,12 @@ 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 + xapreflen + overhead > fcb->adsmaxlen) {
-        WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len +
xapreflen, overhead, fcb->adsmaxlen);
+    if (utf8len + sizeof(xapref) - 1 + overhead > fcb->adsmaxlen) {
+        WARN("not enough room for new DIR_ITEM (%u + %u > %u)", utf8len +
sizeof(xapref) - 1, overhead, fcb->adsmaxlen);
         free_fcb(Vcb, fcb);
         return STATUS_DISK_FULL;
     } else
-        fcb->adsmaxlen -= overhead + utf8len + xapreflen;
+        fcb->adsmaxlen -= overhead + utf8len + sizeof(xapref) - 1;
     fileref = create_fileref(Vcb);
     if (!fileref) {
@@ -2192,7 +2396,7 @@ static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
     RtlZeroMemory(dc, sizeof(dir_child));
-    dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length - xapreflen;
+    dc->utf8.MaximumLength = dc->utf8.Length = fcb->adsxattr.Length + 1 -
sizeof(xapref);
     dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, dc->utf8.MaximumLength,
ALLOC_TAG);
     if (!dc->utf8.Buffer) {
         ERR("out of memory\n");
@@ -2201,7 +2405,7 @@ static NTSTATUS
create_stream(_Requires_lock_held_(_Curr_->tree_lock) _Requires_
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[xapreflen],
fcb->adsxattr.Length - xapreflen);
+    RtlCopyMemory(dc->utf8.Buffer, &fcb->adsxattr.Buffer[sizeof(xapref) - 1],
fcb->adsxattr.Length + 1 - sizeof(xapref));
     dc->name.MaximumLength = dc->name.Length = stream->Length;
     dc->name.Buffer = ExAllocatePoolWithTag(pool_type, dc->name.MaximumLength,
ALLOC_TAG);
@@ -2302,10 +2506,14 @@ static NTSTATUS file_create(PIRP Irp,
_Requires_lock_held_(_Curr_->tree_lock) _R
     file_ref *fileref, *parfileref = NULL;
     ULONG i, j;
     ccb* ccb;
-    static WCHAR datasuf[] =
{':','$','D','A','T','A',0};
+    static const WCHAR datasuf[] =
{':','$','D','A','T','A',0};
     UNICODE_STRING dsus, fpus, stream;
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     POOL_TYPE pool_type = IrpSp->Flags & SL_OPEN_PAGING_FILE ? NonPagedPool :
PagedPool;
+#ifndef __REACTOS__
+    ECP_LIST* ecp_list;
+    ATOMIC_CREATE_ECP_CONTEXT* acec = NULL;
+#endif
 #ifdef DEBUG_FCB_REFCOUNTS
     LONG oc;
 #endif
@@ -2318,8 +2526,27 @@ static NTSTATUS file_create(PIRP Irp,
_Requires_lock_held_(_Curr_->tree_lock) _R
     if (options & FILE_DELETE_ON_CLOSE &&
IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_READONLY)
         return STATUS_CANNOT_DELETE;
-    dsus.Buffer = datasuf;
-    dsus.Length = dsus.MaximumLength = (USHORT)wcslen(datasuf) * sizeof(WCHAR);
+#ifndef __REACTOS__
+    if (NT_SUCCESS(FsRtlGetEcpListFromIrp(Irp, &ecp_list)) && ecp_list) {
+        void* ctx = NULL;
+        GUID type;
+        ULONG ctxsize;
+
+        do {
+            Status = FsRtlGetNextExtraCreateParameter(ecp_list, ctx, &type, &ctx,
&ctxsize);
+
+            if (NT_SUCCESS(Status)) {
+                if (RtlCompareMemory(&type, &GUID_ECP_ATOMIC_CREATE,
sizeof(GUID)) == sizeof(GUID) && ctxsize >= sizeof(ATOMIC_CREATE_ECP_CONTEXT))
{
+                    acec = ctx;
+                    break;
+                }
+            }
+        } while (NT_SUCCESS(Status));
+    }
+#endif
+
+    dsus.Buffer = (WCHAR*)datasuf;
+    dsus.Length = dsus.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
     fpus.Buffer = NULL;
     if (!loaded_related) {
@@ -2451,6 +2678,15 @@ static NTSTATUS file_create(PIRP Irp,
_Requires_lock_held_(_Curr_->tree_lock) _R
     if (!ccb) {
         ERR("out of memory\n");
         Status = STATUS_INSUFFICIENT_RESOURCES;
+        fileref->deleted = TRUE;
+        fileref->fcb->deleted = TRUE;
+
+        if (stream.Length == 0) {
+            ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+            parfileref->fcb->inode_item.st_size -= fileref->dc->utf8.Length *
2;
+            ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+        }
+
         free_fileref(Vcb, fileref);
         goto end;
     }
@@ -2484,6 +2720,41 @@ static NTSTATUS file_create(PIRP Irp,
_Requires_lock_held_(_Curr_->tree_lock) _R
     FileObject->SectionObjectPointer =
&fileref->fcb->nonpaged->segment_object;
+#ifndef __REACTOS__
+    // FIXME - ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT
+    if (acec && acec->InFlags &
ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED) {
+        if (acec->ReparseBufferLength > sizeof(UINT32) &&
*(UINT32*)acec->ReparseBuffer == IO_REPARSE_TAG_SYMLINK) {
+            fileref->fcb->inode_item.st_mode &= ~(__S_IFIFO | __S_IFCHR |
__S_IFBLK | __S_IFSOCK);
+            fileref->fcb->type = BTRFS_TYPE_FILE;
+        }
+
+        if (fileref->fcb->type == BTRFS_TYPE_SOCKET || fileref->fcb->type ==
BTRFS_TYPE_FIFO ||
+            fileref->fcb->type == BTRFS_TYPE_CHARDEV || fileref->fcb->type ==
BTRFS_TYPE_BLOCKDEV) {
+            // NOP. If called from LXSS, humour it - we hardcode the values elsewhere.
+        } else {
+            Status = set_reparse_point2(fileref->fcb, acec->ReparseBuffer,
acec->ReparseBufferLength, NULL, NULL, Irp, rollback);
+            if (!NT_SUCCESS(Status)) {
+                ERR("set_reparse_point2 returned %08x\n", Status);
+                fileref->deleted = TRUE;
+                fileref->fcb->deleted = TRUE;
+
+                if (stream.Length == 0) {
+
ExAcquireResourceExclusiveLite(parfileref->fcb->Header.Resource, TRUE);
+                    parfileref->fcb->inode_item.st_size -=
fileref->dc->utf8.Length * 2;
+                    ExReleaseResourceLite(parfileref->fcb->Header.Resource);
+                }
+
+                free_fileref(Vcb, fileref);
+                return Status;
+            }
+        }
+
+        acec->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET;
+    }
+#endif
+
+    fileref->dc->type = fileref->fcb->type;
+
     goto end2;
 end:
@@ -2738,7 +3009,8 @@ static NTSTATUS get_reparse_block(fcb* fcb, UINT8** data) {
         }
         RtlCopyMemory(*data, fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length);
-    }
+    } else
+        return STATUS_INVALID_PARAMETER;
     return STATUS_SUCCESS;
 }
diff --git a/drivers/filesystems/btrfs/dirctrl.c b/drivers/filesystems/btrfs/dirctrl.c
index 64ab60f15a..4d8df559d0 100644
--- a/drivers/filesystems/btrfs/dirctrl.c
+++ b/drivers/filesystems/btrfs/dirctrl.c
@@ -31,16 +31,46 @@ typedef struct {
     dir_child* dc;
 } dir_entry;
+ULONG get_reparse_tag_fcb(fcb* fcb) {
+    ULONG tag;
+
+    if (fcb->type == BTRFS_TYPE_SYMLINK)
+        return IO_REPARSE_TAG_SYMLINK;
+    else if (fcb->type == BTRFS_TYPE_DIRECTORY) {
+        if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length <
sizeof(ULONG))
+            return 0;
+
+        RtlCopyMemory(&tag, fcb->reparse_xattr.Buffer, sizeof(ULONG));
+    } else {
+        NTSTATUS Status;
+        ULONG br;
+
+        Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL);
+        if (!NT_SUCCESS(Status)) {
+            ERR("read_file returned %08x\n", Status);
+            return 0;
+        }
+    }
+
+    return tag;
+}
+
 ULONG get_reparse_tag(device_extension* Vcb, root* subvol, UINT64 inode, UINT8 type,
ULONG atts, BOOL lxss, PIRP Irp) {
     fcb* fcb;
-    ULONG tag = 0, br;
+    ULONG tag = 0;
     NTSTATUS Status;
-    if (type == BTRFS_TYPE_SYMLINK) {
-        if (lxss)
-            return IO_REPARSE_TAG_LXSS_SYMLINK;
-        else
-            return IO_REPARSE_TAG_SYMLINK;
+    if (type == BTRFS_TYPE_SYMLINK)
+        return IO_REPARSE_TAG_SYMLINK;
+    else if (lxss) {
+        if (type == BTRFS_TYPE_SOCKET)
+            return IO_REPARSE_TAG_LXSS_SOCKET;
+        else if (type == BTRFS_TYPE_FIFO)
+            return IO_REPARSE_TAG_LXSS_FIFO;
+        else if (type == BTRFS_TYPE_CHARDEV)
+            return IO_REPARSE_TAG_LXSS_CHARDEV;
+        else if (type == BTRFS_TYPE_BLOCKDEV)
+            return IO_REPARSE_TAG_LXSS_BLOCKDEV;
     }
     if (type != BTRFS_TYPE_FILE && type != BTRFS_TYPE_DIRECTORY)
@@ -57,23 +87,8 @@ ULONG get_reparse_tag(device_extension* Vcb, root* subvol, UINT64
inode, UINT8 t
     ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
-    if (type == BTRFS_TYPE_DIRECTORY) {
-        if (!fcb->reparse_xattr.Buffer || fcb->reparse_xattr.Length <
sizeof(ULONG))
-            goto end;
-
-        RtlCopyMemory(&tag, fcb->reparse_xattr.Buffer, sizeof(ULONG));
-    } else {
-        Status = read_file(fcb, (UINT8*)&tag, 0, sizeof(ULONG), &br, NULL);
-        if (!NT_SUCCESS(Status)) {
-            ERR("read_file returned %08x\n", Status);
-            goto end;
-        }
-
-        if (br < sizeof(ULONG))
-            goto end;
-    }
+    tag = get_reparse_tag_fcb(fcb);
-end:
     ExReleaseResourceLite(fcb->Header.Resource);
     free_fcb(Vcb, fcb);
@@ -675,11 +690,7 @@ static NTSTATUS query_directory(PIRP Irp) {
     if (IrpSp->Parameters.QueryDirectory.FileName &&
IrpSp->Parameters.QueryDirectory.FileName->Length > 1) {
         TRACE("QD filename: %.*S\n",
IrpSp->Parameters.QueryDirectory.FileName->Length / sizeof(WCHAR),
IrpSp->Parameters.QueryDirectory.FileName->Buffer);
-#ifndef __REACTOS__
-        if (IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != '*') {
-#else
         if (IrpSp->Parameters.QueryDirectory.FileName->Length > sizeof(WCHAR) ||
IrpSp->Parameters.QueryDirectory.FileName->Buffer[0] != L'*') {
-#endif
             specific_file = TRUE;
             if
(FsRtlDoesNameContainWildCards(IrpSp->Parameters.QueryDirectory.FileName)) {
diff --git a/drivers/filesystems/btrfs/fastio.c b/drivers/filesystems/btrfs/fastio.c
index 9f4c504a12..9ee282d04c 100644
--- a/drivers/filesystems/btrfs/fastio.c
+++ b/drivers/filesystems/btrfs/fastio.c
@@ -296,11 +296,21 @@ static NTSTATUS fast_io_acquire_for_mod_write(PFILE_OBJECT
FileObject, PLARGE_IN
     if (!fcb)
         return STATUS_INVALID_PARAMETER;
-    *ResourceToRelease = fcb->Header.PagingIoResource;
+    // Make sure we don't get interrupted by the flush thread, which can cause a
deadlock
-    if (!ExAcquireResourceSharedLite(*ResourceToRelease, FALSE))
+    if (!ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, FALSE))
         return STATUS_CANT_WAIT;
+    // Ideally this would be PagingIoResource, but that doesn't play well with
copy-on-write,
+    // as we can't guarantee that we won't need to do any reallocations.
+
+    *ResourceToRelease = fcb->Header.Resource;
+
+    if (!ExAcquireResourceExclusiveLite(*ResourceToRelease, FALSE)) {
+        ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+        return STATUS_CANT_WAIT;
+    }
+
     return STATUS_SUCCESS;
 }
@@ -310,11 +320,16 @@ static NTSTATUS NTAPI fast_io_release_for_mod_write(PFILE_OBJECT
FileObject, str
 #else
 static NTSTATUS fast_io_release_for_mod_write(PFILE_OBJECT FileObject, struct _ERESOURCE
*ResourceToRelease, PDEVICE_OBJECT DeviceObject) {
 #endif
-    UNUSED(FileObject);
+    fcb* fcb;
+
     UNUSED(DeviceObject);
+    fcb = FileObject->FsContext;
+
     ExReleaseResourceLite(ResourceToRelease);
+    ExReleaseResourceLite(&fcb->Vcb->tree_lock);
+
     return STATUS_SUCCESS;
 }
diff --git a/drivers/filesystems/btrfs/fileinfo.c b/drivers/filesystems/btrfs/fileinfo.c
index 1d03a669f4..1c342b0f55 100644
--- a/drivers/filesystems/btrfs/fileinfo.c
+++ b/drivers/filesystems/btrfs/fileinfo.c
@@ -21,6 +21,34 @@
 // not currently in mingw - introduced with Windows 10
 #ifndef FileIdInformation
 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59
+#define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70
+
+typedef struct _FILE_STAT_LX_INFORMATION {
+    LARGE_INTEGER FileId;
+    LARGE_INTEGER CreationTime;
+    LARGE_INTEGER LastAccessTime;
+    LARGE_INTEGER LastWriteTime;
+    LARGE_INTEGER ChangeTime;
+    LARGE_INTEGER AllocationSize;
+    LARGE_INTEGER EndOfFile;
+    ULONG         FileAttributes;
+    ULONG         ReparseTag;
+    ULONG         NumberOfLinks;
+    ACCESS_MASK   EffectiveAccess;
+    ULONG         LxFlags;
+    ULONG         LxUid;
+    ULONG         LxGid;
+    ULONG         LxMode;
+    ULONG         LxDeviceIdMajor;
+    ULONG         LxDeviceIdMinor;
+} FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
+
+#define LX_FILE_METADATA_HAS_UID        0x01
+#define LX_FILE_METADATA_HAS_GID        0x02
+#define LX_FILE_METADATA_HAS_MODE       0x04
+#define LX_FILE_METADATA_HAS_DEVICE_ID  0x08
+#define LX_FILE_CASE_SENSITIVE_DIR      0x10
+
 #endif
 #endif
@@ -70,6 +98,20 @@ static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp,
PFILE_OBJ
         goto end;
     }
+    // times of -2 are some sort of undocumented behaviour to do with LXSS
+
+    if (fbi->CreationTime.QuadPart == -2)
+        fbi->CreationTime.QuadPart = 0;
+
+    if (fbi->LastAccessTime.QuadPart == -2)
+        fbi->LastAccessTime.QuadPart = 0;
+
+    if (fbi->LastWriteTime.QuadPart == -2)
+        fbi->LastWriteTime.QuadPart = 0;
+
+    if (fbi->ChangeTime.QuadPart == -2)
+        fbi->ChangeTime.QuadPart = 0;
+
     if (fbi->CreationTime.QuadPart == -1)
         ccb->user_set_creation_time = TRUE;
     else if (fbi->CreationTime.QuadPart != 0) {
@@ -2946,8 +2988,8 @@ static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION*
fni, fcb* f
     ULONG reqlen;
     UNICODE_STRING fn;
     NTSTATUS Status;
-    static WCHAR datasuf[] =
{':','$','D','A','T','A',0};
-    UINT16 datasuflen = (UINT16)wcslen(datasuf) * sizeof(WCHAR);
+    static const WCHAR datasuf[] =
{':','$','D','A','T','A',0};
+    UINT16 datasuflen = sizeof(datasuf) - sizeof(WCHAR);
     if (!fileref) {
         ERR("called without fileref\n");
@@ -2999,7 +3041,7 @@ static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION*
fni, fcb* f
     return Status;
 }
-static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati,
fcb* fcb, ccb* ccb, PIRP Irp, LONG* length) {
+static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati,
fcb* fcb, ccb* ccb, LONG* length) {
     *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
     if (fcb->ads) {
@@ -3015,7 +3057,7 @@ static NTSTATUS
fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATIO
     if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
         ati->ReparseTag = 0;
     else
-        ati->ReparseTag = get_reparse_tag(fcb->Vcb, fcb->subvol, fcb->inode,
fcb->type, fcb->atts, ccb->lxss, Irp);
+        ati->ReparseTag = get_reparse_tag_fcb(fcb);
     return STATUS_SUCCESS;
 }
@@ -3026,7 +3068,7 @@ static NTSTATUS
fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, fi
     FILE_STREAM_INFORMATION *entry, *lastentry;
     NTSTATUS Status;
-    static WCHAR datasuf[] = L":$DATA";
+    static const WCHAR datasuf[] = L":$DATA";
     UNICODE_STRING suf;
     if (!fileref) {
@@ -3034,8 +3076,8 @@ static NTSTATUS
fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, fi
         return STATUS_INVALID_PARAMETER;
     }
-    suf.Buffer = datasuf;
-    suf.Length = suf.MaximumLength = (UINT16)wcslen(datasuf) * sizeof(WCHAR);
+    suf.Buffer = (WCHAR*)datasuf;
+    suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR);
     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY)
         reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length +
sizeof(WCHAR);
@@ -3606,6 +3648,80 @@ static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION*
fii, fcb* fcb,
 }
 #endif
+#ifndef __REACTOS__
+static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb*
fcb, ccb* ccb, LONG* length) {
+    INODE_ITEM* ii;
+
+    fsli->FileId.LowPart = (UINT32)fcb->inode;
+    fsli->FileId.HighPart = (UINT32)fcb->subvol->id;
+
+    if (fcb->ads)
+        ii = &ccb->fileref->parent->fcb->inode_item;
+    else
+        ii = &fcb->inode_item;
+
+    if (fcb == fcb->Vcb->dummy_fcb) {
+        LARGE_INTEGER time;
+
+        KeQuerySystemTime(&time);
+        fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime =
fsli->ChangeTime = time;
+    } else {
+        fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime);
+        fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime);
+        fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime);
+        fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime);
+    }
+
+    if (fcb->ads) {
+        fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart =
fcb->adsdata.Length;
+        fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ?
FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts;
+    } else {
+        fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb);
+        fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 :
fcb->inode_item.st_size;
+        fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL :
fcb->atts;
+    }
+
+    if (fcb->type == BTRFS_TYPE_SOCKET)
+        fsli->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET;
+    else if (fcb->type == BTRFS_TYPE_FIFO)
+        fsli->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO;
+    else if (fcb->type == BTRFS_TYPE_CHARDEV)
+        fsli->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV;
+    else if (fcb->type == BTRFS_TYPE_BLOCKDEV)
+        fsli->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV;
+    else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+        fsli->ReparseTag = 0;
+    else
+        fsli->ReparseTag = get_reparse_tag_fcb(fcb);
+
+    if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO ||
fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV)
+        fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+    if (fcb->ads)
+        fsli->NumberOfLinks =
ccb->fileref->parent->fcb->inode_item.st_nlink;
+    else
+        fsli->NumberOfLinks = fcb->inode_item.st_nlink;
+
+    fsli->EffectiveAccess = ccb->access;
+    fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID |
LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID; // FIXME -
LX_FILE_CASE_SENSITIVE_DIR
+    fsli->LxUid = ii->st_uid;
+    fsli->LxGid = ii->st_gid;
+    fsli->LxMode = ii->st_mode;
+
+    if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) {
+        fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20;
+        fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF);
+    } else {
+        fsli->LxDeviceIdMajor = 0;
+        fsli->LxDeviceIdMinor = 0;
+    }
+
+    *length -= sizeof(FILE_STAT_LX_INFORMATION);
+
+    return STATUS_SUCCESS;
+}
+#endif
+
 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) {
     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
     LONG length = IrpSp->Parameters.QueryFile.Length;
@@ -3694,7 +3810,7 @@ static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT
FileObject, PIRP
             }
             ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
-            Status = fill_in_file_attribute_information(ati, fcb, ccb, Irp, &length);
+            Status = fill_in_file_attribute_information(ati, fcb, ccb, &length);
             ExReleaseResourceLite(&Vcb->tree_lock);
             break;
@@ -3893,6 +4009,23 @@ static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT
FileObject, PIRP
             break;
         }
+
+        case FileStatLxInformation:
+        {
+            FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer;
+
+            if (IrpSp->Parameters.QueryFile.Length <
sizeof(FILE_STAT_LX_INFORMATION)) {
+                WARN("overflow\n");
+                Status = STATUS_BUFFER_OVERFLOW;
+                goto exit;
+            }
+
+            TRACE("FileStatLxInformation\n");
+
+            Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length);
+
+            break;
+        }
 #ifndef _MSC_VER
 #pragma GCC diagnostic pop
 #endif
@@ -4194,13 +4327,6 @@ end:
     return Status;
 }
-typedef struct {
-    ANSI_STRING name;
-    ANSI_STRING value;
-    UCHAR flags;
-    LIST_ENTRY list_entry;
-} ea_item;
-
 _Dispatch_type_(IRP_MJ_SET_EA)
 _Function_class_(DRIVER_DISPATCH)
 NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
@@ -4386,6 +4512,64 @@ NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP
Irp) {
         le = le2;
     }
+    // handle LXSS values
+    le = ealist.Flink;
+    while (le != &ealist) {
+        LIST_ENTRY* le2 = le->Flink;
+
+        item = CONTAINING_RECORD(le, ea_item, list_entry);
+
+        if (item->name.Length == sizeof(lxuid) - 1 &&
RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) ==
item->name.Length) {
+            if (item->value.Length < sizeof(UINT32)) {
+                ERR("uid value was shorter than expected\n");
+                Status = STATUS_INVALID_PARAMETER;
+                goto end2;
+            }
+
+            if (Irp->RequestorMode == KernelMode) {
+                RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer,
sizeof(UINT32));
+                fcb->sd_dirty = TRUE;
+                fcb->sd_deleted = FALSE;
+            }
+
+            RemoveEntryList(&item->list_entry);
+            ExFreePool(item);
+        } else if (item->name.Length == sizeof(lxgid) - 1 &&
RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) ==
item->name.Length) {
+            if (item->value.Length < sizeof(UINT32)) {
+                ERR("gid value was shorter than expected\n");
+                Status = STATUS_INVALID_PARAMETER;
+                goto end2;
+            }
+
+            if (Irp->RequestorMode == KernelMode)
+                RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer,
sizeof(UINT32));
+
+            RemoveEntryList(&item->list_entry);
+            ExFreePool(item);
+        } else if (item->name.Length == sizeof(lxmod) - 1 &&
RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) ==
item->name.Length) {
+            if (item->value.Length < sizeof(UINT32)) {
+                ERR("mode value was shorter than expected\n");
+                Status = STATUS_INVALID_PARAMETER;
+                goto end2;
+            }
+
+            if (Irp->RequestorMode == KernelMode) {
+                UINT32 allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP |
S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID;
+                UINT32 val;
+
+                RtlCopyMemory(&val, item->value.Buffer, sizeof(UINT32));
+
+                fcb->inode_item.st_mode &= ~allowed;
+                fcb->inode_item.st_mode |= val & allowed;
+            }
+
+            RemoveEntryList(&item->list_entry);
+            ExFreePool(item);
+        }
+
+        le = le2;
+    }
+
     if (IsListEmpty(&ealist)) {
         fcb->ealen = 0;
diff --git a/drivers/filesystems/btrfs/flushthread.c
b/drivers/filesystems/btrfs/flushthread.c
index 9e95f82834..6353abb583 100644
--- a/drivers/filesystems/btrfs/flushthread.c
+++ b/drivers/filesystems/btrfs/flushthread.c
@@ -316,14 +316,14 @@ static void clean_space_cache(device_extension* Vcb) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (c->space_changed) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             if (c->space_changed)
                 clean_space_cache_chunk(Vcb, c);
             c->space_changed = FALSE;
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
         }
         le = le->Flink;
@@ -599,11 +599,11 @@ static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8
level, UINT64
         return FALSE;
     }
-    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+    acquire_chunk_lock(c, Vcb);
     space_list_subtract(c, FALSE, address, Vcb->superblock.node_size, rollback);
-    ExReleaseResourceLite(&c->lock);
+    release_chunk_lock(c, Vcb);
     add_parents_to_cache(insert_tp.tree);
@@ -731,11 +731,11 @@ static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level,
UINT64 root_i
         return FALSE;
     }
-    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+    acquire_chunk_lock(c, Vcb);
     space_list_subtract(c, FALSE, address, Vcb->superblock.node_size, rollback);
-    ExReleaseResourceLite(&c->lock);
+    release_chunk_lock(c, Vcb);
     add_parents_to_cache(insert_tp.tree);
@@ -773,11 +773,11 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP
Irp, LIST_ENT
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (!c->readonly && !c->reloc) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             if (c != origchunk && c->chunk_item->type == flags &&
(c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
                 if (insert_tree_extent(Vcb, t->header.level, t->root->id, c,
&addr, Irp, rollback)) {
-                    ExReleaseResourceLite(&c->lock);
+                    release_chunk_lock(c, Vcb);
                     ExReleaseResourceLite(&Vcb->chunk_lock);
                     t->new_address = addr;
                     t->has_new_address = TRUE;
@@ -785,7 +785,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP
Irp, LIST_ENT
                 }
             }
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
         }
         le = le->Flink;
@@ -801,11 +801,11 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP
Irp, LIST_ENT
         return Status;
     }
-    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+    acquire_chunk_lock(c, Vcb);
     if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
         if (insert_tree_extent(Vcb, t->header.level, t->root->id, c, &addr,
Irp, rollback)) {
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
             ExReleaseResourceLite(&Vcb->chunk_lock);
             t->new_address = addr;
             t->has_new_address = TRUE;
@@ -813,7 +813,7 @@ NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP
Irp, LIST_ENT
         }
     }
-    ExReleaseResourceLite(&c->lock);
+    release_chunk_lock(c, Vcb);
     ExReleaseResourceLite(&Vcb->chunk_lock);
@@ -849,14 +849,14 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64
address, tree*
         chunk* c = get_chunk_from_address(Vcb, address);
         if (c) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             if (!c->cache_loaded) {
                 Status = load_cache_chunk(Vcb, c, NULL);
                 if (!NT_SUCCESS(Status)) {
                     ERR("load_cache_chunk returned %08x\n", Status);
-                    ExReleaseResourceLite(&c->lock);
+                    release_chunk_lock(c, Vcb);
                     return Status;
                 }
             }
@@ -865,7 +865,7 @@ static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64
address, tree*
             space_list_add(c, address, Vcb->superblock.node_size, rollback);
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
         } else
             ERR("could not find chunk for address %llx\n", address);
     }
@@ -1351,19 +1351,19 @@ static NTSTATUS allocate_tree_extents(device_extension* Vcb, PIRP
Irp, LIST_ENTR
                 if (c) {
                     if (!c->cache_loaded) {
-                        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+                        acquire_chunk_lock(c, Vcb);
                         if (!c->cache_loaded) {
                             Status = load_cache_chunk(Vcb, c, NULL);
                             if (!NT_SUCCESS(Status)) {
                                 ERR("load_cache_chunk returned %08x\n",
Status);
-                                ExReleaseResourceLite(&c->lock);
+                                release_chunk_lock(c, Vcb);
                                 return Status;
                             }
                         }
-                        ExReleaseResourceLite(&c->lock);
+                        release_chunk_lock(c, Vcb);
                     }
                 }
             }
@@ -2644,14 +2644,14 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP
Irp, LIST_ENTRY*
     while (le != &Vcb->chunks) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, Vcb);
         if (!c->cache_loaded && (!IsListEmpty(&c->changed_extents) ||
c->used != c->oldused)) {
             Status = load_cache_chunk(Vcb, c, NULL);
             if (!NT_SUCCESS(Status)) {
                 ERR("load_cache_chunk returned %08x\n", Status);
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
         }
@@ -2664,7 +2664,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
             Status = flush_changed_extent(Vcb, c, ce, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("flush_changed_extent returned %08x\n", Status);
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
@@ -2677,7 +2677,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
             Status = create_chunk(Vcb, c, Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("create_chunk returned %08x\n", Status);
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
         }
@@ -2691,7 +2691,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
                 Status = flush_fcb(c->old_cache, FALSE, &batchlist, Irp);
                 if (!NT_SUCCESS(Status)) {
                     ERR("flush_fcb returned %08x\n", Status);
-                    ExReleaseResourceLite(&c->lock);
+                    release_chunk_lock(c, Vcb);
                     clear_batch_list(Vcb, &batchlist);
                     goto end;
                 }
@@ -2699,7 +2699,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
                 Status = commit_batch_list(Vcb, &batchlist, Irp);
                 if (!NT_SUCCESS(Status)) {
                     ERR("commit_batch_list returned %08x\n", Status);
-                    ExReleaseResourceLite(&c->lock);
+                    release_chunk_lock(c, Vcb);
                     goto end;
                 }
             }
@@ -2716,21 +2716,21 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP
Irp, LIST_ENTRY*
             Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE,
Irp);
             if (!NT_SUCCESS(Status)) {
                 ERR("error - find_item returned %08x\n", Status);
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
             if (keycmp(searchkey, tp.item->key)) {
                 ERR("could not find (%llx,%x,%llx) in extent_root\n",
searchkey.obj_id, searchkey.obj_type, searchkey.offset);
                 Status = STATUS_INTERNAL_ERROR;
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
             if (tp.item->size < sizeof(BLOCK_GROUP_ITEM)) {
                 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n",
tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
tp.item->size, sizeof(BLOCK_GROUP_ITEM));
                 Status = STATUS_INTERNAL_ERROR;
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
@@ -2738,7 +2738,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
             if (!bgi) {
                 ERR("out of memory\n");
                 Status = STATUS_INSUFFICIENT_RESOURCES;
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
@@ -2751,7 +2751,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_tree_item returned %08x\n", Status);
                 ExFreePool(bgi);
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
@@ -2759,7 +2759,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
             if (!NT_SUCCESS(Status)) {
                 ERR("insert_tree_item returned %08x\n", Status);
                 ExFreePool(bgi);
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 goto end;
             }
@@ -2772,7 +2772,7 @@ static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp,
LIST_ENTRY*
             c->oldused = c->used;
         }
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, Vcb);
         le = le->Flink;
     }
@@ -4414,14 +4414,14 @@ cont:
                 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
                     add_checksum_entry(fcb->Vcb, er->address,
(ULONG)(er->skip_start / fcb->Vcb->superblock.sector_size), NULL, NULL);
-                ExAcquireResourceExclusiveLite(&er->chunk->lock, TRUE);
+                acquire_chunk_lock(er->chunk, fcb->Vcb);
                 if (!er->chunk->cache_loaded) {
                     NTSTATUS Status = load_cache_chunk(fcb->Vcb, er->chunk, NULL);
                     if (!NT_SUCCESS(Status)) {
                         ERR("load_cache_chunk returned %08x\n", Status);
-                        ExReleaseResourceLite(&er->chunk->lock);
+                        release_chunk_lock(er->chunk, fcb->Vcb);
                         goto end;
                     }
                 }
@@ -4430,7 +4430,7 @@ cont:
                 space_list_add(er->chunk, er->address, er->skip_start, NULL);
-                ExReleaseResourceLite(&er->chunk->lock);
+                release_chunk_lock(er->chunk, fcb->Vcb);
                 er->address += er->skip_start;
                 er->length -= er->skip_start;
@@ -4468,14 +4468,14 @@ cont:
                 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM))
                     add_checksum_entry(fcb->Vcb, er->address + er->length -
er->skip_end, (ULONG)(er->skip_end / fcb->Vcb->superblock.sector_size), NULL,
NULL);
-                ExAcquireResourceExclusiveLite(&er->chunk->lock, TRUE);
+                acquire_chunk_lock(er->chunk, fcb->Vcb);
                 if (!er->chunk->cache_loaded) {
                     NTSTATUS Status = load_cache_chunk(fcb->Vcb, er->chunk, NULL);
                     if (!NT_SUCCESS(Status)) {
                         ERR("load_cache_chunk returned %08x\n", Status);
-                        ExReleaseResourceLite(&er->chunk->lock);
+                        release_chunk_lock(er->chunk, fcb->Vcb);
                         goto end;
                     }
                 }
@@ -4484,7 +4484,7 @@ cont:
                 space_list_add(er->chunk, er->address + er->length -
er->skip_end, er->skip_end, NULL);
-                ExReleaseResourceLite(&er->chunk->lock);
+                release_chunk_lock(er->chunk, fcb->Vcb);
                 er->length -= er->skip_end;
             }
@@ -4924,14 +4924,14 @@ NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist,
PIRP Irp) {
     if (fcb->sd_dirty) {
         if (!fcb->sd_deleted) {
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_NTACL, (UINT16)strlen(EA_NTACL),
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_NTACL, sizeof(EA_NTACL) - 1,
                                EA_NTACL_HASH, (UINT8*)fcb->sd,
(UINT16)RtlLengthSecurityDescriptor(fcb->sd));
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else {
-            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_NTACL, (UINT16)strlen(EA_NTACL), EA_NTACL_HASH);
+            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_NTACL, sizeof(EA_NTACL) - 1, EA_NTACL_HASH);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_xattr returned %08x\n", Status);
                 goto end;
@@ -4966,14 +4966,14 @@ NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist,
PIRP Irp) {
             val2--;
             *val2 = '0';
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_DOSATTRIB, (UINT16)strlen(EA_DOSATTRIB),
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1,
                                EA_DOSATTRIB_HASH, val2, (UINT16)(val + sizeof(val) -
val2));
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else {
-            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_DOSATTRIB, (UINT16)strlen(EA_DOSATTRIB), EA_DOSATTRIB_HASH);
+            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1, EA_DOSATTRIB_HASH);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_xattr returned %08x\n", Status);
                 goto end;
@@ -4986,14 +4986,14 @@ NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist,
PIRP Irp) {
     if (fcb->reparse_xattr_changed) {
         if (fcb->reparse_xattr.Buffer && fcb->reparse_xattr.Length > 0)
{
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_REPARSE, (UINT16)strlen(EA_REPARSE),
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_REPARSE, sizeof(EA_REPARSE) - 1,
                                EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer,
(UINT16)fcb->reparse_xattr.Length);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else {
-            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_REPARSE, (UINT16)strlen(EA_REPARSE), EA_REPARSE_HASH);
+            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_REPARSE, sizeof(EA_REPARSE) - 1, EA_REPARSE_HASH);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_xattr returned %08x\n", Status);
                 goto end;
@@ -5005,14 +5005,14 @@ NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist,
PIRP Irp) {
     if (fcb->ea_changed) {
         if (fcb->ea_xattr.Buffer && fcb->ea_xattr.Length > 0) {
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_EA, (UINT16)strlen(EA_EA),
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_EA, sizeof(EA_EA) - 1,
                                EA_EA_HASH, (UINT8*)fcb->ea_xattr.Buffer,
(UINT16)fcb->ea_xattr.Length);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else {
-            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_EA, (UINT16)strlen(EA_EA), EA_EA_HASH);
+            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_EA, sizeof(EA_EA) - 1, EA_EA_HASH);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_xattr returned %08x\n", Status);
                 goto end;
@@ -5024,25 +5024,34 @@ NTSTATUS flush_fcb(fcb* fcb, BOOL cache, LIST_ENTRY* batchlist,
PIRP Irp) {
     if (fcb->prop_compression_changed) {
         if (fcb->prop_compression == PropCompression_None) {
-            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_PROP_COMPRESSION, (UINT16)strlen(EA_PROP_COMPRESSION), EA_PROP_COMPRESSION_HASH);
+            Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1, EA_PROP_COMPRESSION_HASH);
             if (!NT_SUCCESS(Status)) {
                 ERR("delete_xattr returned %08x\n", Status);
                 goto end;
             }
         } else if (fcb->prop_compression == PropCompression_Zlib) {
-            const char zlib[] = "zlib";
+            static const char zlib[] = "zlib";
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_PROP_COMPRESSION, (UINT16)strlen(EA_PROP_COMPRESSION),
-                               EA_PROP_COMPRESSION_HASH, (UINT8*)zlib,
(UINT16)strlen(zlib));
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1,
+                               EA_PROP_COMPRESSION_HASH, (UINT8*)zlib, sizeof(zlib) - 1);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
             }
         } else if (fcb->prop_compression == PropCompression_LZO) {
-            const char lzo[] = "lzo";
+            static const char lzo[] = "lzo";
-            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_PROP_COMPRESSION, (UINT16)strlen(EA_PROP_COMPRESSION),
-                               EA_PROP_COMPRESSION_HASH, (UINT8*)lzo,
(UINT16)strlen(lzo));
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1,
+                               EA_PROP_COMPRESSION_HASH, (UINT8*)lzo, sizeof(lzo) - 1);
+            if (!NT_SUCCESS(Status)) {
+                ERR("set_xattr returned %08x\n", Status);
+                goto end;
+            }
+        } else if (fcb->prop_compression == PropCompression_ZSTD) {
+            static const char zstd[] = "zstd";
+
+            Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode,
EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1,
+                               EA_PROP_COMPRESSION_HASH, (UINT8*)zstd, sizeof(zstd) - 1);
             if (!NT_SUCCESS(Status)) {
                 ERR("set_xattr returned %08x\n", Status);
                 goto end;
@@ -5282,29 +5291,26 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c,
LIST_ENTRY* batchlis
                 return Status;
             }
-            if (keycmp(tp.item->key, searchkey)) {
-                ERR("error - could not find DEV_ITEM for device %llx\n",
searchkey.offset);
-                return STATUS_INTERNAL_ERROR;
-            }
-
-            Status = delete_tree_item(Vcb, &tp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("delete_tree_item returned %08x\n", Status);
-                return Status;
-            }
+            if (!keycmp(tp.item->key, searchkey)) {
+                Status = delete_tree_item(Vcb, &tp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("delete_tree_item returned %08x\n", Status);
+                    return Status;
+                }
-            di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
-            if (!di) {
-                ERR("out of memory\n");
-                return STATUS_INSUFFICIENT_RESOURCES;
-            }
+                di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
+                if (!di) {
+                    ERR("out of memory\n");
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
-            RtlCopyMemory(di, &c->devices[i]->devitem, sizeof(DEV_ITEM));
+                RtlCopyMemory(di, &c->devices[i]->devitem, sizeof(DEV_ITEM));
-            Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM,
c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
-            if (!NT_SUCCESS(Status)) {
-                ERR("insert_tree_item returned %08x\n", Status);
-                return Status;
+                Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM,
c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("insert_tree_item returned %08x\n", Status);
+                    return Status;
+                }
             }
             for (j = i + 1; j < c->chunk_item->num_stripes; j++) {
@@ -5404,6 +5410,8 @@ static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c,
LIST_ENTRY* batchlis
         ExFreePool(s);
     }
+    release_chunk_lock(c, Vcb);
+
     ExDeleteResourceLite(&c->partial_stripes_lock);
     ExDeleteResourceLite(&c->range_locks_lock);
     ExDeleteResourceLite(&c->lock);
@@ -5718,7 +5726,7 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY*
batchlist, PIRP
         le2 = le->Flink;
         if (c->changed) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             // flush partial stripes
             if (!Vcb->readonly && (c->chunk_item->type &
BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)) {
@@ -5737,7 +5745,7 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY*
batchlist, PIRP
                     if (!NT_SUCCESS(Status)) {
                         ERR("flush_partial_stripe returned %08x\n", Status);
                         ExReleaseResourceLite(&c->partial_stripes_lock);
-                        ExReleaseResourceLite(&c->lock);
+                        release_chunk_lock(c, Vcb);
                         ExReleaseResourceLite(&Vcb->chunk_lock);
                         return Status;
                     }
@@ -5747,12 +5755,14 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY*
batchlist, PIRP
             }
             if (c->list_entry_balance.Flink) {
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
                 le = le2;
                 continue;
             }
             if (c->space_changed || c->created) {
+                BOOL created = c->created;
+
                 used_minus_cache = c->used;
                 // subtract self-hosted cache
@@ -5781,23 +5791,28 @@ static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY*
batchlist, PIRP
                     Status = drop_chunk(Vcb, c, batchlist, Irp, rollback);
                     if (!NT_SUCCESS(Status)) {
                         ERR("drop_chunk returned %08x\n", Status);
-                        ExReleaseResourceLite(&c->lock);
+                        release_chunk_lock(c, Vcb);
                         ExReleaseResourceLite(&Vcb->chunk_lock);
                         return Status;
                     }
+
+                    // c is now freed, so avoid releasing non-existent lock
+                    le = le2;
+                    continue;
                 } else if (c->created) {
                     Status = create_chunk(Vcb, c, Irp);
                     if (!NT_SUCCESS(Status)) {
                         ERR("create_chunk returned %08x\n", Status);
-                        ExReleaseResourceLite(&c->lock);
+                        release_chunk_lock(c, Vcb);
                         ExReleaseResourceLite(&Vcb->chunk_lock);
                         return Status;
                     }
                 }
-                if (used_minus_cache > 0)
-                    ExReleaseResourceLite(&c->lock);
-            }
+                if (used_minus_cache > 0 || created)
+                    release_chunk_lock(c, Vcb);
+            } else
+                release_chunk_lock(c, Vcb);
         }
         le = le2;
diff --git a/drivers/filesystems/btrfs/free-space.c
b/drivers/filesystems/btrfs/free-space.c
index 1ed2da9361..f6248e9038 100644
--- a/drivers/filesystems/btrfs/free-space.c
+++ b/drivers/filesystems/btrfs/free-space.c
@@ -161,12 +161,12 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb, LIST_ENTRY*
batchlist, PI
             chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
             if (!c->cache_loaded) {
-                ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+                acquire_chunk_lock(c, Vcb);
                 Status = load_cache_chunk(Vcb, c, NULL);
                 if (!NT_SUCCESS(Status)) {
                     ERR("load_cache_chunk(%llx) returned %08x\n", c->offset,
Status);
-                    ExReleaseResourceLite(&c->lock);
+                    release_chunk_lock(c, Vcb);
                     ExReleaseResourceLite(&Vcb->chunk_lock);
                     return Status;
                 }
@@ -174,7 +174,7 @@ NTSTATUS clear_free_space_cache(device_extension* Vcb, LIST_ENTRY*
batchlist, PI
                 c->changed = TRUE;
                 c->space_changed = TRUE;
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
             }
             le = le->Flink;
@@ -975,14 +975,14 @@ static NTSTATUS insert_cache_extent(fcb* fcb, UINT64 start, UINT64
length, LIST_
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (!c->readonly && !c->reloc) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, fcb->Vcb);
             if (c->chunk_item->type == flags && (c->chunk_item->size
- c->used) >= length) {
                 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL,
NULL, rollback, BTRFS_COMPRESSION_NONE, length, FALSE, 0))
                     return STATUS_SUCCESS;
             }
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, fcb->Vcb);
         }
         le = le->Flink;
@@ -995,14 +995,14 @@ static NTSTATUS insert_cache_extent(fcb* fcb, UINT64 start, UINT64
length, LIST_
         return Status;
     }
-    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+    acquire_chunk_lock(c, fcb->Vcb);
     if (c->chunk_item->type == flags && (c->chunk_item->size -
c->used) >= length) {
         if (insert_extent_chunk(fcb->Vcb, fcb, c, start, length, FALSE, NULL, NULL,
rollback, BTRFS_COMPRESSION_NONE, length, FALSE, 0))
             return STATUS_SUCCESS;
     }
-    ExReleaseResourceLite(&c->lock);
+    release_chunk_lock(c, fcb->Vcb);
     return STATUS_DISK_FULL;
 }
@@ -1360,9 +1360,9 @@ NTSTATUS allocate_cache(device_extension* Vcb, BOOL* changed, PIRP
Irp, LIST_ENT
         if (c->space_changed) {
             BOOL b;
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             Status = allocate_cache_chunk(Vcb, c, &b, &batchlist, Irp, rollback);
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
             if (b)
                 *changed = TRUE;
@@ -1828,9 +1828,9 @@ NTSTATUS update_chunk_caches(device_extension* Vcb, PIRP Irp,
LIST_ENTRY* rollba
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (c->space_changed) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             Status = update_chunk_cache(Vcb, c, &now, &batchlist, Irp, rollback);
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
             if (!NT_SUCCESS(Status)) {
                 ERR("update_chunk_cache(%llx) returned %08x\n", c->offset,
Status);
@@ -1897,9 +1897,9 @@ NTSTATUS update_chunk_caches_tree(device_extension* Vcb, PIRP Irp) {
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (c->space_changed) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             Status = update_chunk_cache_tree(Vcb, c, &batchlist);
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
             if (!NT_SUCCESS(Status)) {
                 ERR("update_chunk_cache_tree(%llx) returned %08x\n",
c->offset, Status);
diff --git a/drivers/filesystems/btrfs/fsctl.c b/drivers/filesystems/btrfs/fsctl.c
index 6cc0f45ab1..3b13e89766 100644
--- a/drivers/filesystems/btrfs/fsctl.c
+++ b/drivers/filesystems/btrfs/fsctl.c
@@ -1024,7 +1024,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT
FileObject, vo
     // add INODE_REF
-    irsize = (UINT16)(offsetof(INODE_REF, name[0]) + strlen(DOTDOT));
+    irsize = (UINT16)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1);
     ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG);
     if (!ir) {
         ERR("out of memory\n");
@@ -1033,7 +1033,7 @@ static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT
FileObject, vo
     }
     ir->index = 0;
-    ir->n = (USHORT)strlen(DOTDOT);
+    ir->n = sizeof(DOTDOT) - 1;
     RtlCopyMemory(ir->name, DOTDOT, ir->n);
     Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF,
r->root_item.objid, ir, irsize, NULL, Irp);
@@ -1164,9 +1164,10 @@ end2:
 }
 static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) {
-    btrfs_inode_info* bii = data;
+    btrfs_inode_info2* bii = data;
     fcb* fcb;
     ccb* ccb;
+    BOOL old_style;
     if (length < sizeof(btrfs_inode_info))
         return STATUS_BUFFER_OVERFLOW;
@@ -1192,6 +1193,8 @@ static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data,
ULONG length
     if (fcb->ads)
         fcb = ccb->fileref->parent->fcb;
+    old_style = length < sizeof(btrfs_inode_info2);
+
     ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE);
     bii->subvol = fcb->subvol->id;
@@ -1210,52 +1213,84 @@ static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void*
data, ULONG length
     bii->flags = fcb->inode_item.flags;
     bii->inline_length = 0;
-    bii->disk_size[0] = 0;
-    bii->disk_size[1] = 0;
-    bii->disk_size[2] = 0;
+    bii->disk_size_uncompressed = 0;
+    bii->disk_size_zlib = 0;
+    bii->disk_size_lzo = 0;
+
+    if (!old_style) {
+        bii->disk_size_zstd = 0;
+        bii->sparse_size = 0;
+    }
     if (fcb->type != BTRFS_TYPE_DIRECTORY) {
+        UINT64 last_end = 0;
         LIST_ENTRY* le;
+        BOOL extents_inline = FALSE;
         le = fcb->extents.Flink;
         while (le != &fcb->extents) {
             extent* ext = CONTAINING_RECORD(le, extent, list_entry);
             if (!ext->ignore) {
+                if (!old_style && ext->offset > last_end)
+                    bii->sparse_size += ext->offset - last_end;
+
                 if (ext->extent_data.type == EXTENT_TYPE_INLINE) {
                     bii->inline_length += ext->datalen -
(UINT16)offsetof(EXTENT_DATA, data[0]);
+                    last_end = ext->offset + ext->extent_data.decoded_size;
+                    extents_inline = TRUE;
                 } else {
                     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data;
                     // FIXME - compressed extents with a hole in them are counted more
than once
                     if (ed2->size != 0) {
-                        if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) {
-                            bii->disk_size[0] += ed2->num_bytes;
-                        } else if (ext->extent_data.compression ==
BTRFS_COMPRESSION_ZLIB) {
-                            bii->disk_size[1] += ed2->size;
-                        } else if (ext->extent_data.compression ==
BTRFS_COMPRESSION_LZO) {
-                            bii->disk_size[2] += ed2->size;
+                        switch (ext->extent_data.compression) {
+                            case BTRFS_COMPRESSION_NONE:
+                                bii->disk_size_uncompressed += ed2->num_bytes;
+                                break;
+
+                            case BTRFS_COMPRESSION_ZLIB:
+                                bii->disk_size_zlib += ed2->size;
+                                break;
+
+                            case BTRFS_COMPRESSION_LZO:
+                                bii->disk_size_lzo += ed2->size;
+                                break;
+
+                            case BTRFS_COMPRESSION_ZSTD:
+                                if (!old_style)
+                                    bii->disk_size_zstd += ed2->size;
+                                break;
                         }
                     }
+
+                    last_end = ext->offset + ed2->num_bytes;
                 }
             }
             le = le->Flink;
         }
+
+        if (!extents_inline && !old_style &&
sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) >
last_end)
+            bii->sparse_size += sector_align(fcb->inode_item.st_size,
fcb->Vcb->superblock.sector_size) - last_end;
     }
     switch (fcb->prop_compression) {
         case PropCompression_Zlib:
             bii->compression_type = BTRFS_COMPRESSION_ZLIB;
-        break;
+            break;
         case PropCompression_LZO:
             bii->compression_type = BTRFS_COMPRESSION_LZO;
-        break;
+            break;
+
+        case PropCompression_ZSTD:
+            bii->compression_type = BTRFS_COMPRESSION_ZSTD;
+            break;
         default:
             bii->compression_type = BTRFS_COMPRESSION_ANY;
-        break;
+            break;
     }
     ExReleaseResourceLite(fcb->Header.Resource);
@@ -1295,7 +1330,7 @@ static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data,
ULONG length
         return STATUS_ACCESS_DENIED;
     }
-    if (bsii->compression_type_changed && bsii->compression_type >
BTRFS_COMPRESSION_LZO)
+    if (bsii->compression_type_changed && bsii->compression_type >
BTRFS_COMPRESSION_ZSTD)
         return STATUS_INVALID_PARAMETER;
     if (fcb->ads)
@@ -1358,6 +1393,10 @@ static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data,
ULONG length
             case BTRFS_COMPRESSION_LZO:
                 fcb->prop_compression = PropCompression_LZO;
             break;
+
+            case BTRFS_COMPRESSION_ZSTD:
+                fcb->prop_compression = PropCompression_ZSTD;
+            break;
         }
         fcb->prop_compression_changed = TRUE;
@@ -2351,10 +2390,6 @@ static NTSTATUS invalidate_volumes(PIRP Irp) {
                 RtlZeroMemory(newvpb, sizeof(VPB));
-                IoAcquireVpbSpinLock(&irql);
-                devobj->Vpb->Flags &= ~VPB_MOUNTED;
-                IoReleaseVpbSpinLock(irql);
-
                 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
                 Vcb->removing = TRUE;
@@ -2403,7 +2438,7 @@ static NTSTATUS invalidate_volumes(PIRP Irp) {
                     ExFreePool(newvpb);
                 if (Vcb->open_files == 0)
-                    uninit(Vcb, FALSE);
+                    uninit(Vcb);
             }
             break;
@@ -2517,7 +2552,6 @@ static void update_volumes(device_extension* Vcb) {
 static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
     NTSTATUS Status;
-    KIRQL irql;
     TRACE("FSCTL_DISMOUNT_VOLUME\n");
@@ -2558,11 +2592,6 @@ static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) {
     ExReleaseResourceLite(&Vcb->tree_lock);
-    IoAcquireVpbSpinLock(&irql);
-    Vcb->Vpb->Flags &= ~VPB_MOUNTED;
-    Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
-    IoReleaseVpbSpinLock(irql);
-
     return STATUS_SUCCESS;
 }
@@ -3631,10 +3660,6 @@ end:
     return Status;
 }
-// based on functions in sys/sysmacros.h
-#define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((UINT32)((rdev) >> 32)
& ~0xFFF))
-#define minor(rdev) (((rdev) & 0xFF) | ((UINT32)((rdev) >> 12) & ~0xFF))
-
 static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG
datalen, PIRP Irp) {
     NTSTATUS Status;
     btrfs_mknod* bmn;
@@ -4161,7 +4186,7 @@ static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT
FileObject,
     ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
-    if (bsxa->namelen == strlen(EA_NTACL) && RtlCompareMemory(bsxa->data,
EA_NTACL, strlen(EA_NTACL)) == strlen(EA_NTACL)) {
+    if (bsxa->namelen == sizeof(EA_NTACL) - 1 &&
RtlCompareMemory(bsxa->data, EA_NTACL, sizeof(EA_NTACL) - 1) == sizeof(EA_NTACL) - 1) {
         if ((!(ccb->access & WRITE_DAC) || !(ccb->access & WRITE_OWNER))
&& Irp->RequestorMode == UserMode) {
             WARN("insufficient privileges\n");
             Status = STATUS_ACCESS_DENIED;
@@ -4199,7 +4224,7 @@ static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT
FileObject,
         Status = STATUS_SUCCESS;
         goto end;
-    } else if (bsxa->namelen == strlen(EA_DOSATTRIB) &&
RtlCompareMemory(bsxa->data, EA_DOSATTRIB, strlen(EA_DOSATTRIB)) ==
strlen(EA_DOSATTRIB)) {
+    } else if (bsxa->namelen == sizeof(EA_DOSATTRIB) - 1 &&
RtlCompareMemory(bsxa->data, EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1) ==
sizeof(EA_DOSATTRIB) - 1) {
         ULONG atts;
         if (bsxa->valuelen > 0 &&
get_file_attributes_from_xattr(bsxa->data + bsxa->namelen, bsxa->valuelen,
&atts)) {
@@ -4230,7 +4255,7 @@ static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT
FileObject,
         Status = STATUS_SUCCESS;
         goto end;
-    } else if (bsxa->namelen == strlen(EA_REPARSE) &&
RtlCompareMemory(bsxa->data, EA_REPARSE, strlen(EA_REPARSE)) == strlen(EA_REPARSE)) {
+    } else if (bsxa->namelen == sizeof(EA_REPARSE) - 1 &&
RtlCompareMemory(bsxa->data, EA_REPARSE, sizeof(EA_REPARSE) - 1) == sizeof(EA_REPARSE)
- 1) {
         if (fcb->reparse_xattr.Buffer) {
             ExFreePool(fcb->reparse_xattr.Buffer);
             fcb->reparse_xattr.Buffer = NULL;
@@ -4254,7 +4279,7 @@ static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT
FileObject,
         Status = STATUS_SUCCESS;
         goto end;
-    } else if (bsxa->namelen == strlen(EA_EA) &&
RtlCompareMemory(bsxa->data, EA_EA, strlen(EA_EA)) == strlen(EA_EA)) {
+    } else if (bsxa->namelen == sizeof(EA_EA) - 1 &&
RtlCompareMemory(bsxa->data, EA_EA, sizeof(EA_EA) - 1) == sizeof(EA_EA) - 1) {
         if (!(ccb->access & FILE_WRITE_EA) && Irp->RequestorMode ==
UserMode) {
             WARN("insufficient privileges\n");
             Status = STATUS_ACCESS_DENIED;
@@ -4310,13 +4335,16 @@ static NTSTATUS fsctl_set_xattr(device_extension* Vcb,
PFILE_OBJECT FileObject,
         Status = STATUS_SUCCESS;
         goto end;
-    } else if (bsxa->namelen == strlen(EA_PROP_COMPRESSION) &&
RtlCompareMemory(bsxa->data, EA_PROP_COMPRESSION, strlen(EA_PROP_COMPRESSION)) ==
strlen(EA_PROP_COMPRESSION)) {
-        const char lzo[] = "lzo";
-        const char zlib[] = "zlib";
-
-        if (bsxa->valuelen == strlen(lzo) && RtlCompareMemory(bsxa->data +
bsxa->namelen, lzo, bsxa->valuelen) == bsxa->valuelen)
+    } else if (bsxa->namelen == sizeof(EA_PROP_COMPRESSION) - 1 &&
RtlCompareMemory(bsxa->data, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1) ==
sizeof(EA_PROP_COMPRESSION) - 1) {
+        static const char lzo[] = "lzo";
+        static const char zlib[] = "zlib";
+        static const char zstd[] = "zstd";
+
+        if (bsxa->valuelen == sizeof(zstd) - 1 &&
RtlCompareMemory(bsxa->data + bsxa->namelen, zstd, bsxa->valuelen) ==
bsxa->valuelen)
+            fcb->prop_compression = PropCompression_ZSTD;
+        else if (bsxa->valuelen == sizeof(lzo) - 1 &&
RtlCompareMemory(bsxa->data + bsxa->namelen, lzo, bsxa->valuelen) ==
bsxa->valuelen)
             fcb->prop_compression = PropCompression_LZO;
-        else if (bsxa->valuelen == strlen(zlib) &&
RtlCompareMemory(bsxa->data + bsxa->namelen, zlib, bsxa->valuelen) ==
bsxa->valuelen)
+        else if (bsxa->valuelen == sizeof(zlib) - 1 &&
RtlCompareMemory(bsxa->data + bsxa->namelen, zlib, bsxa->valuelen) ==
bsxa->valuelen)
             fcb->prop_compression = PropCompression_Zlib;
         else
             fcb->prop_compression = PropCompression_None;
@@ -4331,7 +4359,7 @@ static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT
FileObject,
         Status = STATUS_SUCCESS;
         goto end;
-    } else if (bsxa->namelen >= strlen(stream_pref) &&
RtlCompareMemory(bsxa->data, stream_pref, strlen(stream_pref)) == strlen(stream_pref))
{
+    } else if (bsxa->namelen >= (sizeof(stream_pref) - 1) &&
RtlCompareMemory(bsxa->data, stream_pref, sizeof(stream_pref) - 1) ==
sizeof(stream_pref) - 1) {
         // don't allow xattrs beginning with user., as these appear as streams
instead
         Status = STATUS_OBJECT_NAME_INVALID;
         goto end;
diff --git a/drivers/filesystems/btrfs/pnp.c b/drivers/filesystems/btrfs/pnp.c
index 5a7e91b410..fbb535f070 100644
--- a/drivers/filesystems/btrfs/pnp.c
+++ b/drivers/filesystems/btrfs/pnp.c
@@ -247,12 +247,10 @@ static NTSTATUS pnp_remove_device(PDEVICE_OBJECT DeviceObject) {
         ExAcquireResourceExclusiveLite(&Vcb->tree_lock, TRUE);
         Vcb->removing = TRUE;
-        Vcb->Vpb->Flags &= ~VPB_MOUNTED;
-        Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
         ExReleaseResourceLite(&Vcb->tree_lock);
         if (Vcb->open_files == 0)
-            uninit(Vcb, FALSE);
+            uninit(Vcb);
     }
     return STATUS_SUCCESS;
@@ -270,13 +268,11 @@ NTSTATUS pnp_surprise_removal(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
             Vcb->vde->mounted_device = NULL;
         Vcb->removing = TRUE;
-        Vcb->Vpb->Flags &= ~VPB_MOUNTED;
-        Vcb->Vpb->Flags |= VPB_DIRECT_WRITES_ALLOWED;
         ExReleaseResourceLite(&Vcb->tree_lock);
         if (Vcb->open_files == 0)
-            uninit(Vcb, FALSE);
+            uninit(Vcb);
     }
     return STATUS_SUCCESS;
@@ -349,7 +345,7 @@ end:
 static NTSTATUS bus_query_hardware_ids(PIRP Irp) {
     WCHAR* out;
-    static WCHAR ids[] = L"ROOT\\btrfs\0";
+    static const WCHAR ids[] = L"ROOT\\btrfs\0";
     out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
     if (!out) {
@@ -402,11 +398,11 @@ static NTSTATUS pdo_query_device_id(pdo_device_extension* pdode,
PIRP Irp) {
     WCHAR name[100], *noff, *out;
     int i;
-    static WCHAR pref[] = L"Btrfs\\";
+    static const WCHAR pref[] = L"Btrfs\\";
-    RtlCopyMemory(name, pref, wcslen(pref) * sizeof(WCHAR));
+    RtlCopyMemory(name, pref, sizeof(pref) - sizeof(WCHAR));
-    noff = &name[wcslen(pref)];
+    noff = &name[(sizeof(pref) / sizeof(WCHAR)) - 1];
     for (i = 0; i < 16; i++) {
         *noff = hex_digit(pdode->uuid.uuid[i] >> 4); noff++;
         *noff = hex_digit(pdode->uuid.uuid[i] & 0xf); noff++;
@@ -434,7 +430,7 @@ static NTSTATUS pdo_query_device_id(pdo_device_extension* pdode, PIRP
Irp) {
 static NTSTATUS pdo_query_hardware_ids(PIRP Irp) {
     WCHAR* out;
-    static WCHAR ids[] = L"BtrfsVolume\0";
+    static const WCHAR ids[] = L"BtrfsVolume\0";
     out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG);
     if (!out) {
diff --git a/drivers/filesystems/btrfs/read.c b/drivers/filesystems/btrfs/read.c
index 4384d65784..2eb19b1a80 100644
--- a/drivers/filesystems/btrfs/read.c
+++ b/drivers/filesystems/btrfs/read.c
@@ -1645,6 +1645,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
                 if (!context.stripes[i].mdl) {
                     ERR("IoAllocateMdl failed\n");
+                    MmUnlockPages(master_mdl);
+                    IoFreeMdl(master_mdl);
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto exit;
                 }
@@ -1654,6 +1656,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
         stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) *
ci->num_stripes, ALLOC_TAG);
         if (!stripeoff) {
             ERR("out of memory\n");
+            MmUnlockPages(master_mdl);
+            IoFreeMdl(master_mdl);
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto exit;
         }
@@ -1760,6 +1764,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
         stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(read_data_stripe*) *
ci->num_stripes / ci->sub_stripes, ALLOC_TAG);
         if (!stripes) {
             ERR("out of memory\n");
+            MmUnlockPages(master_mdl);
+            IoFreeMdl(master_mdl);
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto exit;
         }
@@ -1795,6 +1801,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
                         if (!context.stripes[i+j].mdl) {
                             ERR("IoAllocateMdl failed\n");
+                            MmUnlockPages(master_mdl);
+                            IoFreeMdl(master_mdl);
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             goto exit;
                         }
@@ -1818,6 +1826,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
                             if (!context.stripes[i+j].mdl) {
                                 ERR("IoAllocateMdl failed\n");
+                                MmUnlockPages(master_mdl);
+                                IoFreeMdl(master_mdl);
                                 Status = STATUS_INSUFFICIENT_RESOURCES;
                                 goto exit;
                             }
@@ -1839,6 +1849,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
         stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) *
ci->num_stripes / ci->sub_stripes, ALLOC_TAG);
         if (!stripeoff) {
             ERR("out of memory\n");
+            MmUnlockPages(master_mdl);
+            IoFreeMdl(master_mdl);
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto exit;
         }
@@ -2075,6 +2087,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
                 if (!context.stripes[i].mdl) {
                     ERR("IoAllocateMdl failed\n");
+                    MmUnlockPages(master_mdl);
+                    IoFreeMdl(master_mdl);
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto exit;
                 }
@@ -2085,6 +2099,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
             dummypage = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, ALLOC_TAG);
             if (!dummypage) {
                 ERR("out of memory\n");
+                MmUnlockPages(master_mdl);
+                IoFreeMdl(master_mdl);
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 goto exit;
             }
@@ -2092,6 +2108,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
             dummy_mdl = IoAllocateMdl(dummypage, PAGE_SIZE, FALSE, FALSE, NULL);
             if (!dummy_mdl) {
                 ERR("IoAllocateMdl failed\n");
+                MmUnlockPages(master_mdl);
+                IoFreeMdl(master_mdl);
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 goto exit;
             }
@@ -2104,6 +2122,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
         stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) *
ci->num_stripes, ALLOC_TAG);
         if (!stripeoff) {
             ERR("out of memory\n");
+            MmUnlockPages(master_mdl);
+            IoFreeMdl(master_mdl);
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto exit;
         }
@@ -2328,6 +2348,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
                 if (!context.stripes[i].mdl) {
                     ERR("IoAllocateMdl failed\n");
+                    MmUnlockPages(master_mdl);
+                    IoFreeMdl(master_mdl);
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto exit;
                 }
@@ -2338,6 +2360,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
             dummypage = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, ALLOC_TAG);
             if (!dummypage) {
                 ERR("out of memory\n");
+                MmUnlockPages(master_mdl);
+                IoFreeMdl(master_mdl);
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 goto exit;
             }
@@ -2345,6 +2369,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
             dummy_mdl = IoAllocateMdl(dummypage, PAGE_SIZE, FALSE, FALSE, NULL);
             if (!dummy_mdl) {
                 ERR("IoAllocateMdl failed\n");
+                MmUnlockPages(master_mdl);
+                IoFreeMdl(master_mdl);
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 goto exit;
             }
@@ -2357,6 +2383,8 @@ NTSTATUS read_data(_In_ device_extension* Vcb, _In_ UINT64 addr,
_In_ UINT32 len
         stripeoff = ExAllocatePoolWithTag(NonPagedPool, sizeof(UINT32) *
ci->num_stripes, ALLOC_TAG);
         if (!stripeoff) {
             ERR("out of memory\n");
+            MmUnlockPages(master_mdl);
+            IoFreeMdl(master_mdl);
             Status = STATUS_INSUFFICIENT_RESOURCES;
             goto exit;
         }
@@ -2785,7 +2813,7 @@ NTSTATUS read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64
length, ULONG* pb
                         read = (UINT32)min(min(len, ext->datalen) - off, length);
                         RtlCopyMemory(data + bytes_read, &ed->data[off], read);
-                    } else if (ed->compression == BTRFS_COMPRESSION_ZLIB ||
ed->compression == BTRFS_COMPRESSION_LZO) {
+                    } else if (ed->compression == BTRFS_COMPRESSION_ZLIB ||
ed->compression == BTRFS_COMPRESSION_LZO || ed->compression ==
BTRFS_COMPRESSION_ZSTD) {
                         UINT8* decomp;
                         BOOL decomp_alloc;
                         UINT16 inlen = ext->datalen - (UINT16)offsetof(EXTENT_DATA,
data[0]);
@@ -2834,6 +2862,13 @@ NTSTATUS read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64
length, ULONG* pb
                                 if (decomp_alloc) ExFreePool(decomp);
                                 goto exit;
                             }
+                        } else if (ed->compression == BTRFS_COMPRESSION_ZSTD) {
+                            Status = zstd_decompress(ed->data, inlen, decomp,
(UINT32)(read + off));
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("zstd_decompress returned %08x\n", Status);
+                                if (decomp_alloc) ExFreePool(decomp);
+                                goto exit;
+                            }
                         }
                         if (decomp_alloc) {
@@ -3000,6 +3035,18 @@ NTSTATUS read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64
length, ULONG* pb
                                 ERR("lzo_decompress returned %08x\n", Status);
                                 ExFreePool(buf);
+                                if (decomp)
+                                    ExFreePool(decomp);
+
+                                goto exit;
+                            }
+                        } else if (ed->compression == BTRFS_COMPRESSION_ZSTD) {
+                            Status = zstd_decompress(buf2, inlen, decomp ? decomp : (data
+ bytes_read), outlen);
+
+                            if (!NT_SUCCESS(Status)) {
+                                ERR("zstd_decompress returned %08x\n", Status);
+                                ExFreePool(buf);
+
                                 if (decomp)
                                     ExFreePool(decomp);
diff --git a/drivers/filesystems/btrfs/registry.c b/drivers/filesystems/btrfs/registry.c
index 05f66f5df2..84196df002 100644
--- a/drivers/filesystems/btrfs/registry.c
+++ b/drivers/filesystems/btrfs/registry.c
@@ -16,6 +16,7 @@
  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
 #include "btrfs_drv.h"
+#include "zstd/zstd.h"
 extern UNICODE_STRING log_device, log_file, registry_path;
 extern LIST_ENTRY uid_map_list, gid_map_list;
@@ -30,13 +31,13 @@ extern PDEVICE_OBJECT comdo;
 WORK_QUEUE_ITEM wqi;
-static WCHAR option_mounted[] = L"Mounted";
+static const WCHAR option_mounted[] = L"Mounted";
 NTSTATUS registry_load_volume_options(device_extension* Vcb) {
     BTRFS_UUID* uuid = &Vcb->superblock.uuid;
     mount_options* options = &Vcb->options;
     UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus,
readonlyus, zliblevelus, flushintervalus,
-                   maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus,
clearcacheus, allowdegradedus;
+                   maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus,
clearcacheus, allowdegradedus, zstdlevelus;
     OBJECT_ATTRIBUTES oa;
     NTSTATUS Status;
     ULONG i, j, kvfilen, index, retlen;
@@ -45,9 +46,10 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
     options->compress = mount_compress;
     options->compress_force = mount_compress_force;
-    options->compress_type = mount_compress_type > BTRFS_COMPRESSION_LZO ? 0 :
mount_compress_type;
+    options->compress_type = mount_compress_type > BTRFS_COMPRESSION_ZSTD ? 0 :
mount_compress_type;
     options->readonly = mount_readonly;
     options->zlib_level = mount_zlib_level;
+    options->zstd_level = mount_zstd_level;
     options->flush_interval = mount_flush_interval;
     options->max_inline = min(mount_max_inline, Vcb->superblock.node_size -
sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1);
     options->skip_balance = mount_skip_balance;
@@ -118,6 +120,7 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
     RtlInitUnicodeString(¬rimus, L"NoTrim");
     RtlInitUnicodeString(&clearcacheus, L"ClearCache");
     RtlInitUnicodeString(&allowdegradedus, L"AllowDegraded");
+    RtlInitUnicodeString(&zstdlevelus, L"ZstdLevel");
     do {
         Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen,
&retlen);
@@ -145,7 +148,7 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
             } else if (FsRtlAreNamesEqual(&compresstypeus, &us, TRUE, NULL)
&& kvfi->DataOffset > 0 && kvfi->DataLength > 0 &&
kvfi->Type == REG_DWORD) {
                 DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
-                options->compress_type = (UINT8)(*val > BTRFS_COMPRESSION_LZO ? 0 :
*val);
+                options->compress_type = (UINT8)(*val > BTRFS_COMPRESSION_ZSTD ? 0
: *val);
             } else if (FsRtlAreNamesEqual(&readonlyus, &us, TRUE, NULL)
&& kvfi->DataOffset > 0 && kvfi->DataLength > 0 &&
kvfi->Type == REG_DWORD) {
                 DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
@@ -186,6 +189,10 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
                 DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
                 options->allow_degraded = *val;
+            } else if (FsRtlAreNamesEqual(&zstdlevelus, &us, TRUE, NULL)
&& kvfi->DataOffset > 0 && kvfi->DataLength > 0 &&
kvfi->Type == REG_DWORD) {
+                DWORD* val = (DWORD*)((UINT8*)kvfi + kvfi->DataOffset);
+
+                options->zstd_level = *val;
             }
         } else if (Status != STATUS_NO_MORE_ENTRIES) {
             ERR("ZwEnumerateValueKey returned %08x\n", Status);
@@ -199,6 +206,9 @@ NTSTATUS registry_load_volume_options(device_extension* Vcb) {
     if (options->zlib_level > 9)
         options->zlib_level = 9;
+    if (options->zstd_level > (UINT32)ZSTD_maxCLevel())
+        options->zstd_level = ZSTD_maxCLevel();
+
     if (options->flush_interval == 0)
         options->flush_interval = mount_flush_interval;
@@ -258,8 +268,8 @@ NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid) {
         goto end;
     }
-    mountedus.Buffer = option_mounted;
-    mountedus.Length = mountedus.MaximumLength = (USHORT)wcslen(option_mounted) *
sizeof(WCHAR);
+    mountedus.Buffer = (WCHAR*)option_mounted;
+    mountedus.Length = mountedus.MaximumLength = sizeof(option_mounted) - sizeof(WCHAR);
     data = 1;
@@ -308,8 +318,8 @@ static NTSTATUS registry_mark_volume_unmounted_path(PUNICODE_STRING
path) {
     index = 0;
-    mountedus.Buffer = option_mounted;
-    mountedus.Length = mountedus.MaximumLength = (USHORT)wcslen(option_mounted) *
sizeof(WCHAR);
+    mountedus.Buffer = (WCHAR*)option_mounted;
+    mountedus.Length = mountedus.MaximumLength = sizeof(option_mounted) - sizeof(WCHAR);
     do {
         Status = ZwEnumerateValueKey(h, index, KeyValueBasicInformation, kvbi, kvbilen,
&retlen);
@@ -526,7 +536,7 @@ static void read_mappings(PUNICODE_STRING regpath) {
     ULONG dispos;
     NTSTATUS Status;
-    const WCHAR mappings[] = L"\\Mappings";
+    static const WCHAR mappings[] = L"\\Mappings";
     while (!IsListEmpty(&uid_map_list)) {
         uid_map* um = CONTAINING_RECORD(RemoveHeadList(&uid_map_list), uid_map,
listentry);
@@ -535,17 +545,17 @@ static void read_mappings(PUNICODE_STRING regpath) {
         ExFreePool(um);
     }
-    path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) *
sizeof(WCHAR)), ALLOC_TAG);
+    path = ExAllocatePoolWithTag(PagedPool, regpath->Length + sizeof(mappings) -
sizeof(WCHAR), ALLOC_TAG);
     if (!path) {
         ERR("out of memory\n");
         return;
     }
     RtlCopyMemory(path, regpath->Buffer, regpath->Length);
-    RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) *
sizeof(WCHAR));
+    RtlCopyMemory((UINT8*)path + regpath->Length, mappings, sizeof(mappings) -
sizeof(WCHAR));
     us.Buffer = path;
-    us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) *
sizeof(WCHAR));
+    us.Length = us.MaximumLength = regpath->Length + sizeof(mappings) - sizeof(WCHAR);
     InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE |
OBJ_KERNEL_HANDLE, NULL, NULL);
@@ -604,7 +614,7 @@ static void read_group_mappings(PUNICODE_STRING regpath) {
     ULONG dispos;
     NTSTATUS Status;
-    const WCHAR mappings[] = L"\\GroupMappings";
+    static const WCHAR mappings[] = L"\\GroupMappings";
     while (!IsListEmpty(&gid_map_list)) {
         gid_map* gm = CONTAINING_RECORD(RemoveHeadList(&gid_map_list), gid_map,
listentry);
@@ -613,17 +623,17 @@ static void read_group_mappings(PUNICODE_STRING regpath) {
         ExFreePool(gm);
     }
-    path = ExAllocatePoolWithTag(PagedPool, regpath->Length + (wcslen(mappings) *
sizeof(WCHAR)), ALLOC_TAG);
+    path = ExAllocatePoolWithTag(PagedPool, regpath->Length + sizeof(mappings) -
sizeof(WCHAR), ALLOC_TAG);
     if (!path) {
         ERR("out of memory\n");
         return;
     }
     RtlCopyMemory(path, regpath->Buffer, regpath->Length);
-    RtlCopyMemory((UINT8*)path + regpath->Length, mappings, wcslen(mappings) *
sizeof(WCHAR));
+    RtlCopyMemory((UINT8*)path + regpath->Length, mappings, sizeof(mappings) -
sizeof(WCHAR));
     us.Buffer = path;
-    us.Length = us.MaximumLength = regpath->Length + ((USHORT)wcslen(mappings) *
sizeof(WCHAR));
+    us.Length = us.MaximumLength = regpath->Length + sizeof(mappings) - sizeof(WCHAR);
     InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE |
OBJ_KERNEL_HANDLE, NULL, NULL);
@@ -676,7 +686,7 @@ static void read_group_mappings(PUNICODE_STRING regpath) {
         // If we're creating the key for the first time, we add a default mapping of
         // BUILTIN\Users to gid 100, which ought to correspond to the "users"
group on Linux.
-        us2.Length = us2.MaximumLength = (USHORT)wcslen(builtin_users) * sizeof(WCHAR);
+        us2.Length = us2.MaximumLength = sizeof(builtin_users) - sizeof(WCHAR);
         us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.MaximumLength, ALLOC_TAG);
         if (us2.Buffer) {
@@ -760,7 +770,7 @@ void read_registry(PUNICODE_STRING regpath, BOOL refresh) {
     ULONG kvfilen, old_debug_log_level = debug_log_level;
     UNICODE_STRING us, old_log_file, old_log_device;
-    static WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
+    static const WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
 #endif
     ExAcquireResourceExclusiveLite(&mapping_lock, TRUE);
@@ -794,6 +804,7 @@ void read_registry(PUNICODE_STRING regpath, BOOL refresh) {
     get_registry_value(h, L"ClearCache", REG_DWORD, &mount_clear_cache,
sizeof(mount_clear_cache));
     get_registry_value(h, L"AllowDegraded", REG_DWORD,
&mount_allow_degraded, sizeof(mount_allow_degraded));
     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));
     if (!refresh)
         get_registry_value(h, L"NoPNP", REG_DWORD, &no_pnp,
sizeof(no_pnp));
@@ -935,7 +946,7 @@ void read_registry(PUNICODE_STRING regpath, BOOL refresh) {
         ExFreePool(kvfi);
     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-        Status = ZwSetValueKey(h, &us, 0, REG_SZ, def_log_file,
(ULONG)(wcslen(def_log_file) + 1) * sizeof(WCHAR));
+        Status = ZwSetValueKey(h, &us, 0, REG_SZ, (void*)def_log_file,
sizeof(def_log_file));
         if (!NT_SUCCESS(Status))
             ERR("ZwSetValueKey returned %08x\n", Status);
@@ -947,7 +958,7 @@ void read_registry(PUNICODE_STRING regpath, BOOL refresh) {
     }
     if (log_file.Length == 0) {
-        log_file.Length = log_file.MaximumLength = (UINT16)wcslen(def_log_file) *
sizeof(WCHAR);
+        log_file.Length = log_file.MaximumLength = sizeof(def_log_file) - sizeof(WCHAR);
         log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength,
ALLOC_TAG);
         if (!log_file.Buffer) {
diff --git a/drivers/filesystems/btrfs/reparse.c b/drivers/filesystems/btrfs/reparse.c
index 8e8d319b58..957ad07305 100644
--- a/drivers/filesystems/btrfs/reparse.c
+++ b/drivers/filesystems/btrfs/reparse.c
@@ -169,7 +169,7 @@ end:
     return Status;
 }
-static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, ccb* ccb, REPARSE_DATA_BUFFER*
rdb, ULONG buflen, BOOL write, LIST_ENTRY* rollback) {
+static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, fcb* fcb, ccb* ccb,
REPARSE_DATA_BUFFER* rdb, ULONG buflen, BOOL write, LIST_ENTRY* rollback) {
     NTSTATUS Status;
     ULONG minlen;
     ULONG tlength;
@@ -197,15 +197,15 @@ static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, ccb* ccb,
REPARSE_DATA_
         TRACE("substitute name = %.*S\n", subname.Length / sizeof(WCHAR),
subname.Buffer);
     }
-    fileref->fcb->type = BTRFS_TYPE_SYMLINK;
-    fileref->fcb->inode_item.st_mode |= __S_IFLNK;
-    fileref->fcb->inode_item.generation =
fileref->fcb->Vcb->superblock.generation; // so we don't confuse btrfs send
on Linux
+    fcb->type = BTRFS_TYPE_SYMLINK;
+    fcb->inode_item.st_mode |= __S_IFLNK;
+    fcb->inode_item.generation = fcb->Vcb->superblock.generation; // so we
don't confuse btrfs send on Linux
-    if (fileref->dc)
-        fileref->dc->type = fileref->fcb->type;
+    if (fileref && fileref->dc)
+        fileref->dc->type = fcb->type;
     if (write) {
-        Status = truncate_file(fileref->fcb, 0, Irp, rollback);
+        Status = truncate_file(fcb, 0, Irp, rollback);
         if (!NT_SUCCESS(Status)) {
             ERR("truncate_file returned %08x\n", Status);
             return Status;
@@ -238,7 +238,7 @@ static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, ccb* ccb,
REPARSE_DATA_
         offset.QuadPart = 0;
         tlength = target.Length;
-        Status = write_file2(fileref->fcb->Vcb, Irp, offset, target.Buffer,
&tlength, FALSE, TRUE,
+        Status = write_file2(fcb->Vcb, Irp, offset, target.Buffer, &tlength,
FALSE, TRUE,
                              TRUE, FALSE, FALSE, rollback);
         ExFreePool(target.Buffer);
     } else
@@ -247,86 +247,34 @@ static NTSTATUS set_symlink(PIRP Irp, file_ref* fileref, ccb* ccb,
REPARSE_DATA_
     KeQuerySystemTime(&time);
     win_time_to_unix(time, &now);
-    fileref->fcb->inode_item.transid =
fileref->fcb->Vcb->superblock.generation;
-    fileref->fcb->inode_item.sequence++;
+    fcb->inode_item.transid = fcb->Vcb->superblock.generation;
+    fcb->inode_item.sequence++;
-    if (!ccb->user_set_change_time)
-        fileref->fcb->inode_item.st_ctime = now;
+    if (!ccb || !ccb->user_set_change_time)
+        fcb->inode_item.st_ctime = now;
-    if (!ccb->user_set_write_time)
-        fileref->fcb->inode_item.st_mtime = now;
+    if (!ccb || !ccb->user_set_write_time)
+        fcb->inode_item.st_mtime = now;
-    fileref->fcb->subvol->root_item.ctransid =
fileref->fcb->Vcb->superblock.generation;
-    fileref->fcb->subvol->root_item.ctime = now;
+    fcb->subvol->root_item.ctransid = fcb->Vcb->superblock.generation;
+    fcb->subvol->root_item.ctime = now;
-    fileref->fcb->inode_item_changed = TRUE;
-    mark_fcb_dirty(fileref->fcb);
+    fcb->inode_item_changed = TRUE;
+    mark_fcb_dirty(fcb);
-    mark_fileref_dirty(fileref);
+    if (fileref)
+        mark_fileref_dirty(fileref);
     return Status;
 }
-NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
-    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
-    PFILE_OBJECT FileObject = IrpSp->FileObject;
-    void* buffer = Irp->AssociatedIrp.SystemBuffer;
-    REPARSE_DATA_BUFFER* rdb = buffer;
-    DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
-    NTSTATUS Status = STATUS_SUCCESS;
-    fcb* fcb;
-    ccb* ccb;
-    file_ref* fileref;
+NTSTATUS set_reparse_point2(fcb* fcb, REPARSE_DATA_BUFFER* rdb, ULONG buflen, ccb* ccb,
file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
     ULONG tag;
-    LIST_ENTRY rollback;
-
-    TRACE("(%p, %p)\n", DeviceObject, Irp);
-
-    InitializeListHead(&rollback);
-
-    if (!FileObject) {
-        ERR("FileObject was NULL\n");
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    // IFSTest insists on this, for some reason...
-    if (Irp->UserBuffer)
-        return STATUS_INVALID_PARAMETER;
-
-    fcb = FileObject->FsContext;
-    ccb = FileObject->FsContext2;
-
-    if (!ccb) {
-        ERR("ccb was NULL\n");
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    if (Irp->RequestorMode == UserMode && !(ccb->access &
(FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA))) {
-        WARN("insufficient privileges\n");
-        return STATUS_ACCESS_DENIED;
-    }
-
-    fileref = ccb->fileref;
-
-    if (!fileref) {
-        ERR("fileref was NULL\n");
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    if (fcb->ads) {
-        fileref = fileref->parent;
-        fcb = fileref->fcb;
-    }
-
-    TRACE("%S\n", file_desc(FileObject));
-
-    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
-    ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
     if (fcb->type == BTRFS_TYPE_SYMLINK) {
         WARN("tried to set a reparse point on an existing symlink\n");
-        Status = STATUS_INVALID_PARAMETER;
-        goto end;
+        return STATUS_INVALID_PARAMETER;
     }
     // FIXME - fail if we already have the attribute FILE_ATTRIBUTE_REPARSE_POINT
@@ -335,34 +283,32 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     if (buflen < sizeof(ULONG)) {
         WARN("buffer was not long enough to hold tag\n");
-        Status = STATUS_INVALID_BUFFER_SIZE;
-        goto end;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
     Status = FsRtlValidateReparsePointBuffer(buflen, rdb);
     if (!NT_SUCCESS(Status)) {
         ERR("FsRtlValidateReparsePointBuffer returned %08x\n", Status);
-        goto end;
+        return Status;
     }
-    RtlCopyMemory(&tag, buffer, sizeof(ULONG));
+    RtlCopyMemory(&tag, rdb, sizeof(ULONG));
     if (fcb->type == BTRFS_TYPE_FILE &&
         ((tag == IO_REPARSE_TAG_SYMLINK &&
rdb->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) || tag ==
IO_REPARSE_TAG_LXSS_SYMLINK)) {
-        Status = set_symlink(Irp, fileref, ccb, rdb, buflen, tag ==
IO_REPARSE_TAG_SYMLINK, &rollback);
+        Status = set_symlink(Irp, fileref, fcb, ccb, rdb, buflen, tag ==
IO_REPARSE_TAG_SYMLINK, rollback);
         fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
     } else {
         LARGE_INTEGER offset, time;
         BTRFS_TIME now;
-        if (fcb->type == BTRFS_TYPE_DIRECTORY) { // for directories, store as xattr
+        if (fcb->type == BTRFS_TYPE_DIRECTORY || fcb->type == BTRFS_TYPE_CHARDEV ||
fcb->type == BTRFS_TYPE_BLOCKDEV) { // store as xattr
             ANSI_STRING buf;
             buf.Buffer = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
             if (!buf.Buffer) {
                 ERR("out of memory\n");
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto end;
+                return STATUS_INSUFFICIENT_RESOURCES;
             }
             buf.Length = buf.MaximumLength = (UINT16)buflen;
@@ -370,24 +316,24 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
                 ExFreePool(fcb->reparse_xattr.Buffer);
             fcb->reparse_xattr = buf;
-            RtlCopyMemory(buf.Buffer, buffer, buflen);
+            RtlCopyMemory(buf.Buffer, rdb, buflen);
             fcb->reparse_xattr_changed = TRUE;
             Status = STATUS_SUCCESS;
         } else { // otherwise, store as file data
-            Status = truncate_file(fcb, 0, Irp, &rollback);
+            Status = truncate_file(fcb, 0, Irp, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("truncate_file returned %08x\n", Status);
-                goto end;
+                return Status;
             }
             offset.QuadPart = 0;
-            Status = write_file2(fcb->Vcb, Irp, offset, buffer, &buflen, FALSE,
TRUE, TRUE, FALSE, FALSE, &rollback);
+            Status = write_file2(fcb->Vcb, Irp, offset, rdb, &buflen, FALSE, TRUE,
TRUE, FALSE, FALSE, rollback);
             if (!NT_SUCCESS(Status)) {
                 ERR("write_file2 returned %08x\n", Status);
-                goto end;
+                return Status;
             }
         }
@@ -397,10 +343,10 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         fcb->inode_item.transid = fcb->Vcb->superblock.generation;
         fcb->inode_item.sequence++;
-        if (!ccb->user_set_change_time)
+        if (!ccb || !ccb->user_set_change_time)
             fcb->inode_item.st_ctime = now;
-        if (!ccb->user_set_write_time)
+        if (!ccb || !ccb->user_set_write_time)
             fcb->inode_item.st_mtime = now;
         fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT;
@@ -413,6 +359,70 @@ NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         mark_fcb_dirty(fcb);
     }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    PFILE_OBJECT FileObject = IrpSp->FileObject;
+    void* buffer = Irp->AssociatedIrp.SystemBuffer;
+    REPARSE_DATA_BUFFER* rdb = buffer;
+    DWORD buflen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+    NTSTATUS Status = STATUS_SUCCESS;
+    fcb* fcb;
+    ccb* ccb;
+    file_ref* fileref;
+    LIST_ENTRY rollback;
+
+    TRACE("(%p, %p)\n", DeviceObject, Irp);
+
+    InitializeListHead(&rollback);
+
+    if (!FileObject) {
+        ERR("FileObject was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    // IFSTest insists on this, for some reason...
+    if (Irp->UserBuffer)
+        return STATUS_INVALID_PARAMETER;
+
+    fcb = FileObject->FsContext;
+    ccb = FileObject->FsContext2;
+
+    if (!ccb) {
+        ERR("ccb was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (Irp->RequestorMode == UserMode && !(ccb->access &
(FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA))) {
+        WARN("insufficient privileges\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    fileref = ccb->fileref;
+
+    if (!fileref) {
+        ERR("fileref was NULL\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (fcb->ads) {
+        fileref = fileref->parent;
+        fcb = fileref->fcb;
+    }
+
+    TRACE("%S\n", file_desc(FileObject));
+
+    ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, TRUE);
+    ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
+
+    Status = set_reparse_point2(fcb, rdb, buflen, ccb, fileref, Irp, &rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("set_reparse_point2 returned %08x\n", Status);
+        goto end;
+    }
+
     send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL);
 end:
diff --git a/drivers/filesystems/btrfs/scrub.c b/drivers/filesystems/btrfs/scrub.c
index 9dd7c2008d..f2d3d5e9ed 100644
--- a/drivers/filesystems/btrfs/scrub.c
+++ b/drivers/filesystems/btrfs/scrub.c
@@ -3190,7 +3190,7 @@ static void scrub_thread(void* context) {
     while (le != &Vcb->chunks) {
         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, Vcb);
         if (!c->readonly) {
             InsertTailList(&chunks, &c->list_entry_balance);
@@ -3198,7 +3198,7 @@ static void scrub_thread(void* context) {
             Vcb->scrub.chunks_left++;
         }
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, Vcb);
         le = le->Flink;
     }
diff --git a/drivers/filesystems/btrfs/search.c b/drivers/filesystems/btrfs/search.c
index bec1aba420..7333a1e1df 100644
--- a/drivers/filesystems/btrfs/search.c
+++ b/drivers/filesystems/btrfs/search.c
@@ -496,7 +496,7 @@ void remove_volume_child(_Inout_
_Requires_exclusive_lock_held_(_Curr_->child_lo
             pdo = vde->pdo;
             IoDeleteDevice(vde->device);
-            if (no_pnp)
+            if (!no_pnp)
                 IoDeleteDevice(pdo);
         }
     } else
@@ -540,7 +540,7 @@ void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING
devpath) {
         TRACE("DeviceType = %u, DeviceNumber = %u, PartitionNumber = %u\n",
sdn.DeviceType, sdn.DeviceNumber, sdn.PartitionNumber);
     // If we've just added a partition to a whole-disk filesystem, unmount it
-    if (sdn.DeviceNumber != 0xffffffff) {
+    if (sdn.DeviceNumber != 0xffffffff && sdn.PartitionNumber != 0) {
         LIST_ENTRY* le;
         ExAcquireResourceExclusiveLite(&pdo_list_lock, TRUE);
@@ -823,7 +823,7 @@ static void mountmgr_process_drive(PDEVICE_OBJECT mountmgr,
PUNICODE_STRING devi
 static void mountmgr_updated(PDEVICE_OBJECT mountmgr, MOUNTMGR_MOUNT_POINTS* mmps) {
     ULONG i;
-    static WCHAR pref[] = L"\\DosDevices\\";
+    static const WCHAR pref[] = L"\\DosDevices\\";
     for (i = 0; i < mmps->NumberOfMountPoints; i++) {
         UNICODE_STRING symlink, device_name;
@@ -844,8 +844,8 @@ static void mountmgr_updated(PDEVICE_OBJECT mountmgr,
MOUNTMGR_MOUNT_POINTS* mmp
             device_name.Length = device_name.MaximumLength = 0;
         }
-        if (symlink.Length > wcslen(pref) * sizeof(WCHAR) &&
-            RtlCompareMemory(symlink.Buffer, pref, wcslen(pref) * sizeof(WCHAR)) ==
wcslen(pref) * sizeof(WCHAR))
+        if (symlink.Length > sizeof(pref) - sizeof(WCHAR) &&
+            RtlCompareMemory(symlink.Buffer, pref, sizeof(pref) - sizeof(WCHAR)) ==
sizeof(pref) - sizeof(WCHAR))
             mountmgr_process_drive(mountmgr, &device_name);
     }
 }
diff --git a/drivers/filesystems/btrfs/send.c b/drivers/filesystems/btrfs/send.c
index a865c62c3e..260612a155 100644
--- a/drivers/filesystems/btrfs/send.c
+++ b/drivers/filesystems/btrfs/send.c
@@ -1648,7 +1648,7 @@ static NTSTATUS wait_for_flush(send_context* context, traverse_ptr*
tp1, travers
     return STATUS_SUCCESS;
 }
-static NTSTATUS add_ext_holes(LIST_ENTRY* exts, UINT64 size) {
+static NTSTATUS add_ext_holes(device_extension* Vcb, LIST_ENTRY* exts, UINT64 size) {
     UINT64 lastoff = 0;
     LIST_ENTRY* le;
@@ -1699,7 +1699,7 @@ static NTSTATUS add_ext_holes(LIST_ENTRY* exts, UINT64 size) {
         ext2->offset = lastoff;
         ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2);
-        ext2->data.decoded_size = ed2->num_bytes = size - lastoff;
+        ext2->data.decoded_size = ed2->num_bytes = sector_align(size - lastoff,
Vcb->superblock.sector_size);
         ext2->data.type = EXTENT_TYPE_REGULAR;
         ed2->address = ed2->size = ed2->offset = 0;
@@ -2130,13 +2130,13 @@ static NTSTATUS flush_extents(send_context* context, traverse_ptr*
tp1, traverse
         return STATUS_SUCCESS;
     if (context->parent) {
-        Status = add_ext_holes(&context->lastinode.exts,
context->lastinode.size);
+        Status = add_ext_holes(context->Vcb, &context->lastinode.exts,
context->lastinode.size);
         if (!NT_SUCCESS(Status)) {
             ERR("add_ext_holes returned %08x\n", Status);
             return Status;
         }
-        Status = add_ext_holes(&context->lastinode.oldexts,
context->lastinode.size);
+        Status = add_ext_holes(context->Vcb, &context->lastinode.oldexts,
context->lastinode.size);
         if (!NT_SUCCESS(Status)) {
             ERR("add_ext_holes returned %08x\n", Status);
             return Status;
@@ -2187,7 +2187,7 @@ static NTSTATUS flush_extents(send_context* context, traverse_ptr*
tp1, traverse
             if (se->data.compression == BTRFS_COMPRESSION_NONE)
                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, se->data.data,
(UINT16)se->data.decoded_size);
-            else if (se->data.compression == BTRFS_COMPRESSION_ZLIB ||
se->data.compression == BTRFS_COMPRESSION_LZO) {
+            else if (se->data.compression == BTRFS_COMPRESSION_ZLIB ||
se->data.compression == BTRFS_COMPRESSION_LZO || se->data.compression ==
BTRFS_COMPRESSION_ZSTD) {
                 ULONG inlen = se->datalen - (ULONG)offsetof(EXTENT_DATA, data[0]);
                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL,
(UINT16)se->data.decoded_size);
@@ -2217,6 +2217,14 @@ static NTSTATUS flush_extents(send_context* context, traverse_ptr*
tp1, traverse
                         if (se2) ExFreePool(se2);
                         return Status;
                     }
+                } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) {
+                    Status = zstd_decompress(se->data.data, inlen,
&context->data[context->datalen - se->data.decoded_size],
(UINT32)se->data.decoded_size);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("zlib_decompress returned %08x\n", Status);
+                        ExFreePool(se);
+                        if (se2) ExFreePool(se2);
+                        return Status;
+                    }
                 }
             } else {
                 ERR("unhandled compression type %x\n",
se->data.compression);
@@ -2367,7 +2375,7 @@ static NTSTATUS flush_extents(send_context* context, traverse_ptr*
tp1, traverse
                 offset = se->offset + off;
                 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset,
sizeof(UINT64));
-                length = min((UINT16)(context->lastinode.size - se->offset - off),
length);
+                length = (UINT16)min(context->lastinode.size - se->offset - off,
length);
                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, buf + skip_start, length);
                 send_command_finish(context, pos);
@@ -2459,6 +2467,16 @@ static NTSTATUS flush_extents(send_context* context, traverse_ptr*
tp1, traverse
                     if (se2) ExFreePool(se2);
                     return Status;
                 }
+            } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) {
+                Status = zstd_decompress(compbuf, (UINT32)ed2->size, buf,
(UINT32)se->data.decoded_size);
+                if (!NT_SUCCESS(Status)) {
+                    ERR("zstd_decompress returned %08x\n", Status);
+                    ExFreePool(compbuf);
+                    ExFreePool(buf);
+                    ExFreePool(se);
+                    if (se2) ExFreePool(se2);
+                    return Status;
+                }
             }
             ExFreePool(compbuf);
@@ -2494,7 +2512,7 @@ static NTSTATUS flush_extents(send_context* context, traverse_ptr*
tp1, traverse
                 offset = se->offset + off;
                 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset,
sizeof(UINT64));
-                length = min((UINT16)(context->lastinode.size - se->offset - off),
length);
+                length = (UINT16)min(context->lastinode.size - se->offset - off,
length);
                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, &buf[off], length);
                 send_command_finish(context, pos);
@@ -2639,7 +2657,8 @@ static NTSTATUS send_extent_data(send_context* context,
traverse_ptr* tp, traver
             return STATUS_INTERNAL_ERROR;
         }
-        if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression !=
BTRFS_COMPRESSION_ZLIB && ed->compression != BTRFS_COMPRESSION_LZO) {
+        if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression !=
BTRFS_COMPRESSION_ZLIB &&
+            ed->compression != BTRFS_COMPRESSION_LZO && ed->compression !=
BTRFS_COMPRESSION_ZSTD) {
             ERR("unknown compression type %u\n", ed->compression);
             return STATUS_INTERNAL_ERROR;
         }
@@ -2697,7 +2716,8 @@ static NTSTATUS send_extent_data(send_context* context,
traverse_ptr* tp, traver
             return STATUS_INTERNAL_ERROR;
         }
-        if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression !=
BTRFS_COMPRESSION_ZLIB && ed->compression != BTRFS_COMPRESSION_LZO) {
+        if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression !=
BTRFS_COMPRESSION_ZLIB &&
+            ed->compression != BTRFS_COMPRESSION_LZO && ed->compression !=
BTRFS_COMPRESSION_ZSTD) {
             ERR("unknown compression type %u\n", ed->compression);
             return STATUS_INTERNAL_ERROR;
         }
@@ -3730,6 +3750,10 @@ NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG
datalen, PFILE_OBJ
     context = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_context), ALLOC_TAG);
     if (!context) {
         ERR("out of memory\n");
+
+        if (clones)
+            ExFreePool(clones);
+
         ExReleaseResourceLite(&Vcb->send_load_lock);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
@@ -3769,6 +3793,10 @@ NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG
datalen, PFILE_OBJ
         ERR("out of memory\n");
         ExFreePool(context->data);
         ExFreePool(context);
+
+        if (clones)
+            ExFreePool(clones);
+
         ExReleaseResourceLite(&Vcb->send_load_lock);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
@@ -3794,6 +3822,10 @@ NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG
datalen, PFILE_OBJ
         ExFreePool(send);
         ExFreePool(context->data);
         ExFreePool(context);
+
+        if (clones)
+            ExFreePool(clones);
+
         ExReleaseResourceLite(&Vcb->send_load_lock);
         return Status;
     }
diff --git a/drivers/filesystems/btrfs/treefuncs.c b/drivers/filesystems/btrfs/treefuncs.c
index e2f6001e65..9785e12713 100644
--- a/drivers/filesystems/btrfs/treefuncs.c
+++ b/drivers/filesystems/btrfs/treefuncs.c
@@ -72,6 +72,7 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
pt, UINT6
         if ((t->header.num_items * sizeof(leaf_node)) + sizeof(tree_header) >
Vcb->superblock.node_size) {
             ERR("tree at %llx has more items than expected (%x)\n",
t->header.num_items);
+            ExFreePool(t);
             ExFreePool(buf);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
@@ -80,6 +81,7 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
pt, UINT6
             td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
             if (!td) {
                 ERR("out of memory\n");
+                ExFreePool(t);
                 ExFreePool(buf);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
@@ -93,6 +95,8 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
pt, UINT6
             if (ln[i].size + sizeof(tree_header) + sizeof(leaf_node) >
Vcb->superblock.node_size) {
                 ERR("overlarge item in tree %llx: %u > %u\n", addr,
ln[i].size, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node));
+                ExFreeToPagedLookasideList(&t->Vcb->tree_data_lookaside, td);
+                ExFreePool(t);
                 ExFreePool(buf);
                 return STATUS_INTERNAL_ERROR;
             }
@@ -114,6 +118,7 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
pt, UINT6
         if ((t->header.num_items * sizeof(internal_node)) + sizeof(tree_header) >
Vcb->superblock.node_size) {
             ERR("tree at %llx has more items than expected (%x)\n",
t->header.num_items);
+            ExFreePool(t);
             ExFreePool(buf);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
@@ -122,6 +127,7 @@ NTSTATUS load_tree(device_extension* Vcb, UINT64 addr, root* r, tree**
pt, UINT6
             td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
             if (!td) {
                 ERR("out of memory\n");
+                ExFreePool(t);
                 ExFreePool(buf);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
@@ -1096,7 +1102,7 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
                 rollback_space* rs = ri->ptr;
                 if (rs->chunk)
-                    ExAcquireResourceExclusiveLite(&rs->chunk->lock, TRUE);
+                    acquire_chunk_lock(rs->chunk, Vcb);
                 if (ri->type == ROLLBACK_ADD_SPACE)
                     space_list_subtract2(rs->list, rs->list_size, rs->address,
rs->length, NULL, NULL);
@@ -1138,7 +1144,7 @@ void do_rollback(device_extension* Vcb, LIST_ENTRY* rollback) {
                         le2 = le3;
                     }
-                    ExReleaseResourceLite(&rs->chunk->lock);
+                    release_chunk_lock(rs->chunk, Vcb);
                 }
                 ExFreePool(rs);
@@ -1509,6 +1515,7 @@ static NTSTATUS handle_batch_collision(device_extension* Vcb,
batch_item* bi, tr
                                 td2 =
ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
                                 if (!td2) {
                                     ERR("out of memory\n");
+                                    ExFreePool(newdi);
                                     return STATUS_INSUFFICIENT_RESOURCES;
                                 }
@@ -1595,6 +1602,7 @@ static NTSTATUS handle_batch_collision(device_extension* Vcb,
batch_item* bi, tr
                                 td2 =
ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside);
                                 if (!td2) {
                                     ERR("out of memory\n");
+                                    ExFreePool(newir);
                                     return STATUS_INSUFFICIENT_RESOURCES;
                                 }
diff --git a/drivers/filesystems/btrfs/volume.c b/drivers/filesystems/btrfs/volume.c
index 5f7516a5c2..fa33221b7c 100644
--- a/drivers/filesystems/btrfs/volume.c
+++ b/drivers/filesystems/btrfs/volume.c
@@ -53,6 +53,8 @@ NTSTATUS vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
     Irp->IoStatus.Information = 0;
+    ExAcquireResourceExclusiveLite(&pdo_list_lock, TRUE);
+
     ExAcquireResourceSharedLite(&pdode->child_lock, TRUE);
     if (InterlockedDecrement(&vde->open_count) == 0 && vde->removing) {
@@ -83,16 +85,20 @@ NTSTATUS vol_close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
         ExReleaseResourceLite(&pdode->child_lock);
         ExDeleteResourceLite(&pdode->child_lock);
-        IoDetachDevice(vde->pdo);
+
+        if (vde->pdo->AttachedDevice)
+            IoDetachDevice(vde->pdo);
         pdo = vde->pdo;
         IoDeleteDevice(vde->device);
-        if (no_pnp)
+        if (!no_pnp)
             IoDeleteDevice(pdo);
     } else
         ExReleaseResourceLite(&pdode->child_lock);
+    ExReleaseResourceLite(&pdo_list_lock);
+
     return STATUS_SUCCESS;
 }
@@ -971,7 +977,7 @@ static BOOL allow_degraded_mount(BTRFS_UUID* uuid) {
     }
     adus.Buffer = L"AllowDegraded";
-    adus.Length = adus.MaximumLength = (USHORT)(wcslen(adus.Buffer) * sizeof(WCHAR));
+    adus.Length = adus.MaximumLength = sizeof(adus.Buffer) - sizeof(WCHAR);
     if (NT_SUCCESS(ZwQueryValueKey(h, &adus, KeyValueFullInformation, kvfi, kvfilen,
&retlen))) {
         if (kvfi->Type == REG_DWORD && kvfi->DataLength >=
sizeof(UINT32)) {
diff --git a/drivers/filesystems/btrfs/write.c b/drivers/filesystems/btrfs/write.c
index 6292d7efb0..ed1df4c897 100644
--- a/drivers/filesystems/btrfs/write.c
+++ b/drivers/filesystems/btrfs/write.c
@@ -404,10 +404,6 @@ NTSTATUS alloc_chunk(device_extension* Vcb, UINT64 flags, chunk** pc,
BOOL full_
         return STATUS_INTERNAL_ERROR;
     }
-    max_chunk_size = min(max_chunk_size, total_size / 10); // cap at 10%
-
-    TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n",
max_chunk_size, max_stripe_size);
-
     if (flags & BLOCK_FLAG_DUPLICATE) {
         min_stripes = 2;
         max_stripes = 2;
@@ -452,6 +448,13 @@ NTSTATUS alloc_chunk(device_extension* Vcb, UINT64 flags, chunk** pc,
BOOL full_
         allowed_missing = 0;
     }
+    if (max_chunk_size > total_size / 10) {  // cap at 10%
+        max_chunk_size = total_size / 10;
+        max_stripe_size = max_chunk_size / min_stripes;
+    }
+
+    TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n",
max_chunk_size, max_stripe_size);
+
     stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * max_stripes, ALLOC_TAG);
     if (!stripes) {
         ERR("out of memory\n");
@@ -511,6 +514,8 @@ NTSTATUS alloc_chunk(device_extension* Vcb, UINT64 flags, chunk** pc,
BOOL full_
         goto end;
     }
+    c->devices = NULL;
+
     cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE));
     c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, cisize, ALLOC_TAG);
     if (!c->chunk_item) {
@@ -549,7 +554,8 @@ NTSTATUS alloc_chunk(device_extension* Vcb, UINT64 flags, chunk** pc,
BOOL full_
         stripe_size -= stripe_size % stripe_length;
     if (stripe_size == 0) {
-        Status = STATUS_INTERNAL_ERROR;
+        ERR("not enough free space found (stripe_size == 0)\n");
+        Status = STATUS_DISK_FULL;
         goto end;
     }
@@ -1287,7 +1293,8 @@ static NTSTATUS prepare_raid5_write(device_extension* Vcb, chunk* c,
UINT64 addr
             stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) +
irp_offset, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL);
             if (!stripes[i].mdl) {
                 ERR("IoAllocateMdl failed\n");
-                return STATUS_INSUFFICIENT_RESOURCES;
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
             }
         }
     }
@@ -1702,7 +1709,8 @@ static NTSTATUS prepare_raid6_write(device_extension* Vcb, chunk* c,
UINT64 addr
             stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) +
irp_offset, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL);
             if (!stripes[i].mdl) {
                 ERR("IoAllocateMdl failed\n");
-                return STATUS_INSUFFICIENT_RESOURCES;
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto exit;
             }
         }
     }
@@ -1880,7 +1888,6 @@ NTSTATUS write_data(_In_ device_extension* Vcb, _In_ UINT64 address,
_In_reads_b
     NTSTATUS Status;
     UINT32 i;
     CHUNK_ITEM_STRIPE* cis;
-    write_data_stripe* stripe;
     write_stripe* stripes = NULL;
     UINT64 total_writing = 0;
     ULONG allowed_missing, missing;
@@ -2001,6 +2008,7 @@ NTSTATUS write_data(_In_ device_extension* Vcb, _In_ UINT64 address,
_In_reads_b
     }
     for (i = 0; i < c->chunk_item->num_stripes; i++) {
+        write_data_stripe* stripe;
         PIO_STACK_LOCATION IrpSp;
         stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_stripe),
ALLOC_TAG);
@@ -2028,6 +2036,7 @@ NTSTATUS write_data(_In_ device_extension* Vcb, _In_ UINT64 address,
_In_reads_b
                 if (!stripe->Irp) {
                     ERR("IoAllocateIrp failed\n");
+                    ExFreePool(stripe);
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto end;
                 }
@@ -2036,6 +2045,7 @@ NTSTATUS write_data(_In_ device_extension* Vcb, _In_ UINT64 address,
_In_reads_b
                 if (!stripe->Irp) {
                     ERR("IoMakeAssociatedIrp failed\n");
+                    ExFreePool(stripe);
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto end;
                 }
@@ -2333,10 +2343,9 @@ void free_write_data_stripes(write_data_context* wtc) {
         last_mdl = stripe->mdl;
-#ifdef __REACTOS__
         if (stripe->Irp)
             IoFreeIrp(stripe->Irp);
-#endif
+
         le = le->Flink;
     }
@@ -2888,7 +2897,7 @@ BOOL insert_extent_chunk(_In_ device_extension* Vcb, _In_ fcb* fcb,
_In_ chunk*
     ExReleaseResourceLite(&c->changed_extents_lock);
-    ExReleaseResourceLite(&c->lock);
+    release_chunk_lock(c, Vcb);
     if (data) {
         Status = write_data_complete(Vcb, address, data, (UINT32)length, Irp, NULL,
file_write, irp_offset,
@@ -2949,10 +2958,10 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb,
UINT64 start_data,
     if (c->reloc || c->readonly || c->chunk_item->type != Vcb->data_flags)
         return FALSE;
-    ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+    acquire_chunk_lock(c, Vcb);
     if (length > c->chunk_item->size - c->used) {
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, Vcb);
         return FALSE;
     }
@@ -2961,7 +2970,7 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64
start_data,
         if (!NT_SUCCESS(Status)) {
             ERR("load_cache_chunk returned %08x\n", Status);
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, Vcb);
             return FALSE;
         }
     }
@@ -2978,7 +2987,7 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64
start_data,
             if (success)
                 *written += newlen;
             else
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
             return success;
         } else if (s->address > ed2->address + ed2->size)
@@ -2987,7 +2996,7 @@ static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64
start_data,
         le = le->Flink;
     }
-    ExReleaseResourceLite(&c->lock);
+    release_chunk_lock(c, Vcb);
     return FALSE;
 }
@@ -3017,7 +3026,7 @@ static NTSTATUS insert_chunk_fragmented(fcb* fcb, UINT64 start,
UINT64 length, U
         c = CONTAINING_RECORD(le, chunk, list_entry);
         if (!c->readonly && !c->reloc) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, fcb->Vcb);
             if (c->chunk_item->type == flags) {
                 while (!IsListEmpty(&c->space_size) && length > 0) {
@@ -3029,12 +3038,12 @@ static NTSTATUS insert_chunk_fragmented(fcb* fcb, UINT64 start,
UINT64 length, U
                         length -= extlen;
                         if (data) data += extlen;
-                        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+                        acquire_chunk_lock(c, fcb->Vcb);
                     }
                 }
             }
-            ExReleaseResourceLite(&c->lock);
+            release_chunk_lock(c, fcb->Vcb);
             if (length == 0)
                 break;
@@ -3067,7 +3076,7 @@ static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start,
UINT64 length, LI
             c = CONTAINING_RECORD(le, chunk, list_entry);
             if (!c->readonly && !c->reloc) {
-                ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+                acquire_chunk_lock(c, fcb->Vcb);
                 if (c->chunk_item->type == flags &&
(c->chunk_item->size - c->used) >= extlen) {
                     if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen,
!page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) {
@@ -3076,7 +3085,7 @@ static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start,
UINT64 length, LI
                     }
                 }
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, fcb->Vcb);
             }
             le = le->Flink;
@@ -3095,14 +3104,14 @@ static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start,
UINT64 length, LI
             goto end;
         }
-        ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+        acquire_chunk_lock(c, fcb->Vcb);
         if (c->chunk_item->type == flags && (c->chunk_item->size -
c->used) >= extlen) {
             if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL,
NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0))
                 goto cont;
         }
-        ExReleaseResourceLite(&c->lock);
+        release_chunk_lock(c, fcb->Vcb);
         Status = insert_chunk_fragmented(fcb, start, length, NULL, TRUE, rollback);
         if (!NT_SUCCESS(Status))
@@ -3159,7 +3168,7 @@ static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb,
UINT64 start_data
             c = CONTAINING_RECORD(le, chunk, list_entry);
             if (!c->readonly && !c->reloc) {
-                ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+                acquire_chunk_lock(c, Vcb);
                 if (c->chunk_item->type == flags &&
(c->chunk_item->size - c->used) >= newlen &&
                     insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data,
Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
@@ -3177,7 +3186,7 @@ static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb,
UINT64 start_data
                         break;
                     }
                 } else
-                    ExReleaseResourceLite(&c->lock);
+                    release_chunk_lock(c, Vcb);
             }
             le = le->Flink;
@@ -3201,7 +3210,7 @@ static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb,
UINT64 start_data
         }
         if (c) {
-            ExAcquireResourceExclusiveLite(&c->lock, TRUE);
+            acquire_chunk_lock(c, Vcb);
             if (c->chunk_item->type == flags && (c->chunk_item->size
- c->used) >= newlen &&
                 insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp,
rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
@@ -3217,7 +3226,7 @@ static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb,
UINT64 start_data
                     data = &((UINT8*)data)[newlen];
                 }
             } else
-                ExReleaseResourceLite(&c->lock);
+                release_chunk_lock(c, Vcb);
         }
         if (!done) {
@@ -3642,6 +3651,8 @@ static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64
start_data,
                                      Irp, NULL, file_write, irp_offset + ext->offset -
start_data, priority);
         if (!NT_SUCCESS(Status)) {
             ERR("write_data_complete returned %08x\n", Status);
+            ExFreePool(newext1);
+            ExFreePool(newext2);
             return Status;
         }
@@ -3737,6 +3748,8 @@ static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64
start_data,
         Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset,
data, (UINT32)ned2->num_bytes, Irp, NULL, file_write, irp_offset, priority);
         if (!NT_SUCCESS(Status)) {
             ERR("write_data_complete returned %08x\n", Status);
+            ExFreePool(newext1);
+            ExFreePool(newext2);
             return Status;
         }
diff --git a/drivers/filesystems/btrfs/zstd/bitstream.h
b/drivers/filesystems/btrfs/zstd/bitstream.h
new file mode 100644
index 0000000000..ef89b9878e
--- /dev/null
+++ b/drivers/filesystems/btrfs/zstd/bitstream.h
@@ -0,0 +1,455 @@
+/* ******************************************************************
+   bitstream
+   Part of FSE library
+   Copyright (C) 2013-present, Yann Collet.
+
+   BSD 2-Clause License (
http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+   - Source repository : 
https://github.com/Cyan4973/FiniteStateEntropy
+****************************************************************** */
+#ifndef BITSTREAM_H_MODULE
+#define BITSTREAM_H_MODULE
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/*
+*  This API consists of small unitary functions, which must be inlined for best
performance.
+*  Since link-time-optimization is not available for all compilers,
+*  these functions are defined into a .h to be included.
+*/
+
+/*-****************************************
+*  Dependencies
+******************************************/
+#include "mem.h"            /* unaligned access routines */
+#include "debug.h"          /* assert(), DEBUGLOG(), RAWLOG() */
+#include "error_private.h"  /* error codes and messages */
+
+
+/*=========================================
+*  Target specific
+=========================================*/
+#if defined(__BMI__) && defined(__GNUC__)
+#  include <immintrin.h>   /* support for bextr (experimental) */
+#endif
+
+#define STREAM_ACCUMULATOR_MIN_32  25
+#define STREAM_ACCUMULATOR_MIN_64  57
+#define STREAM_ACCUMULATOR_MIN    ((U32)(MEM_32bits() ? STREAM_ACCUMULATOR_MIN_32 :
STREAM_ACCUMULATOR_MIN_64))
+
+
+/*-******************************************
+*  bitStream encoding API (write forward)
+********************************************/
+/* bitStream can mix input from multiple sources.
+ * A critical property of these streams is that they encode and decode in **reverse**
direction.
+ * So the first bit sequence you add will be the last to be read, like a LIFO stack.
+ */
+typedef struct {
+    size_t bitContainer;
+    unsigned bitPos;
+    char*  startPtr;
+    char*  ptr;
+    char*  endPtr;
+} BIT_CStream_t;
+
+MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* dstBuffer, size_t
dstCapacity);
+MEM_STATIC void   BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits);
+MEM_STATIC void   BIT_flushBits(BIT_CStream_t* bitC);
+MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC);
+
+/* Start with initCStream, providing the size of buffer to write into.
+*  bitStream will never write outside of this buffer.
+*  `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be
an error code.
+*
+*  bits are first added to a local register.
+*  Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits
systems.
+*  Writing data into memory is an explicit operation, performed by the flushBits
function.
+*  Hence keep track how many bits are potentially stored into local register to avoid
register overflow.
+*  After a flushBits, a maximum of 7 bits might still be stored into local register.
+*
+*  Avoid storing elements of more than 24 bits if you want compatibility with 32-bits
bitstream readers.
+*
+*  Last operation is to close the bitStream.
+*  The function returns the final size of CStream in bytes.
+*  If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable)
+*/
+
+
+/*-********************************************
+*  bitStream decoding API (read backward)
+**********************************************/
+typedef struct {
+    size_t   bitContainer;
+    unsigned bitsConsumed;
+    const char* ptr;
+    const char* start;
+    const char* limitPtr;
+} BIT_DStream_t;
+
+typedef enum { BIT_DStream_unfinished = 0,
+               BIT_DStream_endOfBuffer = 1,
+               BIT_DStream_completed = 2,
+               BIT_DStream_overflow = 3 } BIT_DStream_status;  /* result of
BIT_reloadDStream() */
+               /* 1,2,4,8 would be better for bitmap combinations, but slows down
performance a bit ... :( */
+
+MEM_STATIC size_t   BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t
srcSize);
+MEM_STATIC size_t   BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits);
+MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD);
+MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD);
+
+
+/* Start by invoking BIT_initDStream().
+*  A chunk of the bitStream is then stored into a local register.
+*  Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems
(size_t).
+*  You can then retrieve bitFields stored into the local register, **in reverse order**.
+*  Local register is explicitly reloaded from memory by the BIT_reloadDStream() method.
+*  A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its
result is BIT_DStream_unfinished.
+*  Otherwise, it can be less than that, so proceed accordingly.
+*  Checking if DStream has reached its end can be performed with BIT_endOfDStream().
+*/
+
+
+/*-****************************************
+*  unsafe API
+******************************************/
+MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, size_t value, unsigned nbBits);
+/* faster, but works only if value is "clean", meaning all high bits above
nbBits are 0 */
+
+MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC);
+/* unsafe version; does not check buffer overflow */
+
+MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits);
+/* faster, but works only if nbBits >= 1 */
+
+
+
+/*-**************************************************************
+*  Internal functions
+****************************************************************/
+MEM_STATIC unsigned BIT_highbit32 (U32 val)
+{
+    assert(val != 0);
+    {
+#   if defined(_MSC_VER)   /* Visual */
+        unsigned long r=0;
+        _BitScanReverse ( &r, val );
+        return (unsigned) r;
+#   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* Use GCC Intrinsic */
+        return 31 - __builtin_clz (val);
+#   else   /* Software version */
+        static const unsigned DeBruijnClz[32] = { 0,  9,  1, 10, 13, 21,  2, 29,
+                                                 11, 14, 16, 18, 22, 25,  3, 30,
+                                                  8, 12, 20, 28, 15, 17, 24,  7,
+                                                 19, 27, 23,  6, 26,  5,  4, 31 };
+        U32 v = val;
+        v |= v >> 1;
+        v |= v >> 2;
+        v |= v >> 4;
+        v |= v >> 8;
+        v |= v >> 16;
+        return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27];
+#   endif
+    }
+}
+
+/*=====    Local Constants   =====*/
+static const unsigned BIT_mask[] = {
+    0,          1,         3,         7,         0xF,       0x1F,
+    0x3F,       0x7F,      0xFF,      0x1FF,     0x3FF,     0x7FF,
+    0xFFF,      0x1FFF,    0x3FFF,    0x7FFF,    0xFFFF,    0x1FFFF,
+    0x3FFFF,    0x7FFFF,   0xFFFFF,   0x1FFFFF,  0x3FFFFF,  0x7FFFFF,
+    0xFFFFFF,   0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF,
+    0x3FFFFFFF, 0x7FFFFFFF}; /* up to 31 bits */
+#define BIT_MASK_SIZE (sizeof(BIT_mask) / sizeof(BIT_mask[0]))
+
+/*-**************************************************************
+*  bitStream encoding
+****************************************************************/
+/*! BIT_initCStream() :
+ *  `dstCapacity` must be > sizeof(size_t)
+ *  @return : 0 if success,
+ *            otherwise an error code (can be tested using ERR_isError()) */
+MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
+                                  void* startPtr, size_t dstCapacity)
+{
+    bitC->bitContainer = 0;
+    bitC->bitPos = 0;
+    bitC->startPtr = (char*)startPtr;
+    bitC->ptr = bitC->startPtr;
+    bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->bitContainer);
+    if (dstCapacity <= sizeof(bitC->bitContainer)) return ERROR(dstSize_tooSmall);
+    return 0;
+}
+
+/*! BIT_addBits() :
+ *  can add up to 31 bits into `bitC`.
+ *  Note : does not check for register overflow ! */
+MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC,
+                            size_t value, unsigned nbBits)
+{
+    MEM_STATIC_ASSERT(BIT_MASK_SIZE == 32);
+    assert(nbBits < BIT_MASK_SIZE);
+    assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+    bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos;
+    bitC->bitPos += nbBits;
+}
+
+/*! BIT_addBitsFast() :
+ *  works only if `value` is _clean_,
+ *  meaning all high bits above nbBits are 0 */
+MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC,
+                                size_t value, unsigned nbBits)
+{
+    assert((value>>nbBits) == 0);
+    assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+    bitC->bitContainer |= value << bitC->bitPos;
+    bitC->bitPos += nbBits;
+}
+
+/*! BIT_flushBitsFast() :
+ *  assumption : bitContainer has not overflowed
+ *  unsafe version; does not check buffer overflow */
+MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC)
+{
+    size_t const nbBytes = bitC->bitPos >> 3;
+    assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+    MEM_writeLEST(bitC->ptr, bitC->bitContainer);
+    bitC->ptr += nbBytes;
+    assert(bitC->ptr <= bitC->endPtr);
+    bitC->bitPos &= 7;
+    bitC->bitContainer >>= nbBytes*8;
+}
+
+/*! BIT_flushBits() :
+ *  assumption : bitContainer has not overflowed
+ *  safe version; check for buffer overflow, and prevents it.
+ *  note : does not signal buffer overflow.
+ *  overflow will be revealed later on using BIT_closeCStream() */
+MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC)
+{
+    size_t const nbBytes = bitC->bitPos >> 3;
+    assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+    MEM_writeLEST(bitC->ptr, bitC->bitContainer);
+    bitC->ptr += nbBytes;
+    if (bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr;
+    bitC->bitPos &= 7;
+    bitC->bitContainer >>= nbBytes*8;
+}
+
+/*! BIT_closeCStream() :
+ *  @return : size of CStream, in bytes,
+ *            or 0 if it could not fit into dstBuffer */
+MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC)
+{
+    BIT_addBitsFast(bitC, 1, 1);   /* endMark */
+    BIT_flushBits(bitC);
+    if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */
+    return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0);
+}
+
+
+/*-********************************************************
+*  bitStream decoding
+**********************************************************/
+/*! BIT_initDStream() :
+ *  Initialize a BIT_DStream_t.
+ * `bitD` : a pointer to an already allocated BIT_DStream_t structure.
+ * `srcSize` must be the *exact* size of the bitStream, in bytes.
+ * @return : size of stream (== srcSize), or an errorCode if a problem is detected
+ */
+MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t
srcSize)
+{
+    if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); }
+
+    bitD->start = (const char*)srcBuffer;
+    bitD->limitPtr = bitD->start + sizeof(bitD->bitContainer);
+
+    if (srcSize >=  sizeof(bitD->bitContainer)) {  /* normal case */
+        bitD->ptr   = (const char*)srcBuffer + srcSize -
sizeof(bitD->bitContainer);
+        bitD->bitContainer = MEM_readLEST(bitD->ptr);
+        { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
+          bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;  /* ensures
bitsConsumed is always set */
+          if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ }
+    } else {
+        bitD->ptr   = bitD->start;
+        bitD->bitContainer = *(const BYTE*)(bitD->start);
+        switch(srcSize)
+        {
+        case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) <<
(sizeof(bitD->bitContainer)*8 - 16);
+                /* fall-through */
+
+        case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) <<
(sizeof(bitD->bitContainer)*8 - 24);
+                /* fall-through */
+
+        case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) <<
(sizeof(bitD->bitContainer)*8 - 32);
+                /* fall-through */
+
+        case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) <<
24;
+                /* fall-through */
+
+        case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) <<
16;
+                /* fall-through */
+
+        case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) <<
8;
+                /* fall-through */
+
+        default: break;
+        }
+        {   BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
+            bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
+            if (lastByte == 0) return ERROR(corruption_detected);  /* endMark not present
*/
+        }
+        bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8;
+    }
+
+    return srcSize;
+}
+
+MEM_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start)
+{
+    return bitContainer >> start;
+}
+
+MEM_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const
nbBits)
+{
+    U32 const regMask = sizeof(bitContainer)*8 - 1;
+    /* if start > regMask, bitstream is corrupted, and result is undefined */
+    assert(nbBits < BIT_MASK_SIZE);
+    return (bitContainer >> (start & regMask)) & BIT_mask[nbBits];
+}
+
+MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
+{
+    assert(nbBits < BIT_MASK_SIZE);
+    return bitContainer & BIT_mask[nbBits];
+}
+
+/*! BIT_lookBits() :
+ *  Provides next n bits from local register.
+ *  local register is not modified.
+ *  On 32-bits, maxNbBits==24.
+ *  On 64-bits, maxNbBits==56.
+ * @return : value extracted */
+MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits)
+{
+    /* arbitrate between double-shift and shift+mask */
+#if 1
+    /* if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8,
+     * bitstream is likely corrupted, and result is undefined */
+    return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) -
bitD->bitsConsumed - nbBits, nbBits);
+#else
... 20921 lines suppressed ...