[BTRFS] Upgrade to 1.1
authorPierre Schweitzer <pierre@reactos.org>
Sun, 16 Dec 2018 11:03:16 +0000 (12:03 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Sun, 16 Dec 2018 11:06:46 +0000 (12:06 +0100)
CORE-15452

61 files changed:
drivers/filesystems/btrfs/CMakeLists.txt
drivers/filesystems/btrfs/balance.c
drivers/filesystems/btrfs/btrfs.c
drivers/filesystems/btrfs/btrfs.h
drivers/filesystems/btrfs/btrfs.rc
drivers/filesystems/btrfs/btrfs_drv.h
drivers/filesystems/btrfs/btrfsioctl.h
drivers/filesystems/btrfs/compress.c
drivers/filesystems/btrfs/create.c
drivers/filesystems/btrfs/dirctrl.c
drivers/filesystems/btrfs/fastio.c
drivers/filesystems/btrfs/fileinfo.c
drivers/filesystems/btrfs/flushthread.c
drivers/filesystems/btrfs/free-space.c
drivers/filesystems/btrfs/fsctl.c
drivers/filesystems/btrfs/pnp.c
drivers/filesystems/btrfs/read.c
drivers/filesystems/btrfs/registry.c
drivers/filesystems/btrfs/reparse.c
drivers/filesystems/btrfs/scrub.c
drivers/filesystems/btrfs/search.c
drivers/filesystems/btrfs/send.c
drivers/filesystems/btrfs/treefuncs.c
drivers/filesystems/btrfs/volume.c
drivers/filesystems/btrfs/write.c
drivers/filesystems/btrfs/zstd/bitstream.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/compiler.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/cpu.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/debug.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/entropy_common.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/error_private.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/error_private.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/fse.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/fse_compress.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/fse_decompress.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/hist.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/hist.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/huf.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/huf_compress.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/huf_decompress.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/mem.h [new file with mode: 0755]
drivers/filesystems/btrfs/zstd/xxhash.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/xxhash.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_common.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_compress.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_compress_internal.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_decompress.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_double_fast.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_double_fast.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_errors.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_fast.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_fast.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_internal.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_lazy.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_lazy.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_ldm.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_ldm.h [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_opt.c [new file with mode: 0644]
drivers/filesystems/btrfs/zstd/zstd_opt.h [new file with mode: 0644]
media/doc/README.FSD

index 65e61d2..59fe956 100644 (file)
@@ -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)
index 3e36489..fe641f4 100644 (file)
@@ -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;
     }
index bdc2988..2582ee0 100644 (file)
 
 #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);
index 27e9b63..eede405 100644 (file)
@@ -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
index 50f7463..957792a 100644 (file)
@@ -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"
index 857555f..e6ea589 100644 (file)
@@ -74,6 +74,7 @@
 // #define DEBUG_LONG_MESSAGES
 // #define DEBUG_FLUSH_TIMES
 // #define DEBUG_STATS
+// #define DEBUG_CHUNK_LOCKS
 #define DEBUG_PARANOID
 #endif
 
 
 #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;
index 9c57c9c..a7f53b3 100644 (file)
@@ -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;
index 0a8a1f7..598171b 100644 (file)
 #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;
+}
index 4c4c5dd..1b4417e 100644 (file)
 
 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;
 }
index 64ab60f..4d8df55 100644 (file)
@@ -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)) {
index 9f4c504..9ee282d 100644 (file)
@@ -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;
 }
 
index 1d03a66..1c342b0 100644 (file)
 // 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;
 
index 9e95f82..6353abb 100644 (file)
@@ -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;
index 1ed2da9..f6248e9 100644 (file)
@@ -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);
index 6cc0f45..3b13e89 100644 (file)
@@ -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;
index 5a7e91b..fbb535f 100644 (file)
@@ -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) {
index 4384d65..2eb19b1 100644 (file)
@@ -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);
 
index 05f66f5..84196df 100644 (file)
@@ -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(&notrimus, 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) {
index 8e8d319..957ad07 100644 (file)
@@ -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:
index 9dd7c20..f2d3d5e 100644 (file)
@@ -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;
     }
index bec1aba..7333a1e 100644 (file)
@@ -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);
     }
 }
index a865c62..260612a 100644 (file)
@@ -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;
     }
index e2f6001..9785e12 100644 (file)
@@ -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;
                                 }
 
index 5f7516a..fa33221 100644 (file)
@@ -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)) {
index 6292d7e..ed1df4c 100644 (file)
@@ -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 (file)
index 0000000..ef89b98
--- /dev/null
@@ -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
+    /* this code path is slower on my os-x laptop */
+    U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
+    return ((bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> 1) >> ((regMask-nbBits) & regMask);
+#endif
+}
+
+/*! BIT_lookBitsFast() :
+ *  unsafe version; only works if nbBits >= 1 */
+MEM_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t* bitD, U32 nbBits)
+{
+    U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
+    assert(nbBits >= 1);
+    return (bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> (((regMask+1)-nbBits) & regMask);
+}
+
+MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits)
+{
+    bitD->bitsConsumed += nbBits;
+}
+
+/*! BIT_readBits() :
+ *  Read (consume) next n bits from local register and update.
+ *  Pay attention to not read more than nbBits contained into local register.
+ * @return : extracted value. */
+MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits)
+{
+    size_t const value = BIT_lookBits(bitD, nbBits);
+    BIT_skipBits(bitD, nbBits);
+    return value;
+}
+
+/*! BIT_readBitsFast() :
+ *  unsafe version; only works only if nbBits >= 1 */
+MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
+{
+    size_t const value = BIT_lookBitsFast(bitD, nbBits);
+    assert(nbBits >= 1);
+    BIT_skipBits(bitD, nbBits);
+    return value;
+}
+
+/*! BIT_reloadDStream() :
+ *  Refill `bitD` from buffer previously set in BIT_initDStream() .
+ *  This function is safe, it guarantees it will not read beyond src buffer.
+ * @return : status of `BIT_DStream_t` internal register.
+ *           when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
+MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
+{
+    if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8))  /* overflow detected, like end of stream */
+        return BIT_DStream_overflow;
+
+    if (bitD->ptr >= bitD->limitPtr) {
+        bitD->ptr -= bitD->bitsConsumed >> 3;
+        bitD->bitsConsumed &= 7;
+        bitD->bitContainer = MEM_readLEST(bitD->ptr);
+        return BIT_DStream_unfinished;
+    }
+    if (bitD->ptr == bitD->start) {
+        if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer;
+        return BIT_DStream_completed;
+    }
+    /* start < ptr < limitPtr */
+    {   U32 nbBytes = bitD->bitsConsumed >> 3;
+        BIT_DStream_status result = BIT_DStream_unfinished;
+        if (bitD->ptr - nbBytes < bitD->start) {
+            nbBytes = (U32)(bitD->ptr - bitD->start);  /* ptr > start */
+            result = BIT_DStream_endOfBuffer;
+        }
+        bitD->ptr -= nbBytes;
+        bitD->bitsConsumed -= nbBytes*8;
+        bitD->bitContainer = MEM_readLEST(bitD->ptr);   /* reminder : srcSize > sizeof(bitD->bitContainer), otherwise bitD->ptr == bitD->start */
+        return result;
+    }
+}
+
+/*! BIT_endOfDStream() :
+ * @return : 1 if DStream has _exactly_ reached its end (all bits consumed).
+ */
+MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream)
+{
+    return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8));
+}
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* BITSTREAM_H_MODULE */
diff --git a/drivers/filesystems/btrfs/zstd/compiler.h b/drivers/filesystems/btrfs/zstd/compiler.h
new file mode 100644 (file)
index 0000000..07f875e
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_COMPILER_H
+#define ZSTD_COMPILER_H
+
+/*-*******************************************************
+*  Compiler specifics
+*********************************************************/
+/* force inlining */
+#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#  define INLINE_KEYWORD inline
+#else
+#  define INLINE_KEYWORD
+#endif
+
+#if defined(__GNUC__)
+#  define FORCE_INLINE_ATTR __attribute__((always_inline))
+#elif defined(_MSC_VER)
+#  define FORCE_INLINE_ATTR __forceinline
+#else
+#  define FORCE_INLINE_ATTR
+#endif
+
+/**
+ * FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant
+ * parameters. They must be inlined for the compiler to elimininate the constant
+ * branches.
+ */
+#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR
+/**
+ * HINT_INLINE is used to help the compiler generate better code. It is *not*
+ * used for "templates", so it can be tweaked based on the compilers
+ * performance.
+ *
+ * gcc-4.8 and gcc-4.9 have been shown to benefit from leaving off the
+ * always_inline attribute.
+ *
+ * clang up to 5.0.0 (trunk) benefit tremendously from the always_inline
+ * attribute.
+ */
+#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5
+#  define HINT_INLINE static INLINE_KEYWORD
+#else
+#  define HINT_INLINE static INLINE_KEYWORD FORCE_INLINE_ATTR
+#endif
+
+/* force no inlining */
+#ifdef _MSC_VER
+#  define FORCE_NOINLINE static __declspec(noinline)
+#else
+#  ifdef __GNUC__
+#    define FORCE_NOINLINE static __attribute__((__noinline__))
+#  else
+#    define FORCE_NOINLINE static
+#  endif
+#endif
+
+/* target attribute */
+#ifndef __has_attribute
+  #define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */
+#endif
+#if defined(__GNUC__)
+#  define TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
+#else
+#  define TARGET_ATTRIBUTE(target)
+#endif
+
+/* Enable runtime BMI2 dispatch based on the CPU.
+ * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default.
+ */
+#ifndef DYNAMIC_BMI2
+  #if ((defined(__clang__) && __has_attribute(__target__)) \
+      || (defined(__GNUC__) \
+          && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \
+      && (defined(__x86_64__) || defined(_M_X86)) \
+      && !defined(__BMI2__)
+  #  define DYNAMIC_BMI2 1
+  #else
+  #  define DYNAMIC_BMI2 0
+  #endif
+#endif
+
+/* prefetch
+ * can be disabled, by declaring NO_PREFETCH macro
+ * All prefetch invocations use a single default locality 2,
+ * generating instruction prefetcht1,
+ * which, according to Intel, means "load data into L2 cache".
+ * This is a good enough "middle ground" for the time being,
+ * though in theory, it would be better to specialize locality depending on data being prefetched.
+ * Tests could not determine any sensible difference based on locality value. */
+#if defined(NO_PREFETCH)
+#  define PREFETCH(ptr)     (void)(ptr)  /* disabled */
+#else
+#  if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86))  /* _mm_prefetch() is not defined outside of x86/x64 */
+#    include <mmintrin.h>   /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
+#    define PREFETCH(ptr)   _mm_prefetch((const char*)(ptr), _MM_HINT_T1)
+#  elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
+#    define PREFETCH(ptr)   __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */)
+#  else
+#    define PREFETCH(ptr)   (void)(ptr)  /* disabled */
+#  endif
+#endif  /* NO_PREFETCH */
+
+#define CACHELINE_SIZE 64
+
+#define PREFETCH_AREA(p, s)  {            \
+    const char* const _ptr = (const char*)(p);  \
+    size_t const _size = (size_t)(s);     \
+    size_t _pos;                          \
+    for (_pos=0; _pos<_size; _pos+=CACHELINE_SIZE) {  \
+        PREFETCH(_ptr + _pos);            \
+    }                                     \
+}
+
+/* disable warnings */
+#ifdef _MSC_VER    /* Visual Studio */
+#  include <intrin.h>                    /* For Visual 2005 */
+#  pragma warning(disable : 4100)        /* disable: C4100: unreferenced formal parameter */
+#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
+#  pragma warning(disable : 4204)        /* disable: C4204: non-constant aggregate initializer */
+#  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
+#  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
+#endif
+
+#endif /* ZSTD_COMPILER_H */
diff --git a/drivers/filesystems/btrfs/zstd/cpu.h b/drivers/filesystems/btrfs/zstd/cpu.h
new file mode 100644 (file)
index 0000000..eeb428a
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2018-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_COMMON_CPU_H
+#define ZSTD_COMMON_CPU_H
+
+/**
+ * Implementation taken from folly/CpuId.h
+ * https://github.com/facebook/folly/blob/master/folly/CpuId.h
+ */
+
+#include <string.h>
+
+#include "mem.h"
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
+typedef struct {
+    U32 f1c;
+    U32 f1d;
+    U32 f7b;
+    U32 f7c;
+} ZSTD_cpuid_t;
+
+MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) {
+    U32 f1c = 0;
+    U32 f1d = 0;
+    U32 f7b = 0;
+    U32 f7c = 0;
+#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
+    int reg[4];
+    __cpuid((int*)reg, 0);
+    {
+        int const n = reg[0];
+        if (n >= 1) {
+            __cpuid((int*)reg, 1);
+            f1c = (U32)reg[2];
+            f1d = (U32)reg[3];
+        }
+        if (n >= 7) {
+            __cpuidex((int*)reg, 7, 0);
+            f7b = (U32)reg[1];
+            f7c = (U32)reg[2];
+        }
+    }
+#elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && defined(__GNUC__)
+    /* The following block like the normal cpuid branch below, but gcc
+     * reserves ebx for use of its pic register so we must specially
+     * handle the save and restore to avoid clobbering the register
+     */
+    U32 n;
+    __asm__(
+        "pushl %%ebx\n\t"
+        "cpuid\n\t"
+        "popl %%ebx\n\t"
+        : "=a"(n)
+        : "a"(0)
+        : "ecx", "edx");
+    if (n >= 1) {
+      U32 f1a;
+      __asm__(
+          "pushl %%ebx\n\t"
+          "cpuid\n\t"
+          "popl %%ebx\n\t"
+          : "=a"(f1a), "=c"(f1c), "=d"(f1d)
+          : "a"(1));
+    }
+    if (n >= 7) {
+      __asm__(
+          "pushl %%ebx\n\t"
+          "cpuid\n\t"
+          "movl %%ebx, %%eax\n\r"
+          "popl %%ebx"
+          : "=a"(f7b), "=c"(f7c)
+          : "a"(7), "c"(0)
+          : "edx");
+    }
+#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__)
+    U32 n;
+    __asm__("cpuid" : "=a"(n) : "a"(0) : "ebx", "ecx", "edx");
+    if (n >= 1) {
+      U32 f1a;
+      __asm__("cpuid" : "=a"(f1a), "=c"(f1c), "=d"(f1d) : "a"(1) : "ebx");
+    }
+    if (n >= 7) {
+      U32 f7a;
+      __asm__("cpuid"
+              : "=a"(f7a), "=b"(f7b), "=c"(f7c)
+              : "a"(7), "c"(0)
+              : "edx");
+    }
+#endif
+    {
+        ZSTD_cpuid_t cpuid;
+        cpuid.f1c = f1c;
+        cpuid.f1d = f1d;
+        cpuid.f7b = f7b;
+        cpuid.f7c = f7c;
+        return cpuid;
+    }
+}
+
+#define X(name, r, bit)                                                        \
+  MEM_STATIC int ZSTD_cpuid_##name(ZSTD_cpuid_t const cpuid) {                 \
+    return ((cpuid.r) & (1U << bit)) != 0;                                     \
+  }
+
+/* cpuid(1): Processor Info and Feature Bits. */
+#define C(name, bit) X(name, f1c, bit)
+  C(sse3, 0)
+  C(pclmuldq, 1)
+  C(dtes64, 2)
+  C(monitor, 3)
+  C(dscpl, 4)
+  C(vmx, 5)
+  C(smx, 6)
+  C(eist, 7)
+  C(tm2, 8)
+  C(ssse3, 9)
+  C(cnxtid, 10)
+  C(fma, 12)
+  C(cx16, 13)
+  C(xtpr, 14)
+  C(pdcm, 15)
+  C(pcid, 17)
+  C(dca, 18)
+  C(sse41, 19)
+  C(sse42, 20)
+  C(x2apic, 21)
+  C(movbe, 22)
+  C(popcnt, 23)
+  C(tscdeadline, 24)
+  C(aes, 25)
+  C(xsave, 26)
+  C(osxsave, 27)
+  C(avx, 28)
+  C(f16c, 29)
+  C(rdrand, 30)
+#undef C
+#define D(name, bit) X(name, f1d, bit)
+  D(fpu, 0)
+  D(vme, 1)
+  D(de, 2)
+  D(pse, 3)
+  D(tsc, 4)
+  D(msr, 5)
+  D(pae, 6)
+  D(mce, 7)
+  D(cx8, 8)
+  D(apic, 9)
+  D(sep, 11)
+  D(mtrr, 12)
+  D(pge, 13)
+  D(mca, 14)
+  D(cmov, 15)
+  D(pat, 16)
+  D(pse36, 17)
+  D(psn, 18)
+  D(clfsh, 19)
+  D(ds, 21)
+  D(acpi, 22)
+  D(mmx, 23)
+  D(fxsr, 24)
+  D(sse, 25)
+  D(sse2, 26)
+  D(ss, 27)
+  D(htt, 28)
+  D(tm, 29)
+  D(pbe, 31)
+#undef D
+
+/* cpuid(7): Extended Features. */
+#define B(name, bit) X(name, f7b, bit)
+  B(bmi1, 3)
+  B(hle, 4)
+  B(avx2, 5)
+  B(smep, 7)
+  B(bmi2, 8)
+  B(erms, 9)
+  B(invpcid, 10)
+  B(rtm, 11)
+  B(mpx, 14)
+  B(avx512f, 16)
+  B(avx512dq, 17)
+  B(rdseed, 18)
+  B(adx, 19)
+  B(smap, 20)
+  B(avx512ifma, 21)
+  B(pcommit, 22)
+  B(clflushopt, 23)
+  B(clwb, 24)
+  B(avx512pf, 26)
+  B(avx512er, 27)
+  B(avx512cd, 28)
+  B(sha, 29)
+  B(avx512bw, 30)
+  B(avx512vl, 31)
+#undef B
+#define C(name, bit) X(name, f7c, bit)
+  C(prefetchwt1, 0)
+  C(avx512vbmi, 1)
+#undef C
+
+#undef X
+
+#endif /* ZSTD_COMMON_CPU_H */
diff --git a/drivers/filesystems/btrfs/zstd/debug.h b/drivers/filesystems/btrfs/zstd/debug.h
new file mode 100644 (file)
index 0000000..0c04ad2
--- /dev/null
@@ -0,0 +1,123 @@
+/* ******************************************************************
+   debug
+   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
+****************************************************************** */
+
+
+/*
+ * The purpose of this header is to enable debug functions.
+ * They regroup assert(), DEBUGLOG() and RAWLOG() for run-time,
+ * and DEBUG_STATIC_ASSERT() for compile-time.
+ *
+ * By default, DEBUGLEVEL==0, which means run-time debug is disabled.
+ *
+ * Level 1 enables assert() only.
+ * Starting level 2, traces can be generated and pushed to stderr.
+ * The higher the level, the more verbose the traces.
+ *
+ * It's possible to dynamically adjust level using variable g_debug_level,
+ * which is only declared if DEBUGLEVEL>=2,
+ * and is a global variable, not multi-thread protected (use with care)
+ */
+
+#ifndef DEBUG_H_12987983217
+#define DEBUG_H_12987983217
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/* static assert is triggered at compile time, leaving no runtime artefact,
+ * but can only work with compile-time constants.
+ * This variant can only be used inside a function. */
+#define DEBUG_STATIC_ASSERT(c) (void)sizeof(char[(c) ? 1 : -1])
+
+
+/* DEBUGLEVEL is expected to be defined externally,
+ * typically through compiler command line.
+ * Value must be a number. */
+#ifndef DEBUGLEVEL
+#  define DEBUGLEVEL 0
+#endif
+
+/* recommended values for DEBUGLEVEL :
+ * 0 : no debug, all run-time functions disabled
+ * 1 : no display, enables assert() only
+ * 2 : reserved, for currently active debug path
+ * 3 : events once per object lifetime (CCtx, CDict, etc.)
+ * 4 : events once per frame
+ * 5 : events once per block
+ * 6 : events once per sequence (verbose)
+ * 7+: events at every position (*very* verbose)
+ *
+ * It's generally inconvenient to output traces > 5.
+ * In which case, it's possible to selectively enable higher verbosity levels
+ * by modifying g_debug_level.
+ */
+
+#if (DEBUGLEVEL>=1)
+#  include <assert.h>
+#else
+#  ifndef assert   /* assert may be already defined, due to prior #include <assert.h> */
+#    define assert(condition) ((void)0)   /* disable assert (default) */
+#  endif
+#endif
+
+#if (DEBUGLEVEL>=2)
+#  include <stdio.h>
+extern int g_debuglevel; /* here, this variable is only declared,
+                           it actually lives in debug.c,
+                           and is shared by the whole process.
+                           It's typically used to enable very verbose levels
+                           on selective conditions (such as position in src) */
+
+#  define RAWLOG(l, ...) {                                      \
+                if (l<=g_debuglevel) {                          \
+                    fprintf(stderr, __VA_ARGS__);               \
+            }   }
+#  define DEBUGLOG(l, ...) {                                    \
+                if (l<=g_debuglevel) {                          \
+                    fprintf(stderr, __FILE__ ": " __VA_ARGS__); \
+                    fprintf(stderr, " \n");                     \
+            }   }
+#else
+#  define RAWLOG(l, ...)      {}    /* disabled */
+#  define DEBUGLOG(l, ...)    {}    /* disabled */
+#endif
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* DEBUG_H_12987983217 */
diff --git a/drivers/filesystems/btrfs/zstd/entropy_common.c b/drivers/filesystems/btrfs/zstd/entropy_common.c
new file mode 100644 (file)
index 0000000..b12944e
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+   Common functions of New Generation Entropy library
+   Copyright (C) 2016, 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 :
+    - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
+    - Public forum : https://groups.google.com/forum/#!forum/lz4c
+*************************************************************************** */
+
+/* *************************************
+*  Dependencies
+***************************************/
+#include "mem.h"
+#include "error_private.h"       /* ERR_*, ERROR */
+#define FSE_STATIC_LINKING_ONLY  /* FSE_MIN_TABLELOG */
+#include "fse.h"
+#define HUF_STATIC_LINKING_ONLY  /* HUF_TABLELOG_ABSOLUTEMAX */
+#include "huf.h"
+
+
+/*===   Version   ===*/
+unsigned FSE_versionNumber(void) { return FSE_VERSION_NUMBER; }
+
+
+/*===   Error Management   ===*/
+unsigned FSE_isError(size_t code) { return ERR_isError(code); }
+const char* FSE_getErrorName(size_t code) { return ERR_getErrorName(code); }
+
+unsigned HUF_isError(size_t code) { return ERR_isError(code); }
+const char* HUF_getErrorName(size_t code) { return ERR_getErrorName(code); }
+
+
+/*-**************************************************************
+*  FSE NCount encoding-decoding
+****************************************************************/
+size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
+                 const void* headerBuffer, size_t hbSize)
+{
+    const BYTE* const istart = (const BYTE*) headerBuffer;
+    const BYTE* const iend = istart + hbSize;
+    const BYTE* ip = istart;
+    int nbBits;
+    int remaining;
+    int threshold;
+    U32 bitStream;
+    int bitCount;
+    unsigned charnum = 0;
+    int previous0 = 0;
+
+    if (hbSize < 4) {
+        /* This function only works when hbSize >= 4 */
+        char buffer[4];
+        memset(buffer, 0, sizeof(buffer));
+        memcpy(buffer, headerBuffer, hbSize);
+        {   size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr,
+                                                    buffer, sizeof(buffer));
+            if (FSE_isError(countSize)) return countSize;
+            if (countSize > hbSize) return ERROR(corruption_detected);
+            return countSize;
+    }   }
+    assert(hbSize >= 4);
+
+    /* init */
+    memset(normalizedCounter, 0, (*maxSVPtr+1) * sizeof(normalizedCounter[0]));   /* all symbols not present in NCount have a frequency of 0 */
+    bitStream = MEM_readLE32(ip);
+    nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG;   /* extract tableLog */
+    if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge);
+    bitStream >>= 4;
+    bitCount = 4;
+    *tableLogPtr = nbBits;
+    remaining = (1<<nbBits)+1;
+    threshold = 1<<nbBits;
+    nbBits++;
+
+    while ((remaining>1) & (charnum<=*maxSVPtr)) {
+        if (previous0) {
+            unsigned n0 = charnum;
+            while ((bitStream & 0xFFFF) == 0xFFFF) {
+                n0 += 24;
+                if (ip < iend-5) {
+                    ip += 2;
+                    bitStream = MEM_readLE32(ip) >> bitCount;
+                } else {
+                    bitStream >>= 16;
+                    bitCount   += 16;
+            }   }
+            while ((bitStream & 3) == 3) {
+                n0 += 3;
+                bitStream >>= 2;
+                bitCount += 2;
+            }
+            n0 += bitStream & 3;
+            bitCount += 2;
+            if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall);
+            while (charnum < n0) normalizedCounter[charnum++] = 0;
+            if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
+                assert((bitCount >> 3) <= 3); /* For first condition to work */
+                ip += bitCount>>3;
+                bitCount &= 7;
+                bitStream = MEM_readLE32(ip) >> bitCount;
+            } else {
+                bitStream >>= 2;
+        }   }
+        {   int const max = (2*threshold-1) - remaining;
+            int count;
+
+            if ((bitStream & (threshold-1)) < (U32)max) {
+                count = bitStream & (threshold-1);
+                bitCount += nbBits-1;
+            } else {
+                count = bitStream & (2*threshold-1);
+                if (count >= threshold) count -= max;
+                bitCount += nbBits;
+            }
+
+            count--;   /* extra accuracy */
+            remaining -= count < 0 ? -count : count;   /* -1 means +1 */
+            normalizedCounter[charnum++] = (short)count;
+            previous0 = !count;
+            while (remaining < threshold) {
+                nbBits--;
+                threshold >>= 1;
+            }
+
+            if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
+                ip += bitCount>>3;
+                bitCount &= 7;
+            } else {
+                bitCount -= (int)(8 * (iend - 4 - ip));
+                ip = iend - 4;
+            }
+            bitStream = MEM_readLE32(ip) >> (bitCount & 31);
+    }   }   /* while ((remaining>1) & (charnum<=*maxSVPtr)) */
+    if (remaining != 1) return ERROR(corruption_detected);
+    if (bitCount > 32) return ERROR(corruption_detected);
+    *maxSVPtr = charnum-1;
+
+    ip += (bitCount+7)>>3;
+    return ip-istart;
+}
+
+
+/*! HUF_readStats() :
+    Read compact Huffman tree, saved by HUF_writeCTable().
+    `huffWeight` is destination buffer.
+    `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32.
+    @return : size read from `src` , or an error Code .
+    Note : Needed by HUF_readCTable() and HUF_readDTableX?() .
+*/
+size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats,
+                     U32* nbSymbolsPtr, U32* tableLogPtr,
+                     const void* src, size_t srcSize)
+{
+    U32 weightTotal;
+    const BYTE* ip = (const BYTE*) src;
+    size_t iSize;
+    size_t oSize;
+
+    if (!srcSize) return ERROR(srcSize_wrong);
+    iSize = ip[0];
+    /* memset(huffWeight, 0, hwSize);   *//* is not necessary, even though some analyzer complain ... */
+
+    if (iSize >= 128) {  /* special header */
+        oSize = iSize - 127;
+        iSize = ((oSize+1)/2);
+        if (iSize+1 > srcSize) return ERROR(srcSize_wrong);
+        if (oSize >= hwSize) return ERROR(corruption_detected);
+        ip += 1;
+        {   U32 n;
+            for (n=0; n<oSize; n+=2) {
+                huffWeight[n]   = ip[n/2] >> 4;
+                huffWeight[n+1] = ip[n/2] & 15;
+    }   }   }
+    else  {   /* header compressed with FSE (normal case) */
+        FSE_DTable fseWorkspace[FSE_DTABLE_SIZE_U32(6)];  /* 6 is max possible tableLog for HUF header (maybe even 5, to be tested) */
+        if (iSize+1 > srcSize) return ERROR(srcSize_wrong);
+        oSize = FSE_decompress_wksp(huffWeight, hwSize-1, ip+1, iSize, fseWorkspace, 6);   /* max (hwSize-1) values decoded, as last one is implied */
+        if (FSE_isError(oSize)) return oSize;
+    }
+
+    /* collect weight stats */
+    memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32));
+    weightTotal = 0;
+    {   U32 n; for (n=0; n<oSize; n++) {
+            if (huffWeight[n] >= HUF_TABLELOG_MAX) return ERROR(corruption_detected);
+            rankStats[huffWeight[n]]++;
+            weightTotal += (1 << huffWeight[n]) >> 1;
+    }   }
+    if (weightTotal == 0) return ERROR(corruption_detected);
+
+    /* get last non-null symbol weight (implied, total must be 2^n) */
+    {   U32 const tableLog = BIT_highbit32(weightTotal) + 1;
+        if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected);
+        *tableLogPtr = tableLog;
+        /* determine last weight */
+        {   U32 const total = 1 << tableLog;
+            U32 const rest = total - weightTotal;
+            U32 const verif = 1 << BIT_highbit32(rest);
+            U32 const lastWeight = BIT_highbit32(rest) + 1;
+            if (verif != rest) return ERROR(corruption_detected);    /* last value must be a clean power of 2 */
+            huffWeight[oSize] = (BYTE)lastWeight;
+            rankStats[lastWeight]++;
+    }   }
+
+    /* check tree construction validity */
+    if ((rankStats[1] < 2) || (rankStats[1] & 1)) return ERROR(corruption_detected);   /* by construction : at least 2 elts of rank 1, must be even */
+
+    /* results */
+    *nbSymbolsPtr = (U32)(oSize+1);
+    return iSize+1;
+}
diff --git a/drivers/filesystems/btrfs/zstd/error_private.c b/drivers/filesystems/btrfs/zstd/error_private.c
new file mode 100644 (file)
index 0000000..d004ee6
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* The purpose of this file is to have a single list of error strings embedded in binary */
+
+#include "error_private.h"
+
+const char* ERR_getErrorString(ERR_enum code)
+{
+    static const char* const notErrorCode = "Unspecified error code";
+    switch( code )
+    {
+    case PREFIX(no_error): return "No error detected";
+    case PREFIX(GENERIC):  return "Error (generic)";
+    case PREFIX(prefix_unknown): return "Unknown frame descriptor";
+    case PREFIX(version_unsupported): return "Version not supported";
+    case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
+    case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding";
+    case PREFIX(corruption_detected): return "Corrupted block detected";
+    case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
+    case PREFIX(parameter_unsupported): return "Unsupported parameter";
+    case PREFIX(parameter_outOfBound): return "Parameter is out of bound";
+    case PREFIX(init_missing): return "Context should be init first";
+    case PREFIX(memory_allocation): return "Allocation error : not enough memory";
+    case PREFIX(workSpace_tooSmall): return "workSpace buffer is not large enough";
+    case PREFIX(stage_wrong): return "Operation not authorized at current processing stage";
+    case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported";
+    case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large";
+    case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small";
+    case PREFIX(dictionary_corrupted): return "Dictionary is corrupted";
+    case PREFIX(dictionary_wrong): return "Dictionary mismatch";
+    case PREFIX(dictionaryCreation_failed): return "Cannot create Dictionary from provided samples";
+    case PREFIX(dstSize_tooSmall): return "Destination buffer is too small";
+    case PREFIX(srcSize_wrong): return "Src size is incorrect";
+        /* following error codes are not stable and may be removed or changed in a future version */
+    case PREFIX(frameIndex_tooLarge): return "Frame index is too large";
+    case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking";
+    case PREFIX(maxCode):
+    default: return notErrorCode;
+    }
+}
diff --git a/drivers/filesystems/btrfs/zstd/error_private.h b/drivers/filesystems/btrfs/zstd/error_private.h
new file mode 100644 (file)
index 0000000..0d2fa7e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* Note : this module is expected to remain private, do not expose it */
+
+#ifndef ERROR_H_MODULE
+#define ERROR_H_MODULE
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/* ****************************************
+*  Dependencies
+******************************************/
+#include <stddef.h>        /* size_t */
+#include "zstd_errors.h"  /* enum list */
+
+
+/* ****************************************
+*  Compiler-specific
+******************************************/
+#if defined(__GNUC__)
+#  define ERR_STATIC static __attribute__((unused))
+#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+#  define ERR_STATIC static inline
+#elif defined(_MSC_VER)
+#  define ERR_STATIC static __inline
+#else
+#  define ERR_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
+#endif
+
+
+/*-****************************************
+*  Customization (error_public.h)
+******************************************/
+typedef ZSTD_ErrorCode ERR_enum;
+#define PREFIX(name) ZSTD_error_##name
+
+
+/*-****************************************
+*  Error codes handling
+******************************************/
+#undef ERROR   /* reported already defined on VS 2015 (Rich Geldreich) */
+#define ERROR(name) ZSTD_ERROR(name)
+#define ZSTD_ERROR(name) ((size_t)-PREFIX(name))
+
+ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
+
+ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) { if (!ERR_isError(code)) return (ERR_enum)0; return (ERR_enum) (0-code); }
+
+
+/*-****************************************
+*  Error Strings
+******************************************/
+
+const char* ERR_getErrorString(ERR_enum code);   /* error_private.c */
+
+ERR_STATIC const char* ERR_getErrorName(size_t code)
+{
+    return ERR_getErrorString(ERR_getErrorCode(code));
+}
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ERROR_H_MODULE */
diff --git a/drivers/filesystems/btrfs/zstd/fse.h b/drivers/filesystems/btrfs/zstd/fse.h
new file mode 100644 (file)
index 0000000..a5a6b6d
--- /dev/null
@@ -0,0 +1,708 @@
+/* ******************************************************************
+   FSE : Finite State Entropy codec
+   Public Prototypes declaration
+   Copyright (C) 2013-2016, 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
+****************************************************************** */
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef FSE_H
+#define FSE_H
+
+
+/*-*****************************************
+*  Dependencies
+******************************************/
+#include <stddef.h>    /* size_t, ptrdiff_t */
+
+
+/*-*****************************************
+*  FSE_PUBLIC_API : control library symbols visibility
+******************************************/
+#if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4)
+#  define FSE_PUBLIC_API __attribute__ ((visibility ("default")))
+#elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1)   /* Visual expected */
+#  define FSE_PUBLIC_API __declspec(dllexport)
+#elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1)
+#  define FSE_PUBLIC_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+#  define FSE_PUBLIC_API
+#endif
+
+/*------   Version   ------*/
+#define FSE_VERSION_MAJOR    0
+#define FSE_VERSION_MINOR    9
+#define FSE_VERSION_RELEASE  0
+
+#define FSE_LIB_VERSION FSE_VERSION_MAJOR.FSE_VERSION_MINOR.FSE_VERSION_RELEASE
+#define FSE_QUOTE(str) #str
+#define FSE_EXPAND_AND_QUOTE(str) FSE_QUOTE(str)
+#define FSE_VERSION_STRING FSE_EXPAND_AND_QUOTE(FSE_LIB_VERSION)
+
+#define FSE_VERSION_NUMBER  (FSE_VERSION_MAJOR *100*100 + FSE_VERSION_MINOR *100 + FSE_VERSION_RELEASE)
+FSE_PUBLIC_API unsigned FSE_versionNumber(void);   /**< library version number; to be used when checking dll version */
+
+
+/*-****************************************
+*  FSE simple functions
+******************************************/
+/*! FSE_compress() :
+    Compress content of buffer 'src', of size 'srcSize', into destination buffer 'dst'.
+    'dst' buffer must be already allocated. Compression runs faster is dstCapacity >= FSE_compressBound(srcSize).
+    @return : size of compressed data (<= dstCapacity).
+    Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
+                     if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression instead.
+                     if FSE_isError(return), compression failed (more details using FSE_getErrorName())
+*/
+FSE_PUBLIC_API size_t FSE_compress(void* dst, size_t dstCapacity,
+                             const void* src, size_t srcSize);
+
+/*! FSE_decompress():
+    Decompress FSE data from buffer 'cSrc', of size 'cSrcSize',
+    into already allocated destination buffer 'dst', of size 'dstCapacity'.
+    @return : size of regenerated data (<= maxDstSize),
+              or an error code, which can be tested using FSE_isError() .
+
+    ** Important ** : FSE_decompress() does not decompress non-compressible nor RLE data !!!
+    Why ? : making this distinction requires a header.
+    Header management is intentionally delegated to the user layer, which can better manage special cases.
+*/
+FSE_PUBLIC_API size_t FSE_decompress(void* dst,  size_t dstCapacity,
+                               const void* cSrc, size_t cSrcSize);
+
+
+/*-*****************************************
+*  Tool functions
+******************************************/
+FSE_PUBLIC_API size_t FSE_compressBound(size_t size);       /* maximum compressed size */
+
+/* Error Management */
+FSE_PUBLIC_API unsigned    FSE_isError(size_t code);        /* tells if a return value is an error code */
+FSE_PUBLIC_API const char* FSE_getErrorName(size_t code);   /* provides error code string (useful for debugging) */
+
+
+/*-*****************************************
+*  FSE advanced functions
+******************************************/
+/*! FSE_compress2() :
+    Same as FSE_compress(), but allows the selection of 'maxSymbolValue' and 'tableLog'
+    Both parameters can be defined as '0' to mean : use default value
+    @return : size of compressed data
+    Special values : if return == 0, srcData is not compressible => Nothing is stored within cSrc !!!
+                     if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression.
+                     if FSE_isError(return), it's an error code.
+*/
+FSE_PUBLIC_API size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
+
+
+/*-*****************************************
+*  FSE detailed API
+******************************************/
+/*!
+FSE_compress() does the following:
+1. count symbol occurrence from source[] into table count[] (see hist.h)
+2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog)
+3. save normalized counters to memory buffer using writeNCount()
+4. build encoding table 'CTable' from normalized counters
+5. encode the data stream using encoding table 'CTable'
+
+FSE_decompress() does the following:
+1. read normalized counters with readNCount()
+2. build decoding table 'DTable' from normalized counters
+3. decode the data stream using decoding table 'DTable'
+
+The following API allows targeting specific sub-functions for advanced tasks.
+For example, it's possible to compress several blocks using the same 'CTable',
+or to save and provide normalized distribution using external method.
+*/
+
+/* *** COMPRESSION *** */
+
+/*! FSE_optimalTableLog():
+    dynamically downsize 'tableLog' when conditions are met.
+    It saves CPU time, by using smaller tables, while preserving or even improving compression ratio.
+    @return : recommended tableLog (necessarily <= 'maxTableLog') */
+FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
+
+/*! FSE_normalizeCount():
+    normalize counts so that sum(count[]) == Power_of_2 (2^tableLog)
+    'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1).
+    @return : tableLog,
+              or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_normalizeCount(short* normalizedCounter, unsigned tableLog,
+                    const unsigned* count, size_t srcSize, unsigned maxSymbolValue);
+
+/*! FSE_NCountWriteBound():
+    Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'.
+    Typically useful for allocation purpose. */
+FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog);
+
+/*! FSE_writeNCount():
+    Compactly save 'normalizedCounter' into 'buffer'.
+    @return : size of the compressed table,
+              or an errorCode, which can be tested using FSE_isError(). */
+FSE_PUBLIC_API size_t FSE_writeNCount (void* buffer, size_t bufferSize,
+                                 const short* normalizedCounter,
+                                 unsigned maxSymbolValue, unsigned tableLog);
+
+/*! Constructor and Destructor of FSE_CTable.
+    Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */
+typedef unsigned FSE_CTable;   /* don't allocate that. It's only meant to be more restrictive than void* */
+FSE_PUBLIC_API FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog);
+FSE_PUBLIC_API void        FSE_freeCTable (FSE_CTable* ct);
+
+/*! FSE_buildCTable():
+    Builds `ct`, which must be already allocated, using FSE_createCTable().
+    @return : 0, or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog);
+
+/*! FSE_compress_usingCTable():
+    Compress `src` using `ct` into `dst` which must be already allocated.
+    @return : size of compressed data (<= `dstCapacity`),
+              or 0 if compressed data could not fit into `dst`,
+              or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_compress_usingCTable (void* dst, size_t dstCapacity, const void* src, size_t srcSize, const FSE_CTable* ct);
+
+/*!
+Tutorial :
+----------
+The first step is to count all symbols. FSE_count() does this job very fast.
+Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells.
+'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0]
+maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value)
+FSE_count() will return the number of occurrence of the most frequent symbol.
+This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility.
+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()).
+
+The next step is to normalize the frequencies.
+FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'.
+It also guarantees a minimum of 1 to any Symbol with frequency >= 1.
+You can use 'tableLog'==0 to mean "use default tableLog value".
+If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(),
+which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default").
+
+The result of FSE_normalizeCount() will be saved into a table,
+called 'normalizedCounter', which is a table of signed short.
+'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells.
+The return value is tableLog if everything proceeded as expected.
+It is 0 if there is a single symbol within distribution.
+If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()).
+
+'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount().
+'buffer' must be already allocated.
+For guaranteed success, buffer size must be at least FSE_headerBound().
+The result of the function is the number of bytes written into 'buffer'.
+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small).
+
+'normalizedCounter' can then be used to create the compression table 'CTable'.
+The space required by 'CTable' must be already allocated, using FSE_createCTable().
+You can then use FSE_buildCTable() to fill 'CTable'.
+If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()).
+
+'CTable' can then be used to compress 'src', with FSE_compress_usingCTable().
+Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize'
+The function returns the size of compressed data (without header), necessarily <= `dstCapacity`.
+If it returns '0', compressed data could not fit into 'dst'.
+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()).
+*/
+
+
+/* *** DECOMPRESSION *** */
+
+/*! FSE_readNCount():
+    Read compactly saved 'normalizedCounter' from 'rBuffer'.
+    @return : size read from 'rBuffer',
+              or an errorCode, which can be tested using FSE_isError().
+              maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */
+FSE_PUBLIC_API size_t FSE_readNCount (short* normalizedCounter,
+                           unsigned* maxSymbolValuePtr, unsigned* tableLogPtr,
+                           const void* rBuffer, size_t rBuffSize);
+
+/*! Constructor and Destructor of FSE_DTable.
+    Note that its size depends on 'tableLog' */
+typedef unsigned FSE_DTable;   /* don't allocate that. It's just a way to be more restrictive than void* */
+FSE_PUBLIC_API FSE_DTable* FSE_createDTable(unsigned tableLog);
+FSE_PUBLIC_API void        FSE_freeDTable(FSE_DTable* dt);
+
+/*! FSE_buildDTable():
+    Builds 'dt', which must be already allocated, using FSE_createDTable().
+    return : 0, or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog);
+
+/*! FSE_decompress_usingDTable():
+    Decompress compressed source `cSrc` of size `cSrcSize` using `dt`
+    into `dst` which must be already allocated.
+    @return : size of regenerated data (necessarily <= `dstCapacity`),
+              or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt);
+
+/*!
+Tutorial :
+----------
+(Note : these functions only decompress FSE-compressed blocks.
+ If block is uncompressed, use memcpy() instead
+ If block is a single repeated byte, use memset() instead )
+
+The first step is to obtain the normalized frequencies of symbols.
+This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount().
+'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short.
+In practice, that means it's necessary to know 'maxSymbolValue' beforehand,
+or size the table to handle worst case situations (typically 256).
+FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'.
+The result of FSE_readNCount() is the number of bytes read from 'rBuffer'.
+Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that.
+If there is an error, the function will return an error code, which can be tested using FSE_isError().
+
+The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'.
+This is performed by the function FSE_buildDTable().
+The space required by 'FSE_DTable' must be already allocated using FSE_createDTable().
+If there is an error, the function will return an error code, which can be tested using FSE_isError().
+
+`FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable().
+`cSrcSize` must be strictly correct, otherwise decompression will fail.
+FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`).
+If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small)
+*/
+
+#endif  /* FSE_H */
+
+#if defined(FSE_STATIC_LINKING_ONLY) && !defined(FSE_H_FSE_STATIC_LINKING_ONLY)
+#define FSE_H_FSE_STATIC_LINKING_ONLY
+
+/* *** Dependency *** */
+#include "bitstream.h"
+
+
+/* *****************************************
+*  Static allocation
+*******************************************/
+/* FSE buffer bounds */
+#define FSE_NCOUNTBOUND 512
+#define FSE_BLOCKBOUND(size) (size + (size>>7))
+#define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size))   /* Macro version, useful for static allocation */
+
+/* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */
+#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue)   (1 + (1<<(maxTableLog-1)) + ((maxSymbolValue+1)*2))
+#define FSE_DTABLE_SIZE_U32(maxTableLog)                   (1 + (1<<maxTableLog))
+
+/* or use the size to malloc() space directly. Pay attention to alignment restrictions though */
+#define FSE_CTABLE_SIZE(maxTableLog, maxSymbolValue)   (FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(FSE_CTable))
+#define FSE_DTABLE_SIZE(maxTableLog)                   (FSE_DTABLE_SIZE_U32(maxTableLog) * sizeof(FSE_DTable))
+
+
+/* *****************************************
+ *  FSE advanced API
+ ***************************************** */
+
+unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus);
+/**< same as FSE_optimalTableLog(), which used `minus==2` */
+
+/* FSE_compress_wksp() :
+ * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
+ * FSE_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable.
+ */
+#define FSE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue)   ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + ((maxTableLog > 12) ? (1 << (maxTableLog - 2)) : 1024) )
+size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
+
+size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits);
+/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */
+
+size_t FSE_buildCTable_rle (FSE_CTable* ct, unsigned char symbolValue);
+/**< build a fake FSE_CTable, designed to compress always the same symbolValue */
+
+/* FSE_buildCTable_wksp() :
+ * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
+ * `wkspSize` must be >= `(1<<tableLog)`.
+ */
+size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
+
+size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits);
+/**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */
+
+size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue);
+/**< build a fake FSE_DTable, designed to always generate the same symbolValue */
+
+size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog);
+/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DTABLE_SIZE_U32(maxLog)` */
+
+typedef enum {
+   FSE_repeat_none,  /**< Cannot use the previous table */
+   FSE_repeat_check, /**< Can use the previous table but it must be checked */
+   FSE_repeat_valid  /**< Can use the previous table and it is asumed to be valid */
+ } FSE_repeat;
+
+/* *****************************************
+*  FSE symbol compression API
+*******************************************/
+/*!
+   This API consists of small unitary functions, which highly benefit from being inlined.
+   Hence their body are included in next section.
+*/
+typedef struct {
+    ptrdiff_t   value;
+    const void* stateTable;
+    const void* symbolTT;
+    unsigned    stateLog;
+} FSE_CState_t;
+
+static void FSE_initCState(FSE_CState_t* CStatePtr, const FSE_CTable* ct);
+
+static void FSE_encodeSymbol(BIT_CStream_t* bitC, FSE_CState_t* CStatePtr, unsigned symbol);
+
+static void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* CStatePtr);
+
+/**<
+These functions are inner components of FSE_compress_usingCTable().
+They allow the creation of custom streams, mixing multiple tables and bit sources.
+
+A key property to keep in mind is that encoding and decoding are done **in reverse direction**.
+So the first symbol you will encode is the last you will decode, like a LIFO stack.
+
+You will need a few variables to track your CStream. They are :
+
+FSE_CTable    ct;         // Provided by FSE_buildCTable()
+BIT_CStream_t bitStream;  // bitStream tracking structure
+FSE_CState_t  state;      // State tracking structure (can have several)
+
+
+The first thing to do is to init bitStream and state.
+    size_t errorCode = BIT_initCStream(&bitStream, dstBuffer, maxDstSize);
+    FSE_initCState(&state, ct);
+
+Note that BIT_initCStream() can produce an error code, so its result should be tested, using FSE_isError();
+You can then encode your input data, byte after byte.
+FSE_encodeSymbol() outputs a maximum of 'tableLog' bits at a time.
+Remember decoding will be done in reverse direction.
+    FSE_encodeByte(&bitStream, &state, symbol);
+
+At any time, you can also add any bit sequence.
+Note : maximum allowed nbBits is 25, for compatibility with 32-bits decoders
+    BIT_addBits(&bitStream, bitField, nbBits);
+
+The above methods don't commit data to memory, they just store it into local register, for speed.
+Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t).
+Writing data to memory is a manual operation, performed by the flushBits function.
+    BIT_flushBits(&bitStream);
+
+Your last FSE encoding operation shall be to flush your last state value(s).
+    FSE_flushState(&bitStream, &state);
+
+Finally, you must close the bitStream.
+The function returns the size of CStream in bytes.
+If data couldn't fit into dstBuffer, it will return a 0 ( == not compressible)
+If there is an error, it returns an errorCode (which can be tested using FSE_isError()).
+    size_t size = BIT_closeCStream(&bitStream);
+*/
+
+
+/* *****************************************
+*  FSE symbol decompression API
+*******************************************/
+typedef struct {
+    size_t      state;
+    const void* table;   /* precise table may vary, depending on U16 */
+} FSE_DState_t;
+
+
+static void     FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt);
+
+static unsigned char FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD);
+
+static unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr);
+
+/**<
+Let's now decompose FSE_decompress_usingDTable() into its unitary components.
+You will decode FSE-encoded symbols from the bitStream,
+and also any other bitFields you put in, **in reverse order**.
+
+You will need a few variables to track your bitStream. They are :
+
+BIT_DStream_t DStream;    // Stream context
+FSE_DState_t  DState;     // State context. Multiple ones are possible
+FSE_DTable*   DTablePtr;  // Decoding table, provided by FSE_buildDTable()
+
+The first thing to do is to init the bitStream.
+    errorCode = BIT_initDStream(&DStream, srcBuffer, srcSize);
+
+You should then retrieve your initial state(s)
+(in reverse flushing order if you have several ones) :
+    errorCode = FSE_initDState(&DState, &DStream, DTablePtr);
+
+You can then decode your data, symbol after symbol.
+For information the maximum number of bits read by FSE_decodeSymbol() is 'tableLog'.
+Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out).
+    unsigned char symbol = FSE_decodeSymbol(&DState, &DStream);
+
+You can retrieve any bitfield you eventually stored into the bitStream (in reverse order)
+Note : maximum allowed nbBits is 25, for 32-bits compatibility
+    size_t bitField = BIT_readBits(&DStream, nbBits);
+
+All above operations only read from local register (which size depends on size_t).
+Refueling the register from memory is manually performed by the reload method.
+    endSignal = FSE_reloadDStream(&DStream);
+
+BIT_reloadDStream() result tells if there is still some more data to read from DStream.
+BIT_DStream_unfinished : there is still some data left into the DStream.
+BIT_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled.
+BIT_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed.
+BIT_DStream_tooFar : Dstream went too far. Decompression result is corrupted.
+
+When reaching end of buffer (BIT_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop,
+to properly detect the exact end of stream.
+After each decoded symbol, check if DStream is fully consumed using this simple test :
+    BIT_reloadDStream(&DStream) >= BIT_DStream_completed
+
+When it's done, verify decompression is fully completed, by checking both DStream and the relevant states.
+Checking if DStream has reached its end is performed by :
+    BIT_endOfDStream(&DStream);
+Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible.
+    FSE_endOfDState(&DState);
+*/
+
+
+/* *****************************************
+*  FSE unsafe API
+*******************************************/
+static unsigned char FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD);
+/* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */
+
+
+/* *****************************************
+*  Implementation of inlined functions
+*******************************************/
+typedef struct {
+    int deltaFindState;
+    U32 deltaNbBits;
+} FSE_symbolCompressionTransform; /* total 8 bytes */
+
+MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct)
+{
+    const void* ptr = ct;
+    const U16* u16ptr = (const U16*) ptr;
+    const U32 tableLog = MEM_read16(ptr);
+    statePtr->value = (ptrdiff_t)1<<tableLog;
+    statePtr->stateTable = u16ptr+2;
+    statePtr->symbolTT = ((const U32*)ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1));
+    statePtr->stateLog = tableLog;
+}
+
+
+/*! FSE_initCState2() :
+*   Same as FSE_initCState(), but the first symbol to include (which will be the last to be read)
+*   uses the smallest state value possible, saving the cost of this symbol */
+MEM_STATIC void FSE_initCState2(FSE_CState_t* statePtr, const FSE_CTable* ct, U32 symbol)
+{
+    FSE_initCState(statePtr, ct);
+    {   const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol];
+        const U16* stateTable = (const U16*)(statePtr->stateTable);
+        U32 nbBitsOut  = (U32)((symbolTT.deltaNbBits + (1<<15)) >> 16);
+        statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits;
+        statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState];
+    }
+}
+
+MEM_STATIC void FSE_encodeSymbol(BIT_CStream_t* bitC, FSE_CState_t* statePtr, U32 symbol)
+{
+    FSE_symbolCompressionTransform const symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol];
+    const U16* const stateTable = (const U16*)(statePtr->stateTable);
+    U32 const nbBitsOut  = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16);
+    BIT_addBits(bitC, statePtr->value, nbBitsOut);
+    statePtr->value = stateTable[ (statePtr->value >> nbBitsOut) + symbolTT.deltaFindState];
+}
+
+MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePtr)
+{
+    BIT_addBits(bitC, statePtr->value, statePtr->stateLog);
+    BIT_flushBits(bitC);
+}
+
+
+/* FSE_getMaxNbBits() :
+ * Approximate maximum cost of a symbol, in bits.
+ * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2)
+ * note 1 : assume symbolValue is valid (<= maxSymbolValue)
+ * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */
+MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue)
+{
+    const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr;
+    return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16;
+}
+
+/* FSE_bitCost() :
+ * Approximate symbol cost, as fractional value, using fixed-point format (accuracyLog fractional bits)
+ * note 1 : assume symbolValue is valid (<= maxSymbolValue)
+ * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */
+MEM_STATIC U32 FSE_bitCost(const void* symbolTTPtr, U32 tableLog, U32 symbolValue, U32 accuracyLog)
+{
+    const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr;
+    U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16;
+    U32 const threshold = (minNbBits+1) << 16;
+    assert(tableLog < 16);
+    assert(accuracyLog < 31-tableLog);  /* ensure enough room for renormalization double shift */
+    {   U32 const tableSize = 1 << tableLog;
+        U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize);
+        U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog;   /* linear interpolation (very approximate) */
+        U32 const bitMultiplier = 1 << accuracyLog;
+        assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold);
+        assert(normalizedDeltaFromThreshold <= bitMultiplier);
+        return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold;
+    }
+}
+
+
+/* ======    Decompression    ====== */
+
+typedef struct {
+    U16 tableLog;
+    U16 fastMode;
+} FSE_DTableHeader;   /* sizeof U32 */
+
+typedef struct
+{
+    unsigned short newState;
+    unsigned char  symbol;
+    unsigned char  nbBits;
+} FSE_decode_t;   /* size == U32 */
+
+MEM_STATIC void FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt)
+{
+    const void* ptr = dt;
+    const FSE_DTableHeader* const DTableH = (const FSE_DTableHeader*)ptr;
+    DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog);
+    BIT_reloadDStream(bitD);
+    DStatePtr->table = dt + 1;
+}
+
+MEM_STATIC BYTE FSE_peekSymbol(const FSE_DState_t* DStatePtr)
+{
+    FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+    return DInfo.symbol;
+}
+
+MEM_STATIC void FSE_updateState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD)
+{
+    FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+    U32 const nbBits = DInfo.nbBits;
+    size_t const lowBits = BIT_readBits(bitD, nbBits);
+    DStatePtr->state = DInfo.newState + lowBits;
+}
+
+MEM_STATIC BYTE FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD)
+{
+    FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+    U32 const nbBits = DInfo.nbBits;
+    BYTE const symbol = DInfo.symbol;
+    size_t const lowBits = BIT_readBits(bitD, nbBits);
+
+    DStatePtr->state = DInfo.newState + lowBits;
+    return symbol;
+}
+
+/*! FSE_decodeSymbolFast() :
+    unsafe, only works if no symbol has a probability > 50% */
+MEM_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD)
+{
+    FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+    U32 const nbBits = DInfo.nbBits;
+    BYTE const symbol = DInfo.symbol;
+    size_t const lowBits = BIT_readBitsFast(bitD, nbBits);
+
+    DStatePtr->state = DInfo.newState + lowBits;
+    return symbol;
+}
+
+MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr)
+{
+    return DStatePtr->state == 0;
+}
+
+
+
+#ifndef FSE_COMMONDEFS_ONLY
+
+/* **************************************************************
+*  Tuning parameters
+****************************************************************/
+/*!MEMORY_USAGE :
+*  Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+*  Increasing memory usage improves compression ratio
+*  Reduced memory usage can improve speed, due to cache effect
+*  Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */
+#ifndef FSE_MAX_MEMORY_USAGE
+#  define FSE_MAX_MEMORY_USAGE 14
+#endif
+#ifndef FSE_DEFAULT_MEMORY_USAGE
+#  define FSE_DEFAULT_MEMORY_USAGE 13
+#endif
+
+/*!FSE_MAX_SYMBOL_VALUE :
+*  Maximum symbol value authorized.
+*  Required for proper stack allocation */
+#ifndef FSE_MAX_SYMBOL_VALUE
+#  define FSE_MAX_SYMBOL_VALUE 255
+#endif
+
+/* **************************************************************
+*  template functions type & suffix
+****************************************************************/
+#define FSE_FUNCTION_TYPE BYTE
+#define FSE_FUNCTION_EXTENSION
+#define FSE_DECODE_TYPE FSE_decode_t
+
+
+#endif   /* !FSE_COMMONDEFS_ONLY */
+
+
+/* ***************************************************************
+*  Constants
+*****************************************************************/
+#define FSE_MAX_TABLELOG  (FSE_MAX_MEMORY_USAGE-2)
+#define FSE_MAX_TABLESIZE (1U<<FSE_MAX_TABLELOG)
+#define FSE_MAXTABLESIZE_MASK (FSE_MAX_TABLESIZE-1)
+#define FSE_DEFAULT_TABLELOG (FSE_DEFAULT_MEMORY_USAGE-2)
+#define FSE_MIN_TABLELOG 5
+
+#define FSE_TABLELOG_ABSOLUTE_MAX 15
+#if FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX
+#  error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported"
+#endif
+
+#define FSE_TABLESTEP(tableSize) ((tableSize>>1) + (tableSize>>3) + 3)
+
+
+#endif /* FSE_STATIC_LINKING_ONLY */
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/drivers/filesystems/btrfs/zstd/fse_compress.c b/drivers/filesystems/btrfs/zstd/fse_compress.c
new file mode 100644 (file)
index 0000000..e078eec
--- /dev/null
@@ -0,0 +1,724 @@
+/* ******************************************************************
+   FSE : Finite State Entropy encoder
+   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 :
+    - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
+    - Public forum : https://groups.google.com/forum/#!forum/lz4c
+****************************************************************** */
+
+/* **************************************************************
+*  Includes
+****************************************************************/
+#include <stdlib.h>     /* malloc, free, qsort */
+#include <string.h>     /* memcpy, memset */
+#include "compiler.h"
+#include "mem.h"        /* U32, U16, etc. */
+#include "debug.h"      /* assert, DEBUGLOG */
+#include "hist.h"       /* HIST_count_wksp */
+#include "bitstream.h"
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
+#include "error_private.h"
+#include <ntifs.h>
+#include <ntddk.h>
+
+
+/* **************************************************************
+*  Error Management
+****************************************************************/
+#define FSE_isError ERR_isError
+
+
+/* **************************************************************
+*  Templates
+****************************************************************/
+/*
+  designed to be included
+  for type-specific functions (template emulation in C)
+  Objective is to write these functions only once, for improved maintenance
+*/
+
+/* safety checks */
+#ifndef FSE_FUNCTION_EXTENSION
+#  error "FSE_FUNCTION_EXTENSION must be defined"
+#endif
+#ifndef FSE_FUNCTION_TYPE
+#  error "FSE_FUNCTION_TYPE must be defined"
+#endif
+
+/* Function names */
+#define FSE_CAT(X,Y) X##Y
+#define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y)
+#define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y)
+
+#define FSEC_ALLOC_TAG 0x64455346 // "FSEc"
+
+/* Function templates */
+
+/* FSE_buildCTable_wksp() :
+ * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
+ * wkspSize should be sized to handle worst case situation, which is `1<<max_tableLog * sizeof(FSE_FUNCTION_TYPE)`
+ * workSpace must also be properly aligned with FSE_FUNCTION_TYPE requirements
+ */
+size_t FSE_buildCTable_wksp(FSE_CTable* ct,
+                      const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog,
+                            void* workSpace, size_t wkspSize)
+{
+    U32 const tableSize = 1 << tableLog;
+    U32 const tableMask = tableSize - 1;
+    void* const ptr = ct;
+    U16* const tableU16 = ( (U16*) ptr) + 2;
+    void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableLog ? tableSize>>1 : 1) ;
+    FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT);
+    U32 const step = FSE_TABLESTEP(tableSize);
+    U32 cumul[FSE_MAX_SYMBOL_VALUE+2];
+
+    FSE_FUNCTION_TYPE* const tableSymbol = (FSE_FUNCTION_TYPE*)workSpace;
+    U32 highThreshold = tableSize-1;
+
+    /* CTable header */
+    if (((size_t)1 << tableLog) * sizeof(FSE_FUNCTION_TYPE) > wkspSize) return ERROR(tableLog_tooLarge);
+    tableU16[-2] = (U16) tableLog;
+    tableU16[-1] = (U16) maxSymbolValue;
+    assert(tableLog < 16);   /* required for threshold strategy to work */
+
+    /* For explanations on how to distribute symbol values over the table :
+     * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */
+
+     #ifdef __clang_analyzer__
+     memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize);   /* useless initialization, just to keep scan-build happy */
+     #endif
+
+    /* symbol start positions */
+    {   U32 u;
+        cumul[0] = 0;
+        for (u=1; u<=maxSymbolValue+1; u++) {
+            if (normalizedCounter[u-1]==-1) {  /* Low proba symbol */
+                cumul[u] = cumul[u-1] + 1;
+                tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u-1);
+            } else {
+                cumul[u] = cumul[u-1] + normalizedCounter[u-1];
+        }   }
+        cumul[maxSymbolValue+1] = tableSize+1;
+    }
+
+    /* Spread symbols */
+    {   U32 position = 0;
+        U32 symbol;
+        for (symbol=0; symbol<=maxSymbolValue; symbol++) {
+            int nbOccurences;
+            int const freq = normalizedCounter[symbol];
+            for (nbOccurences=0; nbOccurences<freq; nbOccurences++) {
+                tableSymbol[position] = (FSE_FUNCTION_TYPE)symbol;
+                position = (position + step) & tableMask;
+                while (position > highThreshold)
+                    position = (position + step) & tableMask;   /* Low proba area */
+        }   }
+
+        assert(position==0);  /* Must have initialized all positions */
+    }
+
+    /* Build table */
+    {   U32 u; for (u=0; u<tableSize; u++) {
+        FSE_FUNCTION_TYPE s = tableSymbol[u];   /* note : static analyzer may not understand tableSymbol is properly initialized */
+        tableU16[cumul[s]++] = (U16) (tableSize+u);   /* TableU16 : sorted by symbol order; gives next state value */
+    }   }
+
+    /* Build Symbol Transformation Table */
+    {   unsigned total = 0;
+        unsigned s;
+        for (s=0; s<=maxSymbolValue; s++) {
+            switch (normalizedCounter[s])
+            {
+            case  0:
+                /* filling nonetheless, for compatibility with FSE_getMaxNbBits() */
+                symbolTT[s].deltaNbBits = ((tableLog+1) << 16) - (1<<tableLog);
+                break;
+
+            case -1:
+            case  1:
+                symbolTT[s].deltaNbBits = (tableLog << 16) - (1<<tableLog);
+                symbolTT[s].deltaFindState = total - 1;
+                total ++;
+                break;
+            default :
+                {
+                    U32 const maxBitsOut = tableLog - BIT_highbit32 (normalizedCounter[s]-1);
+                    U32 const minStatePlus = normalizedCounter[s] << maxBitsOut;
+                    symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus;
+                    symbolTT[s].deltaFindState = total - normalizedCounter[s];
+                    total +=  normalizedCounter[s];
+    }   }   }   }
+
+#if 0  /* debug : symbol costs */
+    DEBUGLOG(5, "\n --- table statistics : ");
+    {   U32 symbol;
+        for (symbol=0; symbol<=maxSymbolValue; symbol++) {
+            DEBUGLOG(5, "%3u: w=%3i,   maxBits=%u, fracBits=%.2f",
+                symbol, normalizedCounter[symbol],
+                FSE_getMaxNbBits(symbolTT, symbol),
+                (double)FSE_bitCost(symbolTT, tableLog, symbol, 8) / 256);
+        }
+    }
+#endif
+
+    return 0;
+}
+
+
+size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
+{
+    FSE_FUNCTION_TYPE tableSymbol[FSE_MAX_TABLESIZE];   /* memset() is not necessary, even if static analyzer complain about it */
+    return FSE_buildCTable_wksp(ct, normalizedCounter, maxSymbolValue, tableLog, tableSymbol, sizeof(tableSymbol));
+}
+
+
+
+#ifndef FSE_COMMONDEFS_ONLY
+
+
+/*-**************************************************************
+*  FSE NCount encoding
+****************************************************************/
+size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog)
+{
+    size_t const maxHeaderSize = (((maxSymbolValue+1) * tableLog) >> 3) + 3;
+    return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND;  /* maxSymbolValue==0 ? use default */
+}
+
+static size_t
+FSE_writeNCount_generic (void* header, size_t headerBufferSize,
+                   const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog,
+                         unsigned writeIsSafe)
+{
+    BYTE* const ostart = (BYTE*) header;
+    BYTE* out = ostart;
+    BYTE* const oend = ostart + headerBufferSize;
+    int nbBits;
+    const int tableSize = 1 << tableLog;
+    int remaining;
+    int threshold;
+    U32 bitStream = 0;
+    int bitCount = 0;
+    unsigned symbol = 0;
+    unsigned const alphabetSize = maxSymbolValue + 1;
+    int previousIs0 = 0;
+
+    /* Table Size */
+    bitStream += (tableLog-FSE_MIN_TABLELOG) << bitCount;
+    bitCount  += 4;
+
+    /* Init */
+    remaining = tableSize+1;   /* +1 for extra accuracy */
+    threshold = tableSize;
+    nbBits = tableLog+1;
+
+    while ((symbol < alphabetSize) && (remaining>1)) {  /* stops at 1 */
+        if (previousIs0) {
+            unsigned start = symbol;
+            while ((symbol < alphabetSize) && !normalizedCounter[symbol]) symbol++;
+            if (symbol == alphabetSize) break;   /* incorrect distribution */
+            while (symbol >= start+24) {
+                start+=24;
+                bitStream += 0xFFFFU << bitCount;
+                if ((!writeIsSafe) && (out > oend-2))
+                    return ERROR(dstSize_tooSmall);   /* Buffer overflow */
+                out[0] = (BYTE) bitStream;
+                out[1] = (BYTE)(bitStream>>8);
+                out+=2;
+                bitStream>>=16;
+            }
+            while (symbol >= start+3) {
+                start+=3;
+                bitStream += 3 << bitCount;
+                bitCount += 2;
+            }
+            bitStream += (symbol-start) << bitCount;
+            bitCount += 2;
+            if (bitCount>16) {
+                if ((!writeIsSafe) && (out > oend - 2))
+                    return ERROR(dstSize_tooSmall);   /* Buffer overflow */
+                out[0] = (BYTE)bitStream;
+                out[1] = (BYTE)(bitStream>>8);
+                out += 2;
+                bitStream >>= 16;
+                bitCount -= 16;
+        }   }
+        {   int count = normalizedCounter[symbol++];
+            int const max = (2*threshold-1) - remaining;
+            remaining -= count < 0 ? -count : count;
+            count++;   /* +1 for extra accuracy */
+            if (count>=threshold)
+                count += max;   /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */
+            bitStream += count << bitCount;
+            bitCount  += nbBits;
+            bitCount  -= (count<max);
+            previousIs0  = (count==1);
+            if (remaining<1) return ERROR(GENERIC);
+            while (remaining<threshold) { nbBits--; threshold>>=1; }
+        }
+        if (bitCount>16) {
+            if ((!writeIsSafe) && (out > oend - 2))
+                return ERROR(dstSize_tooSmall);   /* Buffer overflow */
+            out[0] = (BYTE)bitStream;
+            out[1] = (BYTE)(bitStream>>8);
+            out += 2;
+            bitStream >>= 16;
+            bitCount -= 16;
+    }   }
+
+    if (remaining != 1)
+        return ERROR(GENERIC);  /* incorrect normalized distribution */
+    assert(symbol <= alphabetSize);
+
+    /* flush remaining bitStream */
+    if ((!writeIsSafe) && (out > oend - 2))
+        return ERROR(dstSize_tooSmall);   /* Buffer overflow */
+    out[0] = (BYTE)bitStream;
+    out[1] = (BYTE)(bitStream>>8);
+    out+= (bitCount+7) /8;
+
+    return (out-ostart);
+}
+
+
+size_t FSE_writeNCount (void* buffer, size_t bufferSize,
+                  const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
+{
+    if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge);   /* Unsupported */
+    if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC);   /* Unsupported */
+
+    if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog))
+        return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 0);
+
+    return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1 /* write in buffer is safe */);
+}
+
+
+/*-**************************************************************
+*  FSE Compression Code
+****************************************************************/
+
+FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog)
+{
+    size_t size;
+    if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
+    size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32);
+    return (FSE_CTable*)ExAllocatePoolWithTag(PagedPool, size, FSEC_ALLOC_TAG);
+}
+
+void FSE_freeCTable (FSE_CTable* ct) { ExFreePool(ct); }
+
+/* provides the minimum logSize to safely represent a distribution */
+static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
+{
+    U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1;
+    U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2;
+    U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols;
+    assert(srcSize > 1); /* Not supported, RLE should be used instead */
+    return minBits;
+}
+
+unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus)
+{
+    U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus;
+    U32 tableLog = maxTableLog;
+    U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue);
+    assert(srcSize > 1); /* Not supported, RLE should be used instead */
+    if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG;
+    if (maxBitsSrc < tableLog) tableLog = maxBitsSrc;   /* Accuracy can be reduced */
+    if (minBits > tableLog) tableLog = minBits;   /* Need a minimum to safely represent all symbol values */
+    if (tableLog < FSE_MIN_TABLELOG) tableLog = FSE_MIN_TABLELOG;
+    if (tableLog > FSE_MAX_TABLELOG) tableLog = FSE_MAX_TABLELOG;
+    return tableLog;
+}
+
+unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
+{
+    return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2);
+}
+
+
+/* Secondary normalization method.
+   To be used when primary method fails. */
+
+static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, size_t total, U32 maxSymbolValue)
+{
+    short const NOT_YET_ASSIGNED = -2;
+    U32 s;
+    U32 distributed = 0;
+    U32 ToDistribute;
+
+    /* Init */
+    U32 const lowThreshold = (U32)(total >> tableLog);
+    U32 lowOne = (U32)((total * 3) >> (tableLog + 1));
+
+    for (s=0; s<=maxSymbolValue; s++) {
+        if (count[s] == 0) {
+            norm[s]=0;
+            continue;
+        }
+        if (count[s] <= lowThreshold) {
+            norm[s] = -1;
+            distributed++;
+            total -= count[s];
+            continue;
+        }
+        if (count[s] <= lowOne) {
+            norm[s] = 1;
+            distributed++;
+            total -= count[s];
+            continue;
+        }
+
+        norm[s]=NOT_YET_ASSIGNED;
+    }
+    ToDistribute = (1 << tableLog) - distributed;
+
+    if (ToDistribute == 0)
+        return 0;
+
+    if ((total / ToDistribute) > lowOne) {
+        /* risk of rounding to zero */
+        lowOne = (U32)((total * 3) / (ToDistribute * 2));
+        for (s=0; s<=maxSymbolValue; s++) {
+            if ((norm[s] == NOT_YET_ASSIGNED) && (count[s] <= lowOne)) {
+                norm[s] = 1;
+                distributed++;
+                total -= count[s];
+                continue;
+        }   }
+        ToDistribute = (1 << tableLog) - distributed;
+    }
+
+    if (distributed == maxSymbolValue+1) {
+        /* all values are pretty poor;
+           probably incompressible data (should have already been detected);
+           find max, then give all remaining points to max */
+        U32 maxV = 0, maxC = 0;
+        for (s=0; s<=maxSymbolValue; s++)
+            if (count[s] > maxC) { maxV=s; maxC=count[s]; }
+        norm[maxV] += (short)ToDistribute;
+        return 0;
+    }
+
+    if (total == 0) {
+        /* all of the symbols were low enough for the lowOne or lowThreshold */
+        for (s=0; ToDistribute > 0; s = (s+1)%(maxSymbolValue+1))
+            if (norm[s] > 0) { ToDistribute--; norm[s]++; }
+        return 0;
+    }
+
+    {   U64 const vStepLog = 62 - tableLog;
+        U64 const mid = (1ULL << (vStepLog-1)) - 1;
+        U64 const rStep = ((((U64)1<<vStepLog) * ToDistribute) + mid) / total;   /* scale on remaining */
+        U64 tmpTotal = mid;
+        for (s=0; s<=maxSymbolValue; s++) {
+            if (norm[s]==NOT_YET_ASSIGNED) {
+                U64 const end = tmpTotal + (count[s] * rStep);
+                U32 const sStart = (U32)(tmpTotal >> vStepLog);
+                U32 const sEnd = (U32)(end >> vStepLog);
+                U32 const weight = sEnd - sStart;
+                if (weight < 1)
+                    return ERROR(GENERIC);
+                norm[s] = (short)weight;
+                tmpTotal = end;
+    }   }   }
+
+    return 0;
+}
+
+
+size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog,
+                           const unsigned* count, size_t total,
+                           unsigned maxSymbolValue)
+{
+    /* Sanity checks */
+    if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG;
+    if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC);   /* Unsupported size */
+    if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge);   /* Unsupported size */
+    if (tableLog < FSE_minTableLog(total, maxSymbolValue)) return ERROR(GENERIC);   /* Too small tableLog, compression potentially impossible */
+
+    {   static U32 const rtbTable[] = {     0, 473195, 504333, 520860, 550000, 700000, 750000, 830000 };
+        U64 const scale = 62 - tableLog;
+        U64 const step = ((U64)1<<62) / total;   /* <== here, one division ! */
+        U64 const vStep = 1ULL<<(scale-20);
+        int stillToDistribute = 1<<tableLog;
+        unsigned s;
+        unsigned largest=0;
+        short largestP=0;
+        U32 lowThreshold = (U32)(total >> tableLog);
+
+        for (s=0; s<=maxSymbolValue; s++) {
+            if (count[s] == total) return 0;   /* rle special case */
+            if (count[s] == 0) { normalizedCounter[s]=0; continue; }
+            if (count[s] <= lowThreshold) {
+                normalizedCounter[s] = -1;
+                stillToDistribute--;
+            } else {
+                short proba = (short)((count[s]*step) >> scale);
+                if (proba<8) {
+                    U64 restToBeat = vStep * rtbTable[proba];
+                    proba += (count[s]*step) - ((U64)proba<<scale) > restToBeat;
+                }
+                if (proba > largestP) { largestP=proba; largest=s; }
+                normalizedCounter[s] = proba;
+                stillToDistribute -= proba;
+        }   }
+        if (-stillToDistribute >= (normalizedCounter[largest] >> 1)) {
+            /* corner case, need another normalization method */
+            size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue);
+            if (FSE_isError(errorCode)) return errorCode;
+        }
+        else normalizedCounter[largest] += (short)stillToDistribute;
+    }
+
+#if 0
+    {   /* Print Table (debug) */
+        U32 s;
+        U32 nTotal = 0;
+        for (s=0; s<=maxSymbolValue; s++)
+            RAWLOG(2, "%3i: %4i \n", s, normalizedCounter[s]);
+        for (s=0; s<=maxSymbolValue; s++)
+            nTotal += abs(normalizedCounter[s]);
+        if (nTotal != (1U<<tableLog))
+            RAWLOG(2, "Warning !!! Total == %u != %u !!!", nTotal, 1U<<tableLog);
+        getchar();
+    }
+#endif
+
+    return tableLog;
+}
+
+
+/* fake FSE_CTable, for raw (uncompressed) input */
+size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits)
+{
+    const unsigned tableSize = 1 << nbBits;
+    const unsigned tableMask = tableSize - 1;
+    const unsigned maxSymbolValue = tableMask;
+    void* const ptr = ct;
+    U16* const tableU16 = ( (U16*) ptr) + 2;
+    void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableSize>>1);   /* assumption : tableLog >= 1 */
+    FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT);
+    unsigned s;
+
+    /* Sanity checks */
+    if (nbBits < 1) return ERROR(GENERIC);             /* min size */
+
+    /* header */
+    tableU16[-2] = (U16) nbBits;
+    tableU16[-1] = (U16) maxSymbolValue;
+
+    /* Build table */
+    for (s=0; s<tableSize; s++)
+        tableU16[s] = (U16)(tableSize + s);
+
+    /* Build Symbol Transformation Table */
+    {   const U32 deltaNbBits = (nbBits << 16) - (1 << nbBits);
+        for (s=0; s<=maxSymbolValue; s++) {
+            symbolTT[s].deltaNbBits = deltaNbBits;
+            symbolTT[s].deltaFindState = s-1;
+    }   }
+
+    return 0;
+}
+
+/* fake FSE_CTable, for rle input (always same symbol) */
+size_t FSE_buildCTable_rle (FSE_CTable* ct, BYTE symbolValue)
+{
+    void* ptr = ct;
+    U16* tableU16 = ( (U16*) ptr) + 2;
+    void* FSCTptr = (U32*)ptr + 2;
+    FSE_symbolCompressionTransform* symbolTT = (FSE_symbolCompressionTransform*) FSCTptr;
+
+    /* header */
+    tableU16[-2] = (U16) 0;
+    tableU16[-1] = (U16) symbolValue;
+
+    /* Build table */
+    tableU16[0] = 0;
+    tableU16[1] = 0;   /* just in case */
+
+    /* Build Symbol Transformation Table */
+    symbolTT[symbolValue].deltaNbBits = 0;
+    symbolTT[symbolValue].deltaFindState = 0;
+
+    return 0;
+}
+
+
+static size_t FSE_compress_usingCTable_generic (void* dst, size_t dstSize,
+                           const void* src, size_t srcSize,
+                           const FSE_CTable* ct, const unsigned fast)
+{
+    const BYTE* const istart = (const BYTE*) src;
+    const BYTE* const iend = istart + srcSize;
+    const BYTE* ip=iend;
+
+    BIT_CStream_t bitC;
+    FSE_CState_t CState1, CState2;
+
+    /* init */
+    if (srcSize <= 2) return 0;
+    { size_t const initError = BIT_initCStream(&bitC, dst, dstSize);
+      if (FSE_isError(initError)) return 0; /* not enough space available to write a bitstream */ }
+
+#define FSE_FLUSHBITS(s)  (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s))
+
+    if (srcSize & 1) {
+        FSE_initCState2(&CState1, ct, *--ip);
+        FSE_initCState2(&CState2, ct, *--ip);
+        FSE_encodeSymbol(&bitC, &CState1, *--ip);
+        FSE_FLUSHBITS(&bitC);
+    } else {
+        FSE_initCState2(&CState2, ct, *--ip);
+        FSE_initCState2(&CState1, ct, *--ip);
+    }
+
+    /* join to mod 4 */
+    srcSize -= 2;
+    if ((sizeof(bitC.bitContainer)*8 > FSE_MAX_TABLELOG*4+7 ) && (srcSize & 2)) {  /* test bit 2 */
+        FSE_encodeSymbol(&bitC, &CState2, *--ip);
+        FSE_encodeSymbol(&bitC, &CState1, *--ip);
+        FSE_FLUSHBITS(&bitC);
+    }
+
+    /* 2 or 4 encoding per loop */
+    while ( ip>istart ) {
+
+        FSE_encodeSymbol(&bitC, &CState2, *--ip);
+
+        if (sizeof(bitC.bitContainer)*8 < FSE_MAX_TABLELOG*2+7 )   /* this