[BTRFS][UBTRFS][SHELLBTRFS] Upgrade to 1.7.2
authorVictor Perevertkin <victor.perevertkin@reactos.org>
Thu, 23 Apr 2020 02:38:57 +0000 (05:38 +0300)
committerVictor Perevertkin <victor.perevertkin@reactos.org>
Thu, 23 Apr 2020 04:07:36 +0000 (07:07 +0300)
CORE-16679

54 files changed:
dll/shellext/shellbtrfs/balance.cpp
dll/shellext/shellbtrfs/devices.cpp
dll/shellext/shellbtrfs/main.cpp
dll/shellext/shellbtrfs/resource.h
dll/shellext/shellbtrfs/shellbtrfs.rc
dll/shellext/shellbtrfs/volpropsheet.cpp
dll/win32/ubtrfs/resource.h
dll/win32/ubtrfs/ubtrfs.rc
drivers/filesystems/btrfs/CMakeLists.txt
drivers/filesystems/btrfs/balance.c
drivers/filesystems/btrfs/blake2-impl.h [new file with mode: 0644]
drivers/filesystems/btrfs/blake2b-ref.c [new file with mode: 0644]
drivers/filesystems/btrfs/boot.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/calcthread.c
drivers/filesystems/btrfs/compress.c
drivers/filesystems/btrfs/crc32c-amd64.S [new file with mode: 0644]
drivers/filesystems/btrfs/crc32c-x86.S [new file with mode: 0644]
drivers/filesystems/btrfs/crc32c.c
drivers/filesystems/btrfs/crc32c.h [new file with mode: 0644]
drivers/filesystems/btrfs/create.c
drivers/filesystems/btrfs/devctrl.c
drivers/filesystems/btrfs/dirctrl.c
drivers/filesystems/btrfs/extent-tree.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/galois.c
drivers/filesystems/btrfs/pnp.c
drivers/filesystems/btrfs/read.c
drivers/filesystems/btrfs/registry.c
drivers/filesystems/btrfs/reparse.c
drivers/filesystems/btrfs/resource.h
drivers/filesystems/btrfs/scrub.c
drivers/filesystems/btrfs/search.c
drivers/filesystems/btrfs/security.c
drivers/filesystems/btrfs/send.c
drivers/filesystems/btrfs/sha256.c [new file with mode: 0644]
drivers/filesystems/btrfs/treefuncs.c
drivers/filesystems/btrfs/volume.c
drivers/filesystems/btrfs/worker-thread.c
drivers/filesystems/btrfs/write.c
drivers/filesystems/btrfs/xxhash.c [moved from drivers/filesystems/btrfs/zstd/xxhash.c with 98% similarity]
drivers/filesystems/btrfs/xxhash.h [moved from drivers/filesystems/btrfs/zstd/xxhash.h with 100% similarity]
drivers/filesystems/btrfs/zstd/zstd_internal.h
media/doc/README.FSD
media/inf/btrfs.inf
sdk/lib/fslib/btrfslib/CMakeLists.txt
sdk/lib/fslib/btrfslib/btrfslib.c

index 1150311..b739781 100644 (file)
@@ -383,6 +383,8 @@ void BtrfsBalance::SaveBalanceOpts(HWND hwndDlg) {
         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID10) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID10;
         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID5) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID5;
         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID6) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID6;
+        if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1C3) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1C3;
+        if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1C4) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1C4;
     }
 
     if (IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED) {
@@ -515,7 +517,7 @@ INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARA
                 HWND devcb, convcb;
                 btrfs_device* bd;
                 btrfs_balance_opts* opts;
-                static int convtypes[] = { IDS_SINGLE2, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID5, IDS_RAID6, IDS_RAID10, 0 };
+                static int convtypes[] = { IDS_SINGLE2, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID5, IDS_RAID1C3, IDS_RAID6, IDS_RAID10, IDS_RAID1C4, 0 };
                 int i, num_devices = 0, num_writeable_devices = 0;
                 wstring s, u;
                 bool balance_started = balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED);
@@ -601,7 +603,7 @@ INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARA
                         break;
                     else if (num_writeable_devices < 3 && i == 4)
                         break;
-                    else if (num_writeable_devices < 4 && i == 5)
+                    else if (num_writeable_devices < 4 && i == 6)
                         break;
                 }
 
@@ -615,6 +617,8 @@ INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARA
                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID10, opts->profiles & BLOCK_FLAG_RAID10 ? BST_CHECKED : BST_UNCHECKED);
                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID5, opts->profiles & BLOCK_FLAG_RAID5 ? BST_CHECKED : BST_UNCHECKED);
                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID6, opts->profiles & BLOCK_FLAG_RAID6 ? BST_CHECKED : BST_UNCHECKED);
+                CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1C3, opts->profiles & BLOCK_FLAG_RAID1C3 ? BST_CHECKED : BST_UNCHECKED);
+                CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1C4, opts->profiles & BLOCK_FLAG_RAID1C4 ? BST_CHECKED : BST_UNCHECKED);
 
                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_SINGLE), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_DUP), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
@@ -623,6 +627,8 @@ INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARA
                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C3), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C4), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES), balance_started ? false : true);
 
                 // usage
@@ -752,6 +758,8 @@ INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARA
                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), enabled);
                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), enabled);
                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), enabled);
+                                EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C3), enabled);
+                                EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C4), enabled);
                                 break;
                             }
 
index dfa1064..e95cace 100755 (executable)
@@ -205,7 +205,7 @@ static void find_devices(HWND hwnd, const GUID* guid, const mountmgr& mm, vector
                                 if (ss > 0) {
                                     WCHAR* desc3 = (WCHAR*)malloc(ss * sizeof(WCHAR));
 
-                                    if (MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, desc2.c_str(), -1, desc3, (int)(ss * sizeof(WCHAR))))
+                                    if (MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, desc2.c_str(), -1, desc3, ss))
                                         dev.friendly_name = desc3;
 
                                     free(desc3);
index d11b81e..9ba209a 100755 (executable)
@@ -327,7 +327,7 @@ static void register_clsid(const GUID clsid, const WCHAR* description) {
 
         write_reg_key(HKEY_CLASSES_ROOT, clsidkeyname, nullptr, description);
 
-        GetModuleFileNameW(module, dllpath, sizeof(dllpath));
+        GetModuleFileNameW(module, dllpath, sizeof(dllpath) / sizeof(WCHAR));
 
         write_reg_key(HKEY_CLASSES_ROOT, inproc, nullptr, dllpath);
 
index da6f5bd..d48bec6 100755 (executable)
@@ -1,4 +1,4 @@
-//{{NO_DEPENDENCIES}}
+//{{NO_DEPENDENCIES}}
 // Microsoft Visual C++ generated include file.
 // Used by shellbtrfs.rc
 //
 #define IDS_OUT_OF_MEMORY               215
 #define IDS_RECV_UNKNOWN_COMMAND        216
 #define IDS_RECV_CANT_OPEN_PATH         217
+#define IDS_RAID1C3                     218
 #define IDS_RECV_CREATE_SUBVOL_FAILED   219
 #define IDS_RECV_MISSING_PARAM          220
 #define IDS_RECV_SHORT_PARAM            221
 #define IDS_RECV_CREATEHARDLINK_FAILED  227
 #define IDS_RECV_SETENDOFFILE_FAILED    228
 #define IDS_RECV_CANT_CREATE_FILE       229
+#define IDS_RAID1C4                     230
 #define IDS_RECV_SETINODEINFO_FAILED    231
 #define IDS_RECV_SUCCESS                232
 #define IDS_RECV_BUTTON_OK              233
 #define IDC_SOFT                        1057
 #define IDC_PAUSE_SCRUB                 1057
 #define IDC_CANCEL_SCRUB                1058
+#define IDC_PROFILES_RAID1C3            1058
 #define IDC_SCRUB_PROGRESS              1059
+#define IDC_PROFILES_RAID1C4            1059
 #define IDC_SCRUB_STATUS                1060
 #define IDC_COMPRESS_TYPE               1061
 #define IDC_CHECK1                      1062
 #define IDC_DRIVE_LETTER_COMBO          1074
 
 // Next default values for new objects
-// 
+//
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        178
+#define _APS_NEXT_RESOURCE_VALUE        179
 #define _APS_NEXT_COMMAND_VALUE         40001
 #define _APS_NEXT_CONTROL_VALUE         1075
 #define _APS_NEXT_SYMED_VALUE           101
index 0adfa2c..8603475 100755 (executable)
@@ -25,18 +25,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
 // TEXTINCLUDE
 //
 
-1 TEXTINCLUDE 
+1 TEXTINCLUDE
 BEGIN
     "resource.h\0"
 END
 
-2 TEXTINCLUDE 
+2 TEXTINCLUDE
 BEGIN
     "#include ""winres.h""\r\n"
     "\0"
 END
 
-3 TEXTINCLUDE 
+3 TEXTINCLUDE
 BEGIN
     "\r\n"
     "\0"
@@ -61,8 +61,8 @@ IDI_ICON1               ICON                    "subvol.ico"
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,5,0,0
- PRODUCTVERSION 1,5,0,0
+ FILEVERSION 1,7,2,0
+ PRODUCTVERSION 1,7,2,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -78,12 +78,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs shell extension"
-            VALUE "FileVersion", "1.5"
+            VALUE "FileVersion", "1.7.2"
             VALUE "InternalName", "btrfs"
-            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-19"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-20"
             VALUE "OriginalFilename", "shellbtrfs.dll"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.5"
+            VALUE "ProductVersion", "1.7.2"
         END
     END
     BLOCK "VarFileInfo"
@@ -112,8 +112,8 @@ BEGIN
     LTEXT           "Group:",IDC_STATIC,14,141,22,8
     EDITTEXT        IDC_UID,94,123,40,14,ES_AUTOHSCROLL | ES_NUMBER
     EDITTEXT        IDC_GID,94,139,40,14,ES_AUTOHSCROLL | ES_NUMBER
-    LTEXT           "User",IDC_STATIC,14,172,15,8
-    LTEXT           "Group",IDC_STATIC,14,182,20,8
+    LTEXT           "User",IDC_STATIC,14,175,15,8
+    LTEXT           "Group",IDC_STATIC,14,186,20,8
     LTEXT           "Others",IDC_STATIC,14,196,22,8
     LTEXT           "Read",IDC_STATIC,50,162,17,8
     LTEXT           "Write",IDC_STATIC,89,162,18,8
@@ -209,9 +209,11 @@ BEGIN
     CONTROL         "DUP",IDC_PROFILES_DUP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,30,29,10
     CONTROL         "RAID0",IDC_PROFILES_RAID0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,41,36,10
     CONTROL         "RAID1",IDC_PROFILES_RAID1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,52,36,10
-    CONTROL         "RAID10",IDC_PROFILES_RAID10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,52,19,39,10
-    CONTROL         "RAID5",IDC_PROFILES_RAID5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,52,30,36,10
-    CONTROL         "RAID6",IDC_PROFILES_RAID6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,52,41,36,10
+    CONTROL         "RAID10",IDC_PROFILES_RAID10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,63,39,10
+    CONTROL         "RAID5",IDC_PROFILES_RAID5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,52,19,36,10
+    CONTROL         "RAID6",IDC_PROFILES_RAID6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,52,30,36,10
+    CONTROL         "RAID1C3",IDC_PROFILES_RAID1C3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,52,41,44,10
+    CONTROL         "RAID1C4",IDC_PROFILES_RAID1C4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,52,52,44,10
     CONTROL         "&Usage:",IDC_USAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,80,37,10
     EDITTEXT        IDC_USAGE_START,7,94,19,14,ES_AUTOHSCROLL | ES_NUMBER
     CONTROL         "",IDC_USAGE_START_SPINNER,"msctls_updown32",UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,25,94,11,14
@@ -535,6 +537,11 @@ BEGIN
     0
 END
 
+IDD_BALANCE_OPTIONS AFX_DIALOG_LAYOUT
+BEGIN
+    0
+END
+
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -627,9 +634,9 @@ BEGIN
     IDS_DEVLIST_READONLY_NO "No"
     IDS_DEVLIST_ALLOC       "Allocated"
     IDS_DEVLIST_ALLOC_PC    "%"
-    IDS_BALANCE_RUNNING_REMOVAL 
+    IDS_BALANCE_RUNNING_REMOVAL
                             "Currently removing device %llu (%llu out of %llu chunks processed, %1.1f%%)"
-    IDS_BALANCE_PAUSED_REMOVAL 
+    IDS_BALANCE_PAUSED_REMOVAL
                             "Removal of device %llu paused (%llu out of %llu chunks processed, %1.1f%%)"
     IDS_BALANCE_CANCELLED_REMOVAL "Device removal cancelled."
     IDS_BALANCE_COMPLETE_REMOVAL "Device removal completed successfully."
@@ -640,12 +647,12 @@ END
 STRINGTABLE
 BEGIN
     IDS_CANNOT_REMOVE_RAID  "The current RAID levels do not allow this device to be removed. You must do a conversion balance before you will be able to proceed."
-    IDS_REMOVE_DEVICE_CONFIRMATION 
+    IDS_REMOVE_DEVICE_CONFIRMATION
                             "Are you sure that you want to remove device %s, %s?"
     IDS_CONFIRMATION_TITLE  "Confirmation"
-    IDS_ADD_DEVICE_CONFIRMATION 
+    IDS_ADD_DEVICE_CONFIRMATION
                             "Are you sure that you want to add this device?"
-    IDS_ADD_DEVICE_CONFIRMATION_FS 
+    IDS_ADD_DEVICE_CONFIRMATION_FS
                             "Are you sure that you want to add this device? It already appears to contain a filesystem (%s)."
     IDS_BALANCE_FAILED      "Balance failed (error %08x, %s)"
     IDS_BALANCE_FAILED_REMOVAL "Device removal failed (error %08x, %s)"
@@ -656,32 +663,32 @@ BEGIN
     IDS_SCRUB_FINISHED      "Scrub finished."
     IDS_SCRUB_PAUSED        "Scrub paused (%llu out of %llu chunks processed, %1.1f%%)"
     IDS_SCRUB_MSG_STARTED   "Scrub started at %s %s."
-    IDS_SCRUB_MSG_RECOVERABLE_DATA 
+    IDS_SCRUB_MSG_RECOVERABLE_DATA
                             "Recovered from data checksum error at %llx on device %llx."
-    IDS_SCRUB_MSG_RECOVERABLE_METADATA 
+    IDS_SCRUB_MSG_RECOVERABLE_METADATA
                             "Recovered from metadata checksum error at %llx on device %llx."
 END
 
 STRINGTABLE
 BEGIN
-    IDS_SCRUB_MSG_UNRECOVERABLE_DATA 
+    IDS_SCRUB_MSG_UNRECOVERABLE_DATA
                             "Unrecoverable data checksum error at %llx on device %llx (%.*s, offset %llx)"
-    IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL 
+    IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL
                             "Unrecoverable data checksum error at %llx on device %llx (subvol %llx, %.*s, offset %llx)"
-    IDS_SCRUB_MSG_UNRECOVERABLE_METADATA 
+    IDS_SCRUB_MSG_UNRECOVERABLE_METADATA
                             "Unrecoverable metadata checksum error at %llx on device %llx (root %llx, level %x)"
-    IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM 
+    IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM
                             "Unrecoverable metadata checksum error at %llx on device %llx (root %llx, level %x, first item %llx,%x,%llx)"
     IDS_SCRUB_MSG_FINISHED  "Scrub finished at %s %s."
     IDS_SCRUB_MSG_SUMMARY   "Scrubbed %s in %llu seconds (%s/s)."
     IDS_BALANCE_SCRUB_RUNNING "Cannot start balance while scrub running."
     IDS_SCRUB_BALANCE_RUNNING "Cannot start scrub while balance running."
     IDS_SCRUB_MSG_SUMMARY_ERRORS_RECOVERABLE "Recovered from %llu error(s)."
-    IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE 
+    IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE
                             "%llu unrecoverable error(s) found."
     IDS_SCRUB_FAILED        "Scrub failed with error %08x."
     IDS_LOCK_FAILED         "Unable to lock volume: error %08x. Make sure that there are no files open, and that you have closed any Explorer windows."
-    IDS_SCRUB_MSG_RECOVERABLE_PARITY 
+    IDS_SCRUB_MSG_RECOVERABLE_PARITY
                             "Recovered from parity error at %llx on device %llx."
     IDS_COMPRESS_ANY        "(any)"
     IDS_COMPRESS_ZLIB       "Zlib"
@@ -700,12 +707,13 @@ BEGIN
     IDS_OUT_OF_MEMORY       "Out of memory."
     IDS_RECV_UNKNOWN_COMMAND "Unrecognized command %u encountered."
     IDS_RECV_CANT_OPEN_PATH "Couldn't open path %s (error %u, %s)."
-    IDS_RECV_CREATE_SUBVOL_FAILED 
+    IDS_RAID1C3             "RAID1C3"
+    IDS_RECV_CREATE_SUBVOL_FAILED
                             "FSCTL_BTRFS_CREATE_SUBVOL returned %08x (%s)."
     IDS_RECV_MISSING_PARAM  "%S: could not find %s parameter."
     IDS_RECV_SHORT_PARAM    "%S: length of parameter %s was %u, expected %u."
     IDS_RECV_MKNOD_FAILED   "FSCTL_BTRFS_MKNOD returned %08x (%s)."
-    IDS_RECV_SET_REPARSE_POINT_FAILED 
+    IDS_RECV_SET_REPARSE_POINT_FAILED
                             "FSCTL_SET_REPARSE_POINT returned %08x (%s)."
 END
 
@@ -714,17 +722,18 @@ BEGIN
     IDS_RECV_MOVEFILE_FAILED "MoveFile (%s -> %s) failed (error %u, %s)."
     IDS_RECV_SETFILEPOINTER_FAILED "SetFilePointer failed (error %u, %s)."
     IDS_RECV_WRITEFILE_FAILED "WriteFile failed (error %u, %s)."
-    IDS_RECV_CREATEHARDLINK_FAILED 
+    IDS_RECV_CREATEHARDLINK_FAILED
                             "CreateHardLink (%s -> %s) failed (error %u, %s)."
     IDS_RECV_SETENDOFFILE_FAILED "SetEndOfFile failed (error %u, %s)."
     IDS_RECV_CANT_CREATE_FILE "Couldn't create %s (error %u, %s)."
-    IDS_RECV_SETINODEINFO_FAILED 
+    IDS_RAID1C4             "RAID1C4"
+    IDS_RECV_SETINODEINFO_FAILED
                             "FSCTL_BTRFS_SET_INODE_INFO returned %08x (%s)."
     IDS_RECV_SUCCESS        "Received 1 subvolume successfully."
     IDS_RECV_BUTTON_OK      "OK"
-    IDS_RECV_SETFILEATTRIBUTES_FAILED 
+    IDS_RECV_SETFILEATTRIBUTES_FAILED
                             "SetFileAttributes failed (error %u, %s)."
-    IDS_RECV_GETFILEATTRIBUTES_FAILED 
+    IDS_RECV_GETFILEATTRIBUTES_FAILED
                             "GetFileAttributes failed (error %u, %s)."
     IDS_RECV_CSUM_ERROR     "Checksum error."
     IDS_RECV_NOT_A_SEND_STREAM "File was not a send stream."
@@ -734,28 +743,28 @@ END
 
 STRINGTABLE
 BEGIN
-    IDS_RECV_RECEIVED_SUBVOL_FAILED 
+    IDS_RECV_RECEIVED_SUBVOL_FAILED
                             "FSCTL_BTRFS_RECEIVED_SUBVOL returned %08x (%s)."
-    IDS_RECV_SETSECURITYOBJECT_FAILED 
+    IDS_RECV_SETSECURITYOBJECT_FAILED
                             "NtSetSecurityObject returned %08x (%s)."
     IDS_RECV_SETXATTR_FAILED "FSCTL_BTRFS_SET_XATTR returned %08x (%s)."
     IDS_RECV_CREATETHREAD_FAILED "CreateThread failed (error %u, %s)."
     IDS_RECV_FILE_TRUNCATED "File was truncated."
-    IDS_RECV_RESERVE_SUBVOL_FAILED 
+    IDS_RECV_RESERVE_SUBVOL_FAILED
                             "FSCTL_BTRFS_RESERVE_SUBVOL returned %08x (%s)."
     IDS_RECV_CANCELLED      "Receiving cancelled."
     IDS_RECV_CANT_FIND_PARENT_SUBVOL "Could not find parent subvolume."
     IDS_RECV_FIND_SUBVOL_FAILED "FSCTL_BTRFS_FIND_SUBVOL returned %08x (%s)."
-    IDS_RECV_CREATE_SNAPSHOT_FAILED 
+    IDS_RECV_CREATE_SNAPSHOT_FAILED
                             "FSCTL_BTRFS_CREATE_SNAPSHOT returned %08x (%s)."
-    IDS_RECV_GETVOLUMEPATHNAME_FAILED 
+    IDS_RECV_GETVOLUMEPATHNAME_FAILED
                             "GetVolumePathName failed (error %u, %s)."
     IDS_RECV_DELETEFILE_FAILED "DeleteFile failed for %s (error %u, %s)."
-    IDS_RECV_REMOVEDIRECTORY_FAILED 
+    IDS_RECV_REMOVEDIRECTORY_FAILED
                             "RemoveDirectory failed for %s (error %u, %s)."
     IDS_RECV_CANT_FIND_CLONE_SUBVOL "Could not find clone subvolume."
     IDS_RECV_GETFILESIZEEX_FAILED "GetFileSizeEx failed (error %u, %s)."
-    IDS_RECV_DUPLICATE_EXTENTS_FAILED 
+    IDS_RECV_DUPLICATE_EXTENTS_FAILED
                             "FSCTL_DUPLICATE_EXTENTS_TO_FILE returned %08x (%s)."
 END
 
@@ -766,13 +775,13 @@ BEGIN
     IDS_SEND_SUBVOL_HELP    "Exports a subvolume so that it can be recreated on another volume."
     IDS_SEND_CANT_OPEN_FILE "Error opening file %s (error %u, %s)."
     IDS_SEND_CANT_OPEN_DIR  "Error opening directory %s (error %u, %s)."
-    IDS_SEND_FSCTL_BTRFS_SEND_SUBVOL_FAILED 
+    IDS_SEND_FSCTL_BTRFS_SEND_SUBVOL_FAILED
                             "FSCTL_BTRFS_SEND_SUBVOL returned error %08x (%s)."
-    IDS_SEND_FSCTL_BTRFS_READ_SEND_BUFFER_FAILED 
+    IDS_SEND_FSCTL_BTRFS_READ_SEND_BUFFER_FAILED
                             "FSCTL_BTRFS_READ_SEND_BUFFER returned error %08x (%s)."
     IDS_SEND_SUCCESS        "Stream written successfully."
     IDS_SEND_WRITEFILE_FAILED "Writing to file failed (error %u, %s)."
-    IDS_SEND_GET_FILE_INFO_FAILED 
+    IDS_SEND_GET_FILE_INFO_FAILED
                             "GetFileInformationByHandle failed (error %u, %s)."
     IDS_SEND_NOT_READONLY   "Subvolume not readonly."
     IDS_NOT_SUBVOL          "Directory was not a subvolume."
@@ -788,9 +797,9 @@ BEGIN
     IDS_SEND_WRITING        "Writing..."
     IDS_MISSING             "(missing)"
     IDS_RESIZE_SUCCESSFUL   "Device %llx successfully resized to %s."
-    IDS_BALANCE_RUNNING_SHRINK 
+    IDS_BALANCE_RUNNING_SHRINK
                             "Currently shrinking device %llu (%llu out of %llu chunks processed, %1.1f%%)"
-    IDS_BALANCE_PAUSED_SHRINK 
+    IDS_BALANCE_PAUSED_SHRINK
                             "Shrinking of device %llu paused (%llu out of %llu chunks processed, %1.1f%%)"
     IDS_BALANCE_CANCELLED_SHRINK "Device shrinking cancelled."
     IDS_BALANCE_COMPLETE_SHRINK "Device successfully shrunk."
@@ -799,7 +808,7 @@ BEGIN
     IDS_REGCREATEKEY_FAILED "RegCreateKey returned %08x"
     IDS_REGSETVALUEEX_FAILED "RegSetValueEx returned %08x"
     IDS_REGCLOSEKEY_FAILED  "RegCloseKey returned %08x"
-    IDS_CANT_REFLINK_DIFFERENT_FS 
+    IDS_CANT_REFLINK_DIFFERENT_FS
                             "Cannot create a reflink between two different filesystems."
 END
 
index 9c23456..97de35a 100755 (executable)
@@ -172,8 +172,12 @@ void BtrfsVolPropSheet::FormatUsage(HWND hwndDlg, wstring& s, btrfs_usage* usage
 
     static const uint64_t types[] = { BLOCK_FLAG_DATA, BLOCK_FLAG_DATA | BLOCK_FLAG_METADATA, BLOCK_FLAG_METADATA, BLOCK_FLAG_SYSTEM };
     static const ULONG typestrings[] = { IDS_USAGE_DATA, IDS_USAGE_MIXED, IDS_USAGE_METADATA, IDS_USAGE_SYSTEM };
-    static const uint64_t duptypes[] = { 0, BLOCK_FLAG_DUPLICATE, BLOCK_FLAG_RAID0, BLOCK_FLAG_RAID1, BLOCK_FLAG_RAID10, BLOCK_FLAG_RAID5, BLOCK_FLAG_RAID6 };
-    static const ULONG dupstrings[] = { IDS_SINGLE, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID10, IDS_RAID5, IDS_RAID6 };
+    static const uint64_t duptypes[] = { 0, BLOCK_FLAG_DUPLICATE, BLOCK_FLAG_RAID0, BLOCK_FLAG_RAID1, BLOCK_FLAG_RAID10, BLOCK_FLAG_RAID5,
+                                         BLOCK_FLAG_RAID6, BLOCK_FLAG_RAID1C3, BLOCK_FLAG_RAID1C4 };
+    static const ULONG dupstrings[] = { IDS_SINGLE, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID10, IDS_RAID5, IDS_RAID6, IDS_RAID1C3, IDS_RAID1C4 };
+
+    static const uint64_t raid_types = BLOCK_FLAG_DUPLICATE | BLOCK_FLAG_RAID0 | BLOCK_FLAG_RAID1 | BLOCK_FLAG_RAID10 | BLOCK_FLAG_RAID5 |
+                                       BLOCK_FLAG_RAID6 | BLOCK_FLAG_RAID1C3 | BLOCK_FLAG_RAID1C4;
 
     s = L"";
 
@@ -327,9 +331,7 @@ void BtrfsVolPropSheet::FormatUsage(HWND hwndDlg, wstring& s, btrfs_usage* usage
             bue = usage;
 
             while (true) {
-                if ((bue->type & types[i]) == types[i] &&
-                    ((duptypes[j] == 0 && (bue->type & (BLOCK_FLAG_DUPLICATE | BLOCK_FLAG_RAID0 | BLOCK_FLAG_RAID1 | BLOCK_FLAG_RAID10 | BLOCK_FLAG_RAID5 | BLOCK_FLAG_RAID6)) == 0)
-                    || bue->type & duptypes[j])) {
+                if ((bue->type & types[i]) == types[i] && ((duptypes[j] == 0 && (bue->type & raid_types) == 0) || bue->type & duptypes[j])) {
                     wstring sizestring, usedstring, typestring, dupstring;
 
                     if (bue->type & BLOCK_FLAG_DATA && bue->type & BLOCK_FLAG_METADATA && (types[i] == BLOCK_FLAG_DATA || types[i] == BLOCK_FLAG_METADATA))
index f805e97..fbccb72 100644 (file)
@@ -1,6 +1,7 @@
 //{{NO_DEPENDENCIES}}
 // Microsoft Visual C++ generated include file.
 // Used by ubtrfs.rc
+//
 
 // Next default values for new objects
 // 
index 1720597..e43daf3 100644 (file)
@@ -51,8 +51,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,5,0,0
- PRODUCTVERSION 1,5,0,0
+ FILEVERSION 1,7,2,0
+ PRODUCTVERSION 1,7,2,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -68,12 +68,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "Btrfs utility DLL"
-            VALUE "FileVersion", "1.5"
+            VALUE "FileVersion", "1.7.2"
             VALUE "InternalName", "ubtrfs"
-            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-19"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-20"
             VALUE "OriginalFilename", "ubtrfs.dll"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.5"
+            VALUE "ProductVersion", "1.7.2"
         END
     END
     BLOCK "VarFileInfo"
index 14e7842..b45a57c 100644 (file)
@@ -5,6 +5,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/drivers
 
 list(APPEND SOURCE
     balance.c
+    blake2b-ref.c
     boot.c
     btrfs.c
     cache.c
@@ -30,11 +31,13 @@ list(APPEND SOURCE
     scrub.c
     search.c
     security.c
+    sha256.c
     send.c
     treefuncs.c
     volume.c
     worker-thread.c
     write.c
+    xxhash.c
     zstd/entropy_common.c
     zstd/fse_compress.c
     zstd/hist.c
@@ -46,14 +49,21 @@ list(APPEND SOURCE
     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 MODULE ${SOURCE} btrfs.rc)
+if(ARCH STREQUAL "i386")
+    list(APPEND ASM_SOURCE crc32c-x86.S)
+elseif(ARCH STREQUAL "amd64")
+    list(APPEND ASM_SOURCE crc32c-amd64.S)
+endif()
+
+add_asm_files(btrfs_asm ${ASM_SOURCE})
+
+add_library(btrfs MODULE ${SOURCE} ${btrfs_asm} btrfs.rc)
 
 add_definitions(-D__KERNEL__)
 set_module_type(btrfs kernelmodedriver)
index 1a2c6a2..fc47f97 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "btrfs_drv.h"
 #include "btrfsioctl.h"
+#include "crc32c.h"
 #include <ntddstor.h>
 
 typedef struct {
@@ -96,7 +97,7 @@ static NTSTATUS add_metadata_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_l
 
     Status = delete_tree_item(Vcb, tp);
     if (!NT_SUCCESS(Status)) {
-        ERR("delete_tree_item returned %08x\n", Status);
+        ERR("delete_tree_item returned %08lx\n", Status);
         ExFreePool(mr);
         return Status;
     }
@@ -191,7 +192,7 @@ static NTSTATUS add_metadata_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_l
 
                     Status = delete_tree_item(Vcb, &tp2);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("delete_tree_item returned %08x\n", Status);
+                        ERR("delete_tree_item returned %08lx\n", Status);
                         return Status;
                     }
                 } else if (tp2.item->key.obj_type == TYPE_SHARED_BLOCK_REF) {
@@ -209,7 +210,7 @@ static NTSTATUS add_metadata_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_l
 
                     Status = delete_tree_item(Vcb, &tp2);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("delete_tree_item returned %08x\n", Status);
+                        ERR("delete_tree_item returned %08lx\n", Status);
                         return Status;
                     }
                 }
@@ -252,7 +253,7 @@ static NTSTATUS add_metadata_reloc_parent(_Requires_exclusive_lock_held_(_Curr_-
 
     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -273,7 +274,7 @@ static NTSTATUS add_metadata_reloc_parent(_Requires_exclusive_lock_held_(_Curr_-
 
     Status = add_metadata_reloc(Vcb, items, &tp, skinny, mr2, NULL, rollback);
     if (!NT_SUCCESS(Status)) {
-        ERR("add_metadata_reloc returned %08x\n", Status);
+        ERR("add_metadata_reloc returned %08lx\n", Status);
         return Status;
     }
 
@@ -414,7 +415,7 @@ static NTSTATUS add_metadata_reloc_extent_item(_Requires_exclusive_lock_held_(_C
         Status = insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, ei, inline_len, NULL, NULL);
 
     if (!NT_SUCCESS(Status)) {
-        ERR("insert_tree_item returned %08x\n", Status);
+        ERR("insert_tree_item returned %08lx\n", Status);
         ExFreePool(ei);
         return Status;
     }
@@ -428,13 +429,13 @@ static NTSTATUS add_metadata_reloc_extent_item(_Requires_exclusive_lock_held_(_C
             if (ref->type == TYPE_TREE_BLOCK_REF) {
                 Status = insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_TREE_BLOCK_REF, ref->tbr.offset, NULL, 0, NULL, NULL);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("insert_tree_item returned %08x\n", Status);
+                    ERR("insert_tree_item returned %08lx\n", Status);
                     return Status;
                 }
             } else if (ref->type == TYPE_SHARED_BLOCK_REF) {
                 Status = insert_tree_item(Vcb, Vcb->extent_root, mr->new_address, TYPE_SHARED_BLOCK_REF, ref->parent->new_address, NULL, 0, NULL, NULL);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("insert_tree_item returned %08x\n", Status);
+                    ERR("insert_tree_item returned %08lx\n", Status);
                     return Status;
                 }
             }
@@ -458,7 +459,7 @@ static NTSTATUS add_metadata_reloc_extent_item(_Requires_exclusive_lock_held_(_C
 
                     Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0, NULL);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("increase_extent_refcount returned %08x\n", Status);
+                        ERR("increase_extent_refcount returned %08lx\n", Status);
                         return Status;
                     }
 
@@ -467,7 +468,7 @@ static NTSTATUS add_metadata_reloc_extent_item(_Requires_exclusive_lock_held_(_C
                     Status = decrease_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0,
                                                       sbr.offset, false, NULL);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("decrease_extent_refcount returned %08x\n", Status);
+                        ERR("decrease_extent_refcount returned %08lx\n", Status);
                         return Status;
                     }
                 }
@@ -495,7 +496,7 @@ static NTSTATUS add_metadata_reloc_extent_item(_Requires_exclusive_lock_held_(_C
 
                                 Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, NULL);
                                 if (!NT_SUCCESS(Status)) {
-                                    ERR("increase_extent_refcount returned %08x\n", Status);
+                                    ERR("increase_extent_refcount returned %08lx\n", Status);
                                     return Status;
                                 }
 
@@ -504,7 +505,7 @@ static NTSTATUS add_metadata_reloc_extent_item(_Requires_exclusive_lock_held_(_C
                                 Status = decrease_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0,
                                                                   sdr.offset, false, NULL);
                                 if (!NT_SUCCESS(Status)) {
-                                    ERR("decrease_extent_refcount returned %08x\n", Status);
+                                    ERR("decrease_extent_refcount returned %08lx\n", Status);
                                     return Status;
                                 }
 
@@ -591,7 +592,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
         Status = read_data(Vcb, mr->address, Vcb->superblock.node_size, NULL, true, (uint8_t*)mr->data,
                            c && mr->address >= c->offset && mr->address < c->offset + c->chunk_item->size ? c : NULL, &pc, NULL, 0, false, NormalPagePriority);
         if (!NT_SUCCESS(Status)) {
-            ERR("read_data returned %08x\n", Status);
+            ERR("read_data returned %08lx\n", Status);
             return Status;
         }
 
@@ -656,7 +657,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 
                 Status = find_item_to_level(Vcb, r, &tp, firstitem, false, mr->data->level + 1, NULL);
                 if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
-                    ERR("find_item_to_level returned %08x\n", Status);
+                    ERR("find_item_to_level returned %08lx\n", Status);
                     return Status;
                 }
 
@@ -672,7 +673,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 
                     Status = add_metadata_reloc_parent(Vcb, items, t->header.address, &mr2, rollback);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("add_metadata_reloc_parent returned %08x\n", Status);
+                        ERR("add_metadata_reloc_parent returned %08lx\n", Status);
                         return Status;
                     }
 
@@ -683,7 +684,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 
                 Status = add_metadata_reloc_parent(Vcb, items, ref->sbr.offset, &mr2, rollback);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("add_metadata_reloc_parent returned %08x\n", Status);
+                    ERR("add_metadata_reloc_parent returned %08lx\n", Status);
                     return Status;
                 }
 
@@ -788,7 +789,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                         Status = alloc_chunk(Vcb, flags, &newchunk, false);
 
                         if (!NT_SUCCESS(Status)) {
-                            ERR("alloc_chunk returned %08x\n", Status);
+                            ERR("alloc_chunk returned %08lx\n", Status);
                             ExReleaseResourceLite(&Vcb->chunk_lock);
                             goto end;
                         }
@@ -880,7 +881,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 
                                 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, NULL);
                                 if (!NT_SUCCESS(Status)) {
-                                    ERR("find_item returned %08x\n", Status);
+                                    ERR("find_item returned %08lx\n", Status);
                                     goto end;
                                 }
 
@@ -901,13 +902,13 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 
                                 Status = delete_tree_item(Vcb, &tp);
                                 if (!NT_SUCCESS(Status)) {
-                                    ERR("delete_tree_item returned %08x\n", Status);
+                                    ERR("delete_tree_item returned %08lx\n", Status);
                                     goto end;
                                 }
 
                                 Status = insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, NULL);
                                 if (!NT_SUCCESS(Status)) {
-                                    ERR("insert_tree_item returned %08x\n", Status);
+                                    ERR("insert_tree_item returned %08lx\n", Status);
                                     goto end;
                                 }
                             }
@@ -1025,7 +1026,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
                     t3 = t4;
                 }
 
-                *((uint32_t*)mr->data) = ~calc_crc32c(0xffffffff, (uint8_t*)&mr->data->fs_uuid, Vcb->superblock.node_size - sizeof(mr->data->csum));
+                calc_tree_checksum(Vcb, mr->data);
 
                 tw = ExAllocatePoolWithTag(PagedPool, sizeof(tree_write), ALLOC_TAG);
                 if (!tw) {
@@ -1068,7 +1069,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 
     Status = do_tree_writes(Vcb, &tree_writes, true);
     if (!NT_SUCCESS(Status)) {
-        ERR("do_tree_writes returned %08x\n", Status);
+        ERR("do_tree_writes returned %08lx\n", Status);
         goto end;
     }
 
@@ -1078,7 +1079,7 @@ static NTSTATUS write_metadata_items(_Requires_exclusive_lock_held_(_Curr_->tree
 
         Status = add_metadata_reloc_extent_item(Vcb, mr);
         if (!NT_SUCCESS(Status)) {
-            ERR("add_metadata_reloc_extent_item returned %08x\n", Status);
+            ERR("add_metadata_reloc_extent_item returned %08lx\n", Status);
             goto end;
         }
 
@@ -1121,7 +1122,7 @@ static NTSTATUS balance_metadata_chunk(device_extension* Vcb, chunk* c, bool* ch
 
     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         goto end;
     }
 
@@ -1149,7 +1150,7 @@ static NTSTATUS balance_metadata_chunk(device_extension* Vcb, chunk* c, bool* ch
                 Status = add_metadata_reloc(Vcb, &items, &tp, skinny, NULL, c, &rollback);
 
                 if (!NT_SUCCESS(Status)) {
-                    ERR("add_metadata_reloc returned %08x\n", Status);
+                    ERR("add_metadata_reloc returned %08lx\n", Status);
                     goto end;
                 }
 
@@ -1175,7 +1176,7 @@ static NTSTATUS balance_metadata_chunk(device_extension* Vcb, chunk* c, bool* ch
 
     Status = write_metadata_items(Vcb, &items, NULL, c, &rollback);
     if (!NT_SUCCESS(Status)) {
-        ERR("write_metadata_items returned %08x\n", Status);
+        ERR("write_metadata_items returned %08lx\n", Status);
         goto end;
     }
 
@@ -1187,7 +1188,7 @@ end:
     if (NT_SUCCESS(Status)) {
         Status = do_write(Vcb, NULL);
         if (!NT_SUCCESS(Status))
-            ERR("do_write returned %08x\n", Status);
+            ERR("do_write returned %08lx\n", Status);
     }
 
     if (NT_SUCCESS(Status))
@@ -1241,7 +1242,7 @@ static NTSTATUS data_reloc_add_tree_edr(_Requires_lock_held_(_Curr_->tree_lock)
     }
 
     if (!r) {
-        ERR("could not find subvol %I64x\n", edr->count);
+        ERR("could not find subvol %I64x\n", edr->root);
         return STATUS_INTERNAL_ERROR;
     }
 
@@ -1251,7 +1252,7 @@ static NTSTATUS data_reloc_add_tree_edr(_Requires_lock_held_(_Curr_->tree_lock)
 
     Status = find_item(Vcb, r, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -1293,7 +1294,7 @@ static NTSTATUS data_reloc_add_tree_edr(_Requires_lock_held_(_Curr_->tree_lock)
 
                         Status = add_metadata_reloc_parent(Vcb, metadata_items, tp.tree->header.address, &mr, rollback);
                         if (!NT_SUCCESS(Status)) {
-                            ERR("add_metadata_reloc_parent returned %08x\n", Status);
+                            ERR("add_metadata_reloc_parent returned %08lx\n", Status);
                             ExFreePool(ref);
                             return Status;
                         }
@@ -1338,7 +1339,7 @@ static NTSTATUS add_data_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
     Status = delete_tree_item(Vcb, tp);
     if (!NT_SUCCESS(Status)) {
-        ERR("delete_tree_item returned %08x\n", Status);
+        ERR("delete_tree_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -1384,7 +1385,7 @@ static NTSTATUS add_data_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
             Status = data_reloc_add_tree_edr(Vcb, metadata_items, dr, edr, rollback);
             if (!NT_SUCCESS(Status)) {
-                ERR("data_reloc_add_tree_edr returned %08x\n", Status);
+                ERR("data_reloc_add_tree_edr returned %08lx\n", Status);
                 return Status;
             }
         } else if (secttype == TYPE_SHARED_DATA_REF) {
@@ -1403,7 +1404,7 @@ static NTSTATUS add_data_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
             Status = add_metadata_reloc_parent(Vcb, metadata_items, ref->sdr.offset, &mr, rollback);
             if (!NT_SUCCESS(Status)) {
-                ERR("add_metadata_reloc_parent returned %08x\n", Status);
+                ERR("add_metadata_reloc_parent returned %08lx\n", Status);
                 ExFreePool(ref);
                 return Status;
             }
@@ -1431,13 +1432,13 @@ static NTSTATUS add_data_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
                 if (tp2.item->key.obj_type == TYPE_EXTENT_DATA_REF && tp2.item->size >= sizeof(EXTENT_DATA_REF)) {
                     Status = data_reloc_add_tree_edr(Vcb, metadata_items, dr, (EXTENT_DATA_REF*)tp2.item->data, rollback);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("data_reloc_add_tree_edr returned %08x\n", Status);
+                        ERR("data_reloc_add_tree_edr returned %08lx\n", Status);
                         return Status;
                     }
 
                     Status = delete_tree_item(Vcb, &tp2);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("delete_tree_item returned %08x\n", Status);
+                        ERR("delete_tree_item returned %08lx\n", Status);
                         return Status;
                     }
                 } else if (tp2.item->key.obj_type == TYPE_SHARED_DATA_REF && tp2.item->size >= sizeof(uint32_t)) {
@@ -1456,7 +1457,7 @@ static NTSTATUS add_data_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
                     Status = add_metadata_reloc_parent(Vcb, metadata_items, ref->sdr.offset, &mr, rollback);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("add_metadata_reloc_parent returned %08x\n", Status);
+                        ERR("add_metadata_reloc_parent returned %08lx\n", Status);
                         ExFreePool(ref);
                         return Status;
                     }
@@ -1466,7 +1467,7 @@ static NTSTATUS add_data_reloc(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
                     Status = delete_tree_item(Vcb, &tp2);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("delete_tree_item returned %08x\n", Status);
+                        ERR("delete_tree_item returned %08lx\n", Status);
                         return Status;
                     }
                 }
@@ -1620,7 +1621,7 @@ static NTSTATUS add_data_reloc_extent_item(_Requires_exclusive_lock_held_(_Curr_
 
     Status = insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_EXTENT_ITEM, dr->size, ei, inline_len, NULL, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("insert_tree_item returned %08x\n", Status);
+        ERR("insert_tree_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -1643,7 +1644,7 @@ static NTSTATUS add_data_reloc_extent_item(_Requires_exclusive_lock_held_(_Curr_
 
                 Status = insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_EXTENT_DATA_REF, ref->hash, edr, sizeof(EXTENT_DATA_REF), NULL, NULL);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("insert_tree_item returned %08x\n", Status);
+                    ERR("insert_tree_item returned %08lx\n", Status);
                     return Status;
                 }
             } else if (ref->type == TYPE_SHARED_DATA_REF) {
@@ -1659,7 +1660,7 @@ static NTSTATUS add_data_reloc_extent_item(_Requires_exclusive_lock_held_(_Curr_
 
                 Status = insert_tree_item(Vcb, Vcb->extent_root, dr->new_address, TYPE_SHARED_DATA_REF, ref->parent->new_address, sdr, sizeof(uint32_t), NULL, NULL);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("insert_tree_item returned %08x\n", Status);
+                    ERR("insert_tree_item returned %08lx\n", Status);
                     return Status;
                 }
             }
@@ -1695,7 +1696,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
 
     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         goto end;
     }
 
@@ -1719,7 +1720,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                 Status = add_data_reloc(Vcb, &items, &metadata_items, &tp, c, &rollback);
 
                 if (!NT_SUCCESS(Status)) {
-                    ERR("add_data_reloc returned %08x\n", Status);
+                    ERR("add_data_reloc returned %08lx\n", Status);
                     goto end;
                 }
 
@@ -1756,7 +1757,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
         data_reloc* dr = CONTAINING_RECORD(le, data_reloc, list_entry);
         bool done = false;
         LIST_ENTRY* le2;
-        uint32_t* csum;
+        void* csum;
         RTL_BITMAP bmp;
         ULONG* bmparr;
         ULONG bmplen, runlength, index, lastoff;
@@ -1805,7 +1806,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                 Status = alloc_chunk(Vcb, Vcb->data_flags, &newchunk, false);
 
                 if (!NT_SUCCESS(Status)) {
-                    ERR("alloc_chunk returned %08x\n", Status);
+                    ERR("alloc_chunk returned %08lx\n", Status);
                     ExReleaseResourceLite(&Vcb->chunk_lock);
                     goto end;
                 }
@@ -1842,7 +1843,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
             goto end;
         }
 
-        csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(dr->size * sizeof(uint32_t) / Vcb->superblock.sector_size), ALLOC_TAG);
+        csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(dr->size * Vcb->csum_size / Vcb->superblock.sector_size), ALLOC_TAG);
         if (!csum) {
             ERR("out of memory\n");
             ExFreePool(bmparr);
@@ -1859,7 +1860,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
 
         Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, NULL);
         if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) {
-            ERR("find_item returned %08x\n", Status);
+            ERR("find_item returned %08lx\n", Status);
             ExFreePool(csum);
             ExFreePool(bmparr);
             goto end;
@@ -1872,13 +1873,13 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                 if (tp.item->key.obj_type == TYPE_EXTENT_CSUM) {
                     if (tp.item->key.offset >= dr->address + dr->size)
                         break;
-                    else if (tp.item->size >= sizeof(uint32_t) && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(uint32_t)) >= dr->address) {
+                    else if (tp.item->size >= Vcb->csum_size && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / Vcb->csum_size) >= dr->address) {
                         uint64_t cs = max(dr->address, tp.item->key.offset);
-                        uint64_t ce = min(dr->address + dr->size, tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(uint32_t)));
+                        uint64_t ce = min(dr->address + dr->size, tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / Vcb->csum_size));
 
-                        RtlCopyMemory(csum + ((cs - dr->address) / Vcb->superblock.sector_size),
-                                      tp.item->data + ((cs - tp.item->key.offset) * sizeof(uint32_t) / Vcb->superblock.sector_size),
-                                      (ULONG)((ce - cs) * sizeof(uint32_t) / Vcb->superblock.sector_size));
+                        RtlCopyMemory((uint8_t*)csum + ((cs - dr->address) * Vcb->csum_size / Vcb->superblock.sector_size),
+                                      tp.item->data + ((cs - tp.item->key.offset) * Vcb->csum_size / Vcb->superblock.sector_size),
+                                      (ULONG)((ce - cs) * Vcb->csum_size / Vcb->superblock.sector_size));
 
                         RtlClearBits(&bmp, (ULONG)((cs - dr->address) / Vcb->superblock.sector_size), (ULONG)((ce - cs) / Vcb->superblock.sector_size));
 
@@ -1924,7 +1925,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                     Status = read_data(Vcb, dr->address + (off * Vcb->superblock.sector_size), rl * Vcb->superblock.sector_size, NULL, false, data,
                                        c, NULL, NULL, 0, false, NormalPagePriority);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("read_data returned %08x\n", Status);
+                        ERR("read_data returned %08lx\n", Status);
                         ExFreePool(csum);
                         ExFreePool(bmparr);
                         goto end;
@@ -1933,7 +1934,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                     Status = write_data_complete(Vcb, dr->new_address + (off * Vcb->superblock.sector_size), data, rl * Vcb->superblock.sector_size,
                                                  NULL, newchunk, false, 0, NormalPagePriority);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("write_data_complete returned %08x\n", Status);
+                        ERR("write_data_complete returned %08lx\n", Status);
                         ExFreePool(csum);
                         ExFreePool(bmparr);
                         goto end;
@@ -1944,7 +1945,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                 } while (size > 0);
             }
 
-            add_checksum_entry(Vcb, dr->new_address + (index * Vcb->superblock.sector_size), runlength, &csum[index], NULL);
+            add_checksum_entry(Vcb, dr->new_address + (index * Vcb->superblock.sector_size), runlength, (uint8_t*)csum + (index * Vcb->csum_size), NULL);
             add_checksum_entry(Vcb, dr->address + (index * Vcb->superblock.sector_size), runlength, NULL, NULL);
 
             // handle csum run
@@ -1956,10 +1957,10 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                 else
                     rl = runlength;
 
-                Status = read_data(Vcb, dr->address + (index * Vcb->superblock.sector_size), rl * Vcb->superblock.sector_size, &csum[index], false, data,
-                                   c, NULL, NULL, 0, false, NormalPagePriority);
+                Status = read_data(Vcb, dr->address + (index * Vcb->superblock.sector_size), rl * Vcb->superblock.sector_size,
+                                   (uint8_t*)csum + (index * Vcb->csum_size), false, data, c, NULL, NULL, 0, false, NormalPagePriority);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("read_data returned %08x\n", Status);
+                    ERR("read_data returned %08lx\n", Status);
                     ExFreePool(csum);
                     ExFreePool(bmparr);
                     goto end;
@@ -1968,7 +1969,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                 Status = write_data_complete(Vcb, dr->new_address + (index * Vcb->superblock.sector_size), data, rl * Vcb->superblock.sector_size,
                                              NULL, newchunk, false, 0, NormalPagePriority);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("write_data_complete returned %08x\n", Status);
+                    ERR("write_data_complete returned %08lx\n", Status);
                     ExFreePool(csum);
                     ExFreePool(bmparr);
                     goto end;
@@ -2001,14 +2002,14 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
                 Status = read_data(Vcb, dr->address + (off * Vcb->superblock.sector_size), rl * Vcb->superblock.sector_size, NULL, false, data,
                                    c, NULL, NULL, 0, false, NormalPagePriority);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("read_data returned %08x\n", Status);
+                    ERR("read_data returned %08lx\n", Status);
                     goto end;
                 }
 
                 Status = write_data_complete(Vcb, dr->new_address + (off * Vcb->superblock.sector_size), data, rl * Vcb->superblock.sector_size,
                                              NULL, newchunk, false, 0, NormalPagePriority);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("write_data_complete returned %08x\n", Status);
+                    ERR("write_data_complete returned %08lx\n", Status);
                     goto end;
                 }
 
@@ -2025,7 +2026,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
 
     Status = write_metadata_items(Vcb, &metadata_items, &items, NULL, &rollback);
     if (!NT_SUCCESS(Status)) {
-        ERR("write_metadata_items returned %08x\n", Status);
+        ERR("write_metadata_items returned %08lx\n", Status);
         goto end;
     }
 
@@ -2035,7 +2036,7 @@ static NTSTATUS balance_data_chunk(device_extension* Vcb, chunk* c, bool* change
 
         Status = add_data_reloc_extent_item(Vcb, dr);
         if (!NT_SUCCESS(Status)) {
-            ERR("add_data_reloc_extent_item returned %08x\n", Status);
+            ERR("add_data_reloc_extent_item returned %08lx\n", Status);
             goto end;
         }
 
@@ -2117,7 +2118,7 @@ end:
 
         Status = do_write(Vcb, NULL);
         if (!NT_SUCCESS(Status))
-            ERR("do_write returned %08x\n", Status);
+            ERR("do_write returned %08lx\n", Status);
     }
 
     if (NT_SUCCESS(Status)) {
@@ -2217,6 +2218,10 @@ static __inline uint64_t get_chunk_dup_type(chunk* c) {
         return BLOCK_FLAG_RAID5;
     else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
         return BLOCK_FLAG_RAID6;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID1C3)
+        return BLOCK_FLAG_RAID1C3;
+    else if (c->chunk_item->type & BLOCK_FLAG_RAID1C4)
+        return BLOCK_FLAG_RAID1C4;
     else
         return BLOCK_FLAG_SINGLE;
 }
@@ -2266,7 +2271,7 @@ static bool should_balance_chunk(device_extension* Vcb, uint8_t sort, chunk* c)
             factor = c->chunk_item->num_stripes - 1;
         else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
             factor = c->chunk_item->num_stripes - 2;
-        else // SINGLE, DUPLICATE, RAID1
+        else // SINGLE, DUPLICATE, RAID1, RAID1C3, RAID1C4
             factor = 1;
 
         physsize = c->chunk_item->size / factor;
@@ -2388,14 +2393,14 @@ static NTSTATUS add_balance_item(device_extension* Vcb) {
 
     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         goto end;
     }
 
     if (!keycmp(tp.item->key, searchkey)) {
         Status = delete_tree_item(Vcb, &tp);
         if (!NT_SUCCESS(Status)) {
-            ERR("delete_tree_item returned %08x\n", Status);
+            ERR("delete_tree_item returned %08lx\n", Status);
             goto end;
         }
     }
@@ -2426,7 +2431,7 @@ static NTSTATUS add_balance_item(device_extension* Vcb) {
 
     Status = insert_tree_item(Vcb, Vcb->root_root, BALANCE_ITEM_ID, TYPE_TEMP_ITEM, 0, bi, sizeof(BALANCE_ITEM), NULL, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("insert_tree_item returned %08x\n", Status);
+        ERR("insert_tree_item returned %08lx\n", Status);
         ExFreePool(bi);
         goto end;
     }
@@ -2437,7 +2442,7 @@ end:
     if (NT_SUCCESS(Status)) {
         Status = do_write(Vcb, NULL);
         if (!NT_SUCCESS(Status))
-            ERR("do_write returned %08x\n", Status);
+            ERR("do_write returned %08lx\n", Status);
     }
 
     free_trees(Vcb);
@@ -2460,20 +2465,20 @@ static NTSTATUS remove_balance_item(device_extension* Vcb) {
 
     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         goto end;
     }
 
     if (!keycmp(tp.item->key, searchkey)) {
         Status = delete_tree_item(Vcb, &tp);
         if (!NT_SUCCESS(Status)) {
-            ERR("delete_tree_item returned %08x\n", Status);
+            ERR("delete_tree_item returned %08lx\n", Status);
             goto end;
         }
 
         Status = do_write(Vcb, NULL);
         if (!NT_SUCCESS(Status)) {
-            ERR("do_write returned %08x\n", Status);
+            ERR("do_write returned %08lx\n", Status);
             goto end;
         }
 
@@ -2593,7 +2598,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
         Status = do_write(Vcb, NULL);
 
         if (!NT_SUCCESS(Status))
-            ERR("do_write returned %08x\n", Status);
+            ERR("do_write returned %08lx\n", Status);
     } else
         Status = STATUS_SUCCESS;
 
@@ -2610,7 +2615,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
 
     Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -2618,7 +2623,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
         Status = delete_tree_item(Vcb, &tp);
 
         if (!NT_SUCCESS(Status)) {
-            ERR("delete_tree_item returned %08x\n", Status);
+            ERR("delete_tree_item returned %08lx\n", Status);
             return Status;
         }
     }
@@ -2631,7 +2636,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
 
     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -2639,7 +2644,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
         Status = delete_tree_item(Vcb, &tp);
 
         if (!NT_SUCCESS(Status)) {
-            ERR("delete_tree_item returned %08x\n", Status);
+            ERR("delete_tree_item returned %08lx\n", Status);
             return Status;
         }
     }
@@ -2656,7 +2661,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
 
     Status = do_write(Vcb, NULL);
     if (!NT_SUCCESS(Status))
-        ERR("do_write returned %08x\n", Status);
+        ERR("do_write returned %08lx\n", Status);
 
     free_trees(Vcb);
 
@@ -2666,7 +2671,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
     if (!dev->readonly && dev->devobj) {
         Status = remove_superblocks(dev);
         if (!NT_SUCCESS(Status))
-            WARN("remove_superblocks returned %08x\n", Status);
+            WARN("remove_superblocks returned %08lx\n", Status);
     }
 
     // remove entry in volume list
@@ -2693,13 +2698,13 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
                     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
                     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
                     if (!NT_SUCCESS(Status))
-                        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+                        ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
                     else {
                         MOUNTDEV_NAME mdn;
 
                         Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL);
                         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-                            ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+                            ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
                         else {
                             MOUNTDEV_NAME* mdn2;
                             ULONG mdnsize = (ULONG)offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
@@ -2710,7 +2715,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
                             else {
                                 Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL);
                                 if (!NT_SUCCESS(Status))
-                                    ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+                                    ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
                                 else {
                                     UNICODE_STRING name;
 
@@ -2719,7 +2724,7 @@ static NTSTATUS finish_removing_device(_Requires_exclusive_lock_held_(_Curr_->tr
 
                                     Status = mountmgr_add_drive_letter(mountmgr, &name);
                                     if (!NT_SUCCESS(Status))
-                                        WARN("mountmgr_add_drive_letter returned %08x\n", Status);
+                                        WARN("mountmgr_add_drive_letter returned %08lx\n", Status);
                                 }
 
                                 ExFreePool(mdn2);
@@ -2817,7 +2822,7 @@ static void trim_unalloc_space(_Requires_lock_held_(_Curr_->tree_lock) device_ex
 
     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         return;
     }
 
@@ -2833,7 +2838,7 @@ static void trim_unalloc_space(_Requires_lock_held_(_Curr_->tree_lock) device_ex
 
                 lastoff = tp.item->key.offset + de->length;
             } else {
-                ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
+                ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
                 return;
             }
         }
@@ -2885,7 +2890,7 @@ static void trim_unalloc_space(_Requires_lock_held_(_Curr_->tree_lock) device_ex
 
     Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, dmdsa, datalen, NULL, 0, true, NULL);
     if (!NT_SUCCESS(Status))
-        WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status);
+        WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08lx\n", Status);
 
     ExFreePool(dmdsa);
 
@@ -2947,7 +2952,7 @@ static NTSTATUS try_consolidation(device_extension* Vcb, uint64_t flags, chunk**
 
             Status = balance_data_chunk(Vcb, rc, &changed);
             if (!NT_SUCCESS(Status)) {
-                ERR("balance_data_chunk returned %08x\n", Status);
+                ERR("balance_data_chunk returned %08lx\n", Status);
                 Vcb->balance.status = Status;
                 rc->list_entry_balance.Flink = NULL;
                 rc->reloc = false;
@@ -2971,7 +2976,7 @@ static NTSTATUS try_consolidation(device_extension* Vcb, uint64_t flags, chunk**
 
         Status = do_write(Vcb, NULL);
         if (!NT_SUCCESS(Status)) {
-            ERR("do_write returned %08x\n", Status);
+            ERR("do_write returned %08lx\n", Status);
             return Status;
         }
 
@@ -2988,7 +2993,7 @@ static NTSTATUS try_consolidation(device_extension* Vcb, uint64_t flags, chunk**
         *newchunk = rc;
         return Status;
     } else {
-        ERR("alloc_chunk returned %08x\n", Status);
+        ERR("alloc_chunk returned %08lx\n", Status);
         return Status;
     }
 }
@@ -3027,7 +3032,7 @@ static NTSTATUS regenerate_space_list(device_extension* Vcb, device* dev) {
                         factor = c->chunk_item->num_stripes - 1;
                     else if (c->chunk_item->type & BLOCK_FLAG_RAID6)
                         factor = c->chunk_item->num_stripes - 2;
-                    else // SINGLE, DUP, RAID1
+                    else // SINGLE, DUP, RAID1, RAID1C3, RAID1C4
                         factor = 1;
 
                     stripe_size = c->chunk_item->size / factor;
@@ -3092,7 +3097,7 @@ void __stdcall balance_thread(void* context) {
         if (!Vcb->balance.removing && !Vcb->balance.shrinking) {
             Status = add_balance_item(Vcb);
             if (!NT_SUCCESS(Status)) {
-                ERR("add_balance_item returned %08x\n", Status);
+                ERR("add_balance_item returned %08lx\n", Status);
                 Vcb->balance.status = Status;
                 goto end;
             }
@@ -3103,7 +3108,7 @@ void __stdcall balance_thread(void* context) {
                 free_trees(Vcb);
 
                 if (!NT_SUCCESS(Status)) {
-                    ERR("do_write returned %08x\n", Status);
+                    ERR("do_write returned %08lx\n", Status);
                     Vcb->balance.status = Status;
                     goto end;
                 }
@@ -3155,7 +3160,7 @@ void __stdcall balance_thread(void* context) {
             Status = load_cache_chunk(Vcb, c, NULL);
 
             if (!NT_SUCCESS(Status)) {
-                ERR("load_cache_chunk returned %08x\n", Status);
+                ERR("load_cache_chunk returned %08lx\n", Status);
                 Vcb->balance.status = Status;
                 release_chunk_lock(c, Vcb);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
@@ -3182,7 +3187,7 @@ void __stdcall balance_thread(void* context) {
             if (NT_SUCCESS(Status))
                 c->balance_num = Vcb->balance.balance_num;
             else if (Status != STATUS_DISK_FULL || consolidated) {
-                ERR("alloc_chunk returned %08x\n", Status);
+                ERR("alloc_chunk returned %08lx\n", Status);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 Vcb->balance.status = Status;
                 goto end;
@@ -3193,7 +3198,7 @@ void __stdcall balance_thread(void* context) {
             if (Status == STATUS_DISK_FULL) {
                 Status = try_consolidation(Vcb, Vcb->metadata_flags, &c);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("try_consolidation returned %08x\n", Status);
+                    ERR("try_consolidation returned %08lx\n", Status);
                     Vcb->balance.status = Status;
                     goto end;
                 } else
@@ -3213,7 +3218,7 @@ void __stdcall balance_thread(void* context) {
             if (NT_SUCCESS(Status))
                 c->balance_num = Vcb->balance.balance_num;
             else if (Status != STATUS_DISK_FULL || consolidated) {
-                ERR("alloc_chunk returned %08x\n", Status);
+                ERR("alloc_chunk returned %08lx\n", Status);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 Vcb->balance.status = Status;
                 goto end;
@@ -3224,7 +3229,7 @@ void __stdcall balance_thread(void* context) {
             if (Status == STATUS_DISK_FULL) {
                 Status = try_consolidation(Vcb, Vcb->data_flags, &c);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("try_consolidation returned %08x\n", Status);
+                    ERR("try_consolidation returned %08lx\n", Status);
                     Vcb->balance.status = Status;
                     goto end;
                 } else
@@ -3244,7 +3249,7 @@ void __stdcall balance_thread(void* context) {
             if (NT_SUCCESS(Status))
                 c->balance_num = Vcb->balance.balance_num;
             else if (Status != STATUS_DISK_FULL || consolidated) {
-                ERR("alloc_chunk returned %08x\n", Status);
+                ERR("alloc_chunk returned %08lx\n", Status);
                 ExReleaseResourceLite(&Vcb->chunk_lock);
                 Vcb->balance.status = Status;
                 goto end;
@@ -3255,7 +3260,7 @@ void __stdcall balance_thread(void* context) {
             if (Status == STATUS_DISK_FULL) {
                 Status = try_consolidation(Vcb, Vcb->system_flags, &c);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("try_consolidation returned %08x\n", Status);
+                    ERR("try_consolidation returned %08lx\n", Status);
                     Vcb->balance.status = Status;
                     goto end;
                 } else
@@ -3296,7 +3301,7 @@ void __stdcall balance_thread(void* context) {
 
                 Status = balance_data_chunk(Vcb, c, &changed);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("balance_data_chunk returned %08x\n", Status);
+                    ERR("balance_data_chunk returned %08lx\n", Status);
                     Vcb->balance.status = Status;
                     goto end;
                 }
@@ -3340,7 +3345,7 @@ void __stdcall balance_thread(void* context) {
             do {
                 Status = balance_metadata_chunk(Vcb, c, &changed);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("balance_metadata_chunk returned %08x\n", Status);
+                    ERR("balance_metadata_chunk returned %08lx\n", Status);
                     Vcb->balance.status = Status;
                     goto end;
                 }
@@ -3410,7 +3415,7 @@ end:
                     Status = finish_removing_device(Vcb, dev);
 
                     if (!NT_SUCCESS(Status)) {
-                        ERR("finish_removing_device returned %08x\n", Status);
+                        ERR("finish_removing_device returned %08lx\n", Status);
                         dev->reloc = false;
                     }
                 } else
@@ -3444,7 +3449,7 @@ end:
                 if (dev) {
                     Status = regenerate_space_list(Vcb, dev);
                     if (!NT_SUCCESS(Status))
-                        WARN("regenerate_space_list returned %08x\n", Status);
+                        WARN("regenerate_space_list returned %08lx\n", Status);
                 }
             } else {
                 uint64_t old_size;
@@ -3454,19 +3459,19 @@ end:
 
                 Status = update_dev_item(Vcb, dev, NULL);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("update_dev_item returned %08x\n", Status);
+                    ERR("update_dev_item returned %08lx\n", Status);
                     dev->devitem.num_bytes = old_size;
                     Vcb->balance.status = Status;
 
                     Status = regenerate_space_list(Vcb, dev);
                     if (!NT_SUCCESS(Status))
-                        WARN("regenerate_space_list returned %08x\n", Status);
+                        WARN("regenerate_space_list returned %08lx\n", Status);
                 } else {
                     Vcb->superblock.total_bytes -= old_size - dev->devitem.num_bytes;
 
                     Status = do_write(Vcb, NULL);
                     if (!NT_SUCCESS(Status))
-                        ERR("do_write returned %08x\n", Status);
+                        ERR("do_write returned %08lx\n", Status);
 
                     free_trees(Vcb);
                 }
@@ -3479,7 +3484,7 @@ end:
         } else {
             Status = remove_balance_item(Vcb);
             if (!NT_SUCCESS(Status)) {
-                ERR("remove_balance_item returned %08x\n", Status);
+                ERR("remove_balance_item returned %08lx\n", Status);
                 goto end;
             }
         }
@@ -3546,7 +3551,8 @@ NTSTATUS start_balance(device_extension* Vcb, void* data, ULONG length, KPROCESS
         if (bsb->opts[i].flags & BTRFS_BALANCE_OPTS_ENABLED) {
             if (bsb->opts[i].flags & BTRFS_BALANCE_OPTS_PROFILES) {
                 bsb->opts[i].profiles &= BLOCK_FLAG_RAID0 | BLOCK_FLAG_RAID1 | BLOCK_FLAG_DUPLICATE | BLOCK_FLAG_RAID10 |
-                                         BLOCK_FLAG_RAID5 | BLOCK_FLAG_RAID6 | BLOCK_FLAG_SINGLE;
+                                         BLOCK_FLAG_RAID5 | BLOCK_FLAG_RAID6 | BLOCK_FLAG_SINGLE | BLOCK_FLAG_RAID1C3 |
+                                         BLOCK_FLAG_RAID1C4;
 
                 if (bsb->opts[i].profiles == 0)
                     return STATUS_INVALID_PARAMETER;
@@ -3595,7 +3601,8 @@ NTSTATUS start_balance(device_extension* Vcb, void* data, ULONG length, KPROCESS
                 if (bsb->opts[i].convert != BLOCK_FLAG_RAID0 && bsb->opts[i].convert != BLOCK_FLAG_RAID1 &&
                     bsb->opts[i].convert != BLOCK_FLAG_DUPLICATE && bsb->opts[i].convert != BLOCK_FLAG_RAID10 &&
                     bsb->opts[i].convert != BLOCK_FLAG_RAID5 && bsb->opts[i].convert != BLOCK_FLAG_RAID6 &&
-                    bsb->opts[i].convert != BLOCK_FLAG_SINGLE)
+                    bsb->opts[i].convert != BLOCK_FLAG_SINGLE && bsb->opts[i].convert != BLOCK_FLAG_RAID1C3 &&
+                    bsb->opts[i].convert != BLOCK_FLAG_RAID1C4)
                     return STATUS_INVALID_PARAMETER;
             }
         }
@@ -3615,7 +3622,7 @@ NTSTATUS start_balance(device_extension* Vcb, void* data, ULONG length, KPROCESS
 
     Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
     if (!NT_SUCCESS(Status)) {
-        ERR("PsCreateSystemThread returned %08x\n", Status);
+        ERR("PsCreateSystemThread returned %08lx\n", Status);
         return Status;
     }
 
@@ -3636,7 +3643,7 @@ NTSTATUS look_for_balance_item(_Requires_lock_held_(_Curr_->tree_lock) device_ex
 
     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -3646,7 +3653,7 @@ NTSTATUS look_for_balance_item(_Requires_lock_held_(_Curr_->tree_lock) device_ex
     }
 
     if (tp.item->size < sizeof(BALANCE_ITEM)) {
-        WARN("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+        WARN("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
              tp.item->size, sizeof(BALANCE_ITEM));
         return STATUS_INTERNAL_ERROR;
     }
@@ -3698,7 +3705,7 @@ NTSTATUS look_for_balance_item(_Requires_lock_held_(_Curr_->tree_lock) device_ex
 
     Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
     if (!NT_SUCCESS(Status)) {
-        ERR("PsCreateSystemThread returned %08x\n", Status);
+        ERR("PsCreateSystemThread returned %08lx\n", Status);
         return Status;
     }
 
@@ -3802,7 +3809,7 @@ NTSTATUS remove_device(device_extension* Vcb, void* data, ULONG length, KPROCESS
     uint64_t num_rw_devices;
     OBJECT_ATTRIBUTES oa;
 
-    TRACE("(%p, %p, %x)\n", Vcb, data, length);
+    TRACE("(%p, %p, %lx)\n", Vcb, data, length);
 
     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
         return STATUS_PRIVILEGE_NOT_HELD;
@@ -3849,16 +3856,21 @@ NTSTATUS remove_device(device_extension* Vcb, void* data, ULONG length, KPROCESS
 
         if (num_rw_devices == 4 &&
             ((Vcb->data_flags & BLOCK_FLAG_RAID10 || Vcb->metadata_flags & BLOCK_FLAG_RAID10 || Vcb->system_flags & BLOCK_FLAG_RAID10) ||
-             (Vcb->data_flags & BLOCK_FLAG_RAID6 || Vcb->metadata_flags & BLOCK_FLAG_RAID6 || Vcb->system_flags & BLOCK_FLAG_RAID6))
+             (Vcb->data_flags & BLOCK_FLAG_RAID6 || Vcb->metadata_flags & BLOCK_FLAG_RAID6 || Vcb->system_flags & BLOCK_FLAG_RAID6) ||
+             (Vcb->data_flags & BLOCK_FLAG_RAID1C4 || Vcb->metadata_flags & BLOCK_FLAG_RAID1C4 || Vcb->system_flags & BLOCK_FLAG_RAID1C4)
+            )
         ) {
             ExReleaseResourceLite(&Vcb->tree_lock);
-            ERR("would not be enough devices to satisfy RAID requirement (RAID6/10)\n");
+            ERR("would not be enough devices to satisfy RAID requirement (RAID6/10/1C4)\n");
             return STATUS_CANNOT_DELETE;
         }
 
-        if (num_rw_devices == 3 && (Vcb->data_flags & BLOCK_FLAG_RAID5 || Vcb->metadata_flags & BLOCK_FLAG_RAID5 || Vcb->system_flags & BLOCK_FLAG_RAID5)) {
+        if (num_rw_devices == 3 &&
+            ((Vcb->data_flags & BLOCK_FLAG_RAID5 || Vcb->metadata_flags & BLOCK_FLAG_RAID5 || Vcb->system_flags & BLOCK_FLAG_RAID5) ||
+            (Vcb->data_flags & BLOCK_FLAG_RAID1C3 || Vcb->metadata_flags & BLOCK_FLAG_RAID1C3 || Vcb->system_flags & BLOCK_FLAG_RAID1C3))
+            ) {
             ExReleaseResourceLite(&Vcb->tree_lock);
-            ERR("would not be enough devices to satisfy RAID requirement (RAID5)\n");
+            ERR("would not be enough devices to satisfy RAID requirement (RAID5/1C3)\n");
             return STATUS_CANNOT_DELETE;
         }
 
@@ -3898,7 +3910,7 @@ NTSTATUS remove_device(device_extension* Vcb, void* data, ULONG length, KPROCESS
 
     Status = PsCreateSystemThread(&Vcb->balance.thread, 0, &oa, NULL, NULL, balance_thread, Vcb);
     if (!NT_SUCCESS(Status)) {
-        ERR("PsCreateSystemThread returned %08x\n", Status);
+        ERR("PsCreateSystemThread returned %08lx\n", Status);
         dev->reloc = false;
         return Status;
     }
diff --git a/drivers/filesystems/btrfs/blake2-impl.h b/drivers/filesystems/btrfs/blake2-impl.h
new file mode 100644 (file)
index 0000000..d6b05c0
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+   BLAKE2 reference source code package - reference C implementations
+
+   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the
+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+   your option.  The terms of these licenses can be found at:
+
+   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+   - OpenSSL license   : https://www.openssl.org/source/license.html
+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0
+
+   More information about the BLAKE2 hash function can be found at
+   https://blake2.net.
+*/
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+
+#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
+  #if   defined(_MSC_VER)
+    #define BLAKE2_INLINE __inline
+  #elif defined(__GNUC__)
+    #define BLAKE2_INLINE __inline__
+  #else
+    #define BLAKE2_INLINE
+  #endif
+#else
+  #define BLAKE2_INLINE inline
+#endif
+
+static BLAKE2_INLINE uint32_t load32( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint32_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  return (( uint32_t )( p[0] ) <<  0) |
+         (( uint32_t )( p[1] ) <<  8) |
+         (( uint32_t )( p[2] ) << 16) |
+         (( uint32_t )( p[3] ) << 24) ;
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load64( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint64_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  return (( uint64_t )( p[0] ) <<  0) |
+         (( uint64_t )( p[1] ) <<  8) |
+         (( uint64_t )( p[2] ) << 16) |
+         (( uint64_t )( p[3] ) << 24) |
+         (( uint64_t )( p[4] ) << 32) |
+         (( uint64_t )( p[5] ) << 40) |
+         (( uint64_t )( p[6] ) << 48) |
+         (( uint64_t )( p[7] ) << 56) ;
+#endif
+}
+
+static BLAKE2_INLINE uint16_t load16( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint16_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  return ( uint16_t )((( uint32_t )( p[0] ) <<  0) |
+                      (( uint32_t )( p[1] ) <<  8));
+#endif
+}
+
+static BLAKE2_INLINE void store16( void *dst, uint16_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w;
+#endif
+}
+
+static BLAKE2_INLINE void store32( void *dst, uint32_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  p[0] = (uint8_t)(w >>  0);
+  p[1] = (uint8_t)(w >>  8);
+  p[2] = (uint8_t)(w >> 16);
+  p[3] = (uint8_t)(w >> 24);
+#endif
+}
+
+static BLAKE2_INLINE void store64( void *dst, uint64_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  p[0] = (uint8_t)(w >>  0);
+  p[1] = (uint8_t)(w >>  8);
+  p[2] = (uint8_t)(w >> 16);
+  p[3] = (uint8_t)(w >> 24);
+  p[4] = (uint8_t)(w >> 32);
+  p[5] = (uint8_t)(w >> 40);
+  p[6] = (uint8_t)(w >> 48);
+  p[7] = (uint8_t)(w >> 56);
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load48( const void *src )
+{
+  const uint8_t *p = ( const uint8_t * )src;
+  return (( uint64_t )( p[0] ) <<  0) |
+         (( uint64_t )( p[1] ) <<  8) |
+         (( uint64_t )( p[2] ) << 16) |
+         (( uint64_t )( p[3] ) << 24) |
+         (( uint64_t )( p[4] ) << 32) |
+         (( uint64_t )( p[5] ) << 40) ;
+}
+
+static BLAKE2_INLINE void store48( void *dst, uint64_t w )
+{
+  uint8_t *p = ( uint8_t * )dst;
+  p[0] = (uint8_t)(w >>  0);
+  p[1] = (uint8_t)(w >>  8);
+  p[2] = (uint8_t)(w >> 16);
+  p[3] = (uint8_t)(w >> 24);
+  p[4] = (uint8_t)(w >> 32);
+  p[5] = (uint8_t)(w >> 40);
+}
+
+static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )
+{
+  return ( w >> c ) | ( w << ( 32 - c ) );
+}
+
+static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )
+{
+  return ( w >> c ) | ( w << ( 64 - c ) );
+}
+
+#if defined(_MSC_VER)
+#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
+#else
+#define BLAKE2_PACKED(x) x __attribute__((packed))
+#endif
+
+enum blake2b_constant
+{
+    BLAKE2B_BLOCKBYTES = 128,
+    BLAKE2B_OUTBYTES   = 64,
+    BLAKE2B_KEYBYTES   = 64,
+    BLAKE2B_SALTBYTES  = 16,
+    BLAKE2B_PERSONALBYTES = 16
+};
+
+typedef struct blake2b_state__
+{
+    uint64_t h[8];
+    uint64_t t[2];
+    uint64_t f[2];
+    uint8_t  buf[BLAKE2B_BLOCKBYTES];
+    size_t   buflen;
+    size_t   outlen;
+    uint8_t  last_node;
+} blake2b_state;
+
+BLAKE2_PACKED(struct blake2b_param__
+{
+    uint8_t  digest_length; /* 1 */
+    uint8_t  key_length;    /* 2 */
+    uint8_t  fanout;        /* 3 */
+    uint8_t  depth;         /* 4 */
+    uint32_t leaf_length;   /* 8 */
+    uint32_t node_offset;   /* 12 */
+    uint32_t xof_length;    /* 16 */
+    uint8_t  node_depth;    /* 17 */
+    uint8_t  inner_length;  /* 18 */
+    uint8_t  reserved[14];  /* 32 */
+    uint8_t  salt[BLAKE2B_SALTBYTES]; /* 48 */
+    uint8_t  personal[BLAKE2B_PERSONALBYTES];  /* 64 */
+});
+
+typedef struct blake2b_param__ blake2b_param;
diff --git a/drivers/filesystems/btrfs/blake2b-ref.c b/drivers/filesystems/btrfs/blake2b-ref.c
new file mode 100644 (file)
index 0000000..752ed39
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+   BLAKE2 reference source code package - reference C implementations
+
+   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the
+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+   your option.  The terms of these licenses can be found at:
+
+   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+   - OpenSSL license   : https://www.openssl.org/source/license.html
+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0
+
+   More information about the BLAKE2 hash function can be found at
+   https://blake2.net.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2-impl.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+  0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+  0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+  0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+  0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,
+  { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,
+  {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,
+  {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,
+  {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,
+  { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,
+  { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,
+  {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,
+  { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,
+  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 }
+};
+
+static int blake2b_update(blake2b_state* S, const void* in, size_t inlen);
+
+static void blake2b_set_lastnode( blake2b_state *S )
+{
+  S->f[1] = (uint64_t)-1;
+}
+
+/* Some helper functions, not necessarily useful */
+static int blake2b_is_lastblock( const blake2b_state *S )
+{
+  return S->f[0] != 0;
+}
+
+static void blake2b_set_lastblock( blake2b_state *S )
+{
+  if( S->last_node ) blake2b_set_lastnode( S );
+
+  S->f[0] = (uint64_t)-1;
+}
+
+static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
+{
+  S->t[0] += inc;
+  S->t[1] += ( S->t[0] < inc );
+}
+
+static void blake2b_init0( blake2b_state *S )
+{
+  size_t i;
+  memset( S, 0, sizeof( blake2b_state ) );
+
+  for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
+}
+
+/* init xors IV with input parameter block */
+static void blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+  const uint8_t *p = ( const uint8_t * )( P );
+  size_t i;
+
+  blake2b_init0( S );
+
+  /* IV XOR ParamBlock */
+  for( i = 0; i < 8; ++i )
+    S->h[i] ^= load64( p + sizeof( S->h[i] ) * i );
+
+  S->outlen = P->digest_length;
+}
+
+
+
+static void blake2b_init( blake2b_state *S, size_t outlen )
+{
+  blake2b_param P[1];
+
+  P->digest_length = (uint8_t)outlen;
+  P->key_length    = 0;
+  P->fanout        = 1;
+  P->depth         = 1;
+  store32( &P->leaf_length, 0 );
+  store32( &P->node_offset, 0 );
+  store32( &P->xof_length, 0 );
+  P->node_depth    = 0;
+  P->inner_length  = 0;
+  memset( P->reserved, 0, sizeof( P->reserved ) );
+  memset( P->salt,     0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+
+  blake2b_init_param( S, P );
+}
+
+#define G(r,i,a,b,c,d)                      \
+  do {                                      \
+    a = a + b + m[blake2b_sigma[r][2*i+0]]; \
+    d = rotr64(d ^ a, 32);                  \
+    c = c + d;                              \
+    b = rotr64(b ^ c, 24);                  \
+    a = a + b + m[blake2b_sigma[r][2*i+1]]; \
+    d = rotr64(d ^ a, 16);                  \
+    c = c + d;                              \
+    b = rotr64(b ^ c, 63);                  \
+  } while(0)
+
+#define ROUND(r)                    \
+  do {                              \
+    G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+    G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+    G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+    G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+    G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+    G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+    G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+    G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+  } while(0)
+
+static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
+{
+  uint64_t m[16];
+  uint64_t v[16];
+  size_t i;
+
+  for( i = 0; i < 16; ++i ) {
+    m[i] = load64( block + i * sizeof( m[i] ) );
+  }
+
+  for( i = 0; i < 8; ++i ) {
+    v[i] = S->h[i];
+  }
+
+  v[ 8] = blake2b_IV[0];
+  v[ 9] = blake2b_IV[1];
+  v[10] = blake2b_IV[2];
+  v[11] = blake2b_IV[3];
+  v[12] = blake2b_IV[4] ^ S->t[0];
+  v[13] = blake2b_IV[5] ^ S->t[1];
+  v[14] = blake2b_IV[6] ^ S->f[0];
+  v[15] = blake2b_IV[7] ^ S->f[1];
+
+  ROUND( 0 );
+  ROUND( 1 );
+  ROUND( 2 );
+  ROUND( 3 );
+  ROUND( 4 );
+  ROUND( 5 );
+  ROUND( 6 );
+  ROUND( 7 );
+  ROUND( 8 );
+  ROUND( 9 );
+  ROUND( 10 );
+  ROUND( 11 );
+
+  for( i = 0; i < 8; ++i ) {
+    S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+  }
+}
+
+#undef G
+#undef ROUND
+
+static int blake2b_update( blake2b_state *S, const void *pin, size_t inlen )
+{
+  const unsigned char * in = (const unsigned char *)pin;
+  if( inlen > 0 )
+  {
+    size_t left = S->buflen;
+    size_t fill = BLAKE2B_BLOCKBYTES - left;
+    if( inlen > fill )
+    {
+      S->buflen = 0;
+      memcpy( S->buf + left, in, fill ); /* Fill buffer */
+      blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+      blake2b_compress( S, S->buf ); /* Compress */
+      in += fill; inlen -= fill;
+      while(inlen > BLAKE2B_BLOCKBYTES) {
+        blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
+        blake2b_compress( S, in );
+        in += BLAKE2B_BLOCKBYTES;
+        inlen -= BLAKE2B_BLOCKBYTES;
+      }
+    }
+    memcpy( S->buf + S->buflen, in, inlen );
+    S->buflen += inlen;
+  }
+  return 0;
+}
+
+static int blake2b_final( blake2b_state *S, void *out, size_t outlen )
+{
+  uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
+  size_t i;
+
+  if( out == NULL || outlen < S->outlen )
+    return -1;
+
+  if( blake2b_is_lastblock( S ) )
+    return -1;
+
+  blake2b_increment_counter( S, S->buflen );
+  blake2b_set_lastblock( S );
+  memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
+  blake2b_compress( S, S->buf );
+
+  for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+    store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+  memcpy( out, buffer, S->outlen );
+
+  return 0;
+}
+
+/* inlen, at least, should be uint64_t. Others can be size_t. */
+void blake2b( void *out, size_t outlen, const void *in, size_t inlen )
+{
+  blake2b_state S[1];
+
+  blake2b_init( S, outlen );
+
+  blake2b_update( S, ( const uint8_t * )in, inlen );
+  blake2b_final( S, out, outlen );
+}
index 34fee17..aa5a437 100755 (executable)
@@ -30,6 +30,9 @@ extern LIST_ENTRY pdo_list;
 extern ERESOURCE boot_lock;
 extern PDRIVER_OBJECT drvobj;
 
+BTRFS_UUID boot_uuid; // initialized to 0
+uint64_t boot_subvol = 0;
+
 #ifndef _MSC_VER
 NTSTATUS RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString, const WCHAR* pszFormat, ...); // not in mingw
 #endif
@@ -47,113 +50,198 @@ typedef struct {
     ULONG ExtensionFlags;
 } DEVOBJ_EXTENSION2;
 
-static bool get_system_root_partition(uint32_t* disk_num, uint32_t* partition_num) {
+typedef enum {
+    system_root_unknown,
+    system_root_partition,
+    system_root_btrfs
+} system_root_type;
+
+typedef struct {
+    uint32_t disk_num;
+    uint32_t partition_num;
+    BTRFS_UUID uuid;
+    system_root_type type;
+} system_root;
+
+static void get_system_root(system_root* sr) {
     NTSTATUS Status;
     HANDLE h;
     UNICODE_STRING us, target;
     OBJECT_ATTRIBUTES objatt;
-    WCHAR* s;
-    ULONG retlen = 0, left;
+    ULONG retlen = 0;
+    bool second_time = false;
 
     static const WCHAR system_root[] = L"\\SystemRoot";
+    static const WCHAR boot_device[] = L"\\Device\\BootDevice";
     static const WCHAR arc_prefix[] = L"\\ArcName\\multi(0)disk(0)rdisk(";
     static const WCHAR arc_middle[] = L")partition(";
+    static const WCHAR arc_btrfs_prefix[] = L"\\ArcName\\btrfs(";
 
     us.Buffer = (WCHAR*)system_root;
     us.Length = us.MaximumLength = sizeof(system_root) - sizeof(WCHAR);
 
     InitializeObjectAttributes(&objatt, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
 
-    Status = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &objatt);
-    if (!NT_SUCCESS(Status)) {
-        ERR("ZwOpenSymbolicLinkObject returned %08x\n", Status);
-        return false;
-    }
+    while (true) {
+        Status = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &objatt);
+        if (!NT_SUCCESS(Status)) {
+            ERR("ZwOpenSymbolicLinkObject returned %08lx\n", Status);
+            return;
+        }
 
-    target.Length = target.MaximumLength = 0;
+        target.Length = target.MaximumLength = 0;
 
-    Status = ZwQuerySymbolicLinkObject(h, &target, &retlen);
-    if (Status != STATUS_BUFFER_TOO_SMALL) {
-        ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status);
-        NtClose(h);
-        return false;
-    }
+        Status = ZwQuerySymbolicLinkObject(h, &target, &retlen);
+        if (Status != STATUS_BUFFER_TOO_SMALL) {
+            ERR("ZwQuerySymbolicLinkObject returned %08lx\n", Status);
+            NtClose(h);
+            return;
+        }
 
-    if (retlen == 0) {
-        NtClose(h);
-        return false;
-    }
+        if (retlen == 0) {
+            NtClose(h);
+            return;
+        }
 
-    target.Buffer = ExAllocatePoolWithTag(NonPagedPool, retlen, ALLOC_TAG);
-    if (!target.Buffer) {
-        ERR("out of memory\n");
-        NtClose(h);
-        return false;
-    }
+        target.Buffer = ExAllocatePoolWithTag(NonPagedPool, retlen, ALLOC_TAG);
+        if (!target.Buffer) {
+            ERR("out of memory\n");
+            NtClose(h);
+            return;
+        }
 
-    target.Length = target.MaximumLength = (USHORT)retlen;
+        target.Length = target.MaximumLength = (USHORT)retlen;
+
+        Status = ZwQuerySymbolicLinkObject(h, &target, NULL);
+        if (!NT_SUCCESS(Status)) {
+            ERR("ZwQuerySymbolicLinkObject returned %08lx\n", Status);
+            NtClose(h);
+            ExFreePool(target.Buffer);
+            return;
+        }
 
-    Status = ZwQuerySymbolicLinkObject(h, &target, NULL);
-    if (!NT_SUCCESS(Status)) {
-        ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status);
         NtClose(h);
-        ExFreePool(target.Buffer);
-        return false;
-    }
 
-    NtClose(h);
+        if (second_time) {
+            TRACE("boot device is %.*S\n", (int)(target.Length / sizeof(WCHAR)), target.Buffer);
+        } else {
+            TRACE("system root is %.*S\n", (int)(target.Length / sizeof(WCHAR)), target.Buffer);
+        }
+
+        if (!second_time && target.Length >= sizeof(boot_device) - sizeof(WCHAR) &&
+            RtlCompareMemory(target.Buffer, boot_device, sizeof(boot_device) - sizeof(WCHAR)) == sizeof(boot_device) - sizeof(WCHAR)) {
+            ExFreePool(target.Buffer);
 
-    TRACE("system root is %.*S\n", target.Length / sizeof(WCHAR), target.Buffer);
+            us.Buffer = (WCHAR*)boot_device;
+            us.Length = us.MaximumLength = sizeof(boot_device) - sizeof(WCHAR);
 
-    if (target.Length <= sizeof(arc_prefix) - sizeof(WCHAR) ||
-        RtlCompareMemory(target.Buffer, arc_prefix, sizeof(arc_prefix) - sizeof(WCHAR)) != sizeof(arc_prefix) - sizeof(WCHAR)) {
-        ExFreePool(target.Buffer);
-        return false;
+            second_time = true;
+        } else
+            break;
     }
 
-    s = &target.Buffer[(sizeof(arc_prefix) / sizeof(WCHAR)) - 1];
-    left = ((target.Length - sizeof(arc_prefix)) / sizeof(WCHAR)) + 1;
+    sr->type = system_root_unknown;
 
-    if (left == 0 || s[0] < '0' || s[0] > '9') {
-        ExFreePool(target.Buffer);
-        return false;
-    }
+    if (target.Length >= sizeof(arc_prefix) - sizeof(WCHAR) &&
+        RtlCompareMemory(target.Buffer, arc_prefix, sizeof(arc_prefix) - sizeof(WCHAR)) == sizeof(arc_prefix) - sizeof(WCHAR)) {
+        WCHAR* s = &target.Buffer[(sizeof(arc_prefix) / sizeof(WCHAR)) - 1];
+        ULONG left = ((target.Length - sizeof(arc_prefix)) / sizeof(WCHAR)) + 1;
 
-    *disk_num = 0;
+        if (left == 0 || s[0] < '0' || s[0] > '9') {
+            ExFreePool(target.Buffer);
+            return;
+        }
 
-    while (left > 0 && s[0] >= '0' && s[0] <= '9') {
-        *disk_num *= 10;
-        *disk_num += s[0] - '0';
-        s++;
-        left--;
-    }
+        sr->disk_num = 0;
 
-    if (left <= (sizeof(arc_middle) / sizeof(WCHAR)) - 1 ||
-        RtlCompareMemory(s, arc_middle, sizeof(arc_middle) - sizeof(WCHAR)) != sizeof(arc_middle) - sizeof(WCHAR)) {
-        ExFreePool(target.Buffer);
-        return false;
-    }
+        while (left > 0 && s[0] >= '0' && s[0] <= '9') {
+            sr->disk_num *= 10;
+            sr->disk_num += s[0] - '0';
+            s++;
+            left--;
+        }
 
-    s = &s[(sizeof(arc_middle) / sizeof(WCHAR)) - 1];
-    left -= (sizeof(arc_middle) / sizeof(WCHAR)) - 1;
+        if (left <= (sizeof(arc_middle) / sizeof(WCHAR)) - 1 ||
+            RtlCompareMemory(s, arc_middle, sizeof(arc_middle) - sizeof(WCHAR)) != sizeof(arc_middle) - sizeof(WCHAR)) {
+            ExFreePool(target.Buffer);
+            return;
+        }
 
-    if (left == 0 || s[0] < '0' || s[0] > '9') {
-        ExFreePool(target.Buffer);
-        return false;
-    }
+        s = &s[(sizeof(arc_middle) / sizeof(WCHAR)) - 1];
+        left -= (sizeof(arc_middle) / sizeof(WCHAR)) - 1;
+
+        if (left == 0 || s[0] < '0' || s[0] > '9') {
+            ExFreePool(target.Buffer);
+            return;
+        }
 
-    *partition_num = 0;
+        sr->partition_num = 0;
 
-    while (left > 0 && s[0] >= '0' && s[0] <= '9') {
-        *partition_num *= 10;
-        *partition_num += s[0] - '0';
-        s++;
-        left--;
+        while (left > 0 && s[0] >= '0' && s[0] <= '9') {
+            sr->partition_num *= 10;
+            sr->partition_num += s[0] - '0';
+            s++;
+            left--;
+        }
+
+        sr->type = system_root_partition;
+    } else if (target.Length >= sizeof(arc_btrfs_prefix) - sizeof(WCHAR) &&
+        RtlCompareMemory(target.Buffer, arc_btrfs_prefix, sizeof(arc_btrfs_prefix) - sizeof(WCHAR)) == sizeof(arc_btrfs_prefix) - sizeof(WCHAR)) {
+        WCHAR* s = &target.Buffer[(sizeof(arc_btrfs_prefix) / sizeof(WCHAR)) - 1];
+#ifdef __REACTOS__
+        unsigned int i;
+#endif // __REACTOS__
+
+#ifndef __REACTOS__
+        for (unsigned int i = 0; i < 16; i++) {
+#else
+        for (i = 0; i < 16; i++) {
+#endif // __REACTOS__
+            if (*s >= '0' && *s <= '9')
+                sr->uuid.uuid[i] = (*s - '0') << 4;
+            else if (*s >= 'a' && *s <= 'f')
+                sr->uuid.uuid[i] = (*s - 'a' + 0xa) << 4;
+            else if (*s >= 'A' && *s <= 'F')
+                sr->uuid.uuid[i] = (*s - 'A' + 0xa) << 4;
+            else {
+                ExFreePool(target.Buffer);
+                return;
+            }
+
+            s++;
+
+            if (*s >= '0' && *s <= '9')
+                sr->uuid.uuid[i] |= *s - '0';
+            else if (*s >= 'a' && *s <= 'f')
+                sr->uuid.uuid[i] |= *s - 'a' + 0xa;
+            else if (*s >= 'A' && *s <= 'F')
+                sr->uuid.uuid[i] |= *s - 'A' + 0xa;
+            else {
+                ExFreePool(target.Buffer);
+                return;
+            }
+
+            s++;
+
+            if (i == 3 || i == 5 || i == 7 || i == 9) {
+                if (*s != '-') {
+                    ExFreePool(target.Buffer);
+                    return;
+                }
+
+                s++;
+            }
+        }
+
+        if (*s != ')') {
+            ExFreePool(target.Buffer);
+            return;
+        }
+
+        sr->type = system_root_btrfs;
     }
 
     ExFreePool(target.Buffer);
-
-    return true;
 }
 
 static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID* uuid) {
@@ -170,13 +258,13 @@ static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID
 
     Status = RtlUnicodeStringPrintf(&us, L"\\Device\\Harddisk%u\\Partition%u", disk_num, partition_num);
     if (!NT_SUCCESS(Status)) {
-        ERR("RtlUnicodeStringPrintf returned %08x\n", Status);
+        ERR("RtlUnicodeStringPrintf returned %08lx\n", Status);
         return;
     }
 
     Status = IoDeleteSymbolicLink(&us);
     if (!NT_SUCCESS(Status))
-        ERR("IoDeleteSymbolicLink returned %08x\n", Status);
+        ERR("IoDeleteSymbolicLink returned %08lx\n", Status);
 
     RtlCopyMemory(target, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
 
@@ -203,7 +291,7 @@ static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID
 
     Status = IoCreateSymbolicLink(&us, &us2);
     if (!NT_SUCCESS(Status))
-        ERR("IoCreateSymbolicLink returned %08x\n", Status);
+        ERR("IoCreateSymbolicLink returned %08lx\n", Status);
 }
 
 static void mountmgr_notification(BTRFS_UUID* uuid) {
@@ -221,7 +309,7 @@ static void mountmgr_notification(BTRFS_UUID* uuid) {
     RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
     Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
         return;
     }
 
@@ -257,7 +345,7 @@ static void mountmgr_notification(BTRFS_UUID* uuid) {
 
     Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, mmtn, mmtnlen, NULL, 0, false, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION returned %08x\n", Status);
+        ERR("IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION returned %08lx\n", Status);
         ExFreePool(mmtn);
         return;
     }
@@ -265,6 +353,110 @@ static void mountmgr_notification(BTRFS_UUID* uuid) {
     ExFreePool(mmtn);
 }
 
+static void check_boot_options() {
+    NTSTATUS Status;
+    WCHAR* s;
+
+    static const WCHAR pathw[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control";
+    static const WCHAR namew[] = L"SystemStartOptions";
+    static const WCHAR subvol[] = L"SUBVOL=";
+
+    _SEH2_TRY {
+        HANDLE control;
+        OBJECT_ATTRIBUTES oa;
+        UNICODE_STRING path;
+        ULONG kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR));
+        KEY_VALUE_FULL_INFORMATION* kvfi;
+        UNICODE_STRING name;
+        WCHAR* options;
+
+        path.Buffer = (WCHAR*)pathw;
+        path.Length = path.MaximumLength = sizeof(pathw) - sizeof(WCHAR);
+
+        InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+        Status = ZwOpenKey(&control, KEY_QUERY_VALUE, &oa);
+        if (!NT_SUCCESS(Status)) {
+            ERR("ZwOpenKey returned %08lx\n", Status);
+            return;
+        }
+
+        // FIXME - don't fail if value too long (can we query for the length?)
+
+        kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
+        if (!kvfi) {
+            ERR("out of memory\n");
+            NtClose(control);
+            return;
+        }
+
+        name.Buffer = (WCHAR*)namew;
+        name.Length = name.MaximumLength = sizeof(namew) - sizeof(WCHAR);
+
+        Status = ZwQueryValueKey(control, &name, KeyValueFullInformation, kvfi,
+                                 kvfilen, &kvfilen);
+        if (!NT_SUCCESS(Status)) {
+            ERR("ZwQueryValueKey returned %08lx\n", Status);
+            NtClose(control);
+            return;
+        }
+
+        NtClose(control);
+
+        options = (WCHAR*)((uint8_t*)kvfi + kvfi->DataOffset);
+        options[kvfi->DataLength / sizeof(WCHAR)] = 0; // FIXME - make sure buffer long enough to allow this
+
+        s = wcsstr(options, subvol);
+
+        if (!s)
+            return;
+
+        s += (sizeof(subvol) / sizeof(WCHAR)) - 1;
+
+        boot_subvol = 0;
+
+        while (true) {
+            if (*s >= '0' && *s <= '9') {
+                boot_subvol <<= 4;
+                boot_subvol |= *s - '0';
+            } else if (*s >= 'a' && *s <= 'f') {
+                boot_subvol <<= 4;
+                boot_subvol |= *s - 'a' + 0xa;
+            } else if (*s >= 'A' && *s <= 'F') {
+                boot_subvol <<= 4;
+                boot_subvol |= *s - 'A' + 0xa;
+            } else
+                break;
+
+            s++;
+        }
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        return;
+    } _SEH2_END;
+
+    if (boot_subvol != 0) {
+        TRACE("passed subvol %I64x in boot options\n", boot_subvol);
+    }
+}
+
+void boot_add_device(DEVICE_OBJECT* pdo) {
+    pdo_device_extension* pdode = pdo->DeviceExtension;
+
+    AddDevice(drvobj, pdo);
+
+    // To stop Windows sneakily setting DOE_START_PENDING
+    pdode->dont_report = true;
+
+    if (pdo->DeviceObjectExtension) {
+        ((DEVOBJ_EXTENSION2*)pdo->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
+
+        if (pdode && pdode->vde && pdode->vde->device)
+            ((DEVOBJ_EXTENSION2*)pdode->vde->device->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
+    }
+
+    mountmgr_notification(&pdode->uuid);
+}
+
 /* If booting from Btrfs, Windows will pass the device object for the raw partition to
  * mount_vol - which is no good to us, as we only use the \Device\Btrfs{} devices we
  * create so that RAID works correctly.
@@ -276,109 +468,156 @@ static void mountmgr_notification(BTRFS_UUID* uuid) {
  * point to.
  */
 void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count) {
-    uint32_t disk_num, partition_num;
+    system_root sr;
     LIST_ENTRY* le;
     bool done = false;
     PDEVICE_OBJECT pdo_to_add = NULL;
+    volume_child* boot_vc = NULL;
 
-    TRACE("(%p, %p, %u)\n", DriverObject, Context, Count);
+    TRACE("(%p, %p, %lu)\n", DriverObject, Context, Count);
 
     // wait for any PNP notifications in progress to finish
     ExAcquireResourceExclusiveLite(&boot_lock, TRUE);
     ExReleaseResourceLite(&boot_lock);
 
-    if (!get_system_root_partition(&disk_num, &partition_num))
-        return;
+    get_system_root(&sr);
 
-    TRACE("system boot partition is disk %u, partition %u\n", disk_num, partition_num);
+    if (sr.type == system_root_partition) {
+        TRACE("system boot partition is disk %u, partition %u\n", sr.disk_num, sr.partition_num);
 
-    ExAcquireResourceSharedLite(&pdo_list_lock, true);
+        ExAcquireResourceSharedLite(&pdo_list_lock, true);
 
-    le = pdo_list.Flink;
-    while (le != &pdo_list) {
-        LIST_ENTRY* le2;
-        pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+        le = pdo_list.Flink;
+        while (le != &pdo_list) {
+            LIST_ENTRY* le2;
+            pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
 
-        ExAcquireResourceSharedLite(&pdode->child_lock, true);
+            ExAcquireResourceSharedLite(&pdode->child_lock, true);
 
-        le2 = pdode->children.Flink;
+            le2 = pdode->children.Flink;
 
-        while (le2 != &pdode->children) {
-            volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+            while (le2 != &pdode->children) {
+                volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
 
-            if (vc->disk_num == disk_num && vc->part_num == partition_num) {
-                change_symlink(disk_num, partition_num, &pdode->uuid);
-                done = true;
+                if (vc->disk_num == sr.disk_num && vc->part_num == sr.partition_num) {
+                    change_symlink(sr.disk_num, sr.partition_num, &pdode->uuid);
+                    done = true;
 
-                if (!pdode->vde)
-                    pdo_to_add = pdode->pdo;
+                    vc->boot_volume = true;
+                    boot_uuid = pdode->uuid;
 
-                break;
-            }
+                    if (!pdode->vde)
+                        pdo_to_add = pdode->pdo;
 
-            le2 = le2->Flink;
-        }
+                    boot_vc = vc;
 
-        if (done) {
-            le2 = pdode->children.Flink;
+                    break;
+                }
 
-            while (le2 != &pdode->children) {
-                volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
+                le2 = le2->Flink;
+            }
+
+            if (done) {
+                le2 = pdode->children.Flink;
+
+                while (le2 != &pdode->children) {
+                    volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
 
-                /* On Windows 7 we need to clear the DO_SYSTEM_BOOT_PARTITION flag of
-                 * all of our underlying partition objects - otherwise IopMountVolume
-                 * will bugcheck with UNMOUNTABLE_BOOT_VOLUME when it tries and fails
-                 * to mount one. */
-                if (vc->devobj) {
-                    PDEVICE_OBJECT dev = vc->devobj;
+                    /* On Windows 7 we need to clear the DO_SYSTEM_BOOT_PARTITION flag of
+                    * all of our underlying partition objects - otherwise IopMountVolume
+                    * will bugcheck with UNMOUNTABLE_BOOT_VOLUME when it tries and fails
+                    * to mount one. */
+                    if (vc->devobj) {
+                        PDEVICE_OBJECT dev = vc->devobj;
 
-                    ObReferenceObject(dev);
+                        ObReferenceObject(dev);
 
-                    while (dev) {
-                        PDEVICE_OBJECT dev2 = IoGetLowerDeviceObject(dev);
+                        while (dev) {
+                            PDEVICE_OBJECT dev2 = IoGetLowerDeviceObject(dev);
 
-                        dev->Flags &= ~DO_SYSTEM_BOOT_PARTITION;
+                            dev->Flags &= ~DO_SYSTEM_BOOT_PARTITION;
 
-                        ObDereferenceObject(dev);
+                            ObDereferenceObject(dev);
 
-                        dev = dev2;
+                            dev = dev2;
+                        }
                     }
+
+                    le2 = le2->Flink;
                 }
 
-                le2 = le2->Flink;
+                ExReleaseResourceLite(&pdode->child_lock);
+
+                break;
             }
 
             ExReleaseResourceLite(&pdode->child_lock);
 
-            break;
+            le = le->Flink;
         }
 
-        ExReleaseResourceLite(&pdode->child_lock);
+        ExReleaseResourceLite(&pdo_list_lock);
+    } else if (sr.type == system_root_btrfs) {
+        boot_uuid = sr.uuid;
+
+        ExAcquireResourceSharedLite(&pdo_list_lock, true);
+
+        le = pdo_list.Flink;
+        while (le != &pdo_list) {
+            pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
+
+            if (RtlCompareMemory(&pdode->uuid, &sr.uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                if (!pdode->vde)
+                    pdo_to_add = pdode->pdo;
 
-        le = le->Flink;
+                break;
+            }
+
+            le = le->Flink;
+        }
+
+        ExReleaseResourceLite(&pdo_list_lock);
     }
 
-    ExReleaseResourceLite(&pdo_list_lock);
+    if (boot_vc) {
+        NTSTATUS Status;
+        UNICODE_STRING name;
 
-    // If our FS depends on volumes that aren't there when we do our IoRegisterPlugPlayNotification calls
-    // in DriverEntry, bus_query_device_relations won't get called until it's too late. We need to do our
-    // own call to AddDevice here as a result. We need to clear the DOE_START_PENDING bits, or NtOpenFile
-    // will return STATUS_NO_SUCH_DEVICE.
-    if (pdo_to_add) {
-        pdo_device_extension* pdode = pdo_to_add->DeviceExtension;
+        /* On Windows 8, mountmgr!MountMgrFindBootVolume returns the first volume in its database
+         * with the DO_SYSTEM_BOOT_PARTITION flag set. We've cleared the bit on the underlying devices,
+         * but as it caches it we need to disable and re-enable the volume so mountmgr receives a PNP
+         * notification to refresh its list. */
 
-        AddDevice(drvobj, pdo_to_add);
+        static const WCHAR prefix[] = L"\\??";
 
-        // To stop Windows sneakily setting DOE_START_PENDING
-        pdode->dont_report = true;
+        name.Length = name.MaximumLength = boot_vc->pnp_name.Length + sizeof(prefix) - sizeof(WCHAR);
 
-        if (pdo_to_add->DeviceObjectExtension) {
-            ((DEVOBJ_EXTENSION2*)pdo_to_add->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
+        name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG);
+        if (!name.Buffer)
+            ERR("out of memory\n");
+        else {
+            RtlCopyMemory(name.Buffer, prefix, sizeof(prefix) - sizeof(WCHAR));
+            RtlCopyMemory(&name.Buffer[(sizeof(prefix) / sizeof(WCHAR)) - 1], boot_vc->pnp_name.Buffer, boot_vc->pnp_name.Length);
 
-            if (pdode && pdode->vde && pdode->vde->device)
-                ((DEVOBJ_EXTENSION2*)pdode->vde->device->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING;
-        }
+            Status = IoSetDeviceInterfaceState(&name, false);
+            if (!NT_SUCCESS(Status))
+                ERR("IoSetDeviceInterfaceState returned %08lx\n", Status);
 
-        mountmgr_notification(&pdode->uuid);
+            Status = IoSetDeviceInterfaceState(&name, true);
+            if (!NT_SUCCESS(Status))
+                ERR("IoSetDeviceInterfaceState returned %08lx\n", Status);
+
+            ExFreePool(name.Buffer);
+        }
     }
+
+    if (sr.type == system_root_btrfs || boot_vc)
+        check_boot_options();
+
+    // If our FS depends on volumes that aren't there when we do our IoRegisterPlugPlayNotification calls
+    // in DriverEntry, bus_query_device_relations won't get called until it's too late. We need to do our
+    // own call to AddDevice here as a result. We need to clear the DOE_START_PENDING bits, or NtOpenFile
+    // will return STATUS_NO_SUCH_DEVICE.
+    if (pdo_to_add)
+        boot_add_device(pdo_to_add);
 }
index 2080c5d..84495cd 100644 (file)
 #endif
 
 #include "btrfs_drv.h"
+#include "xxhash.h"
+#include "crc32c.h"
 #ifndef __REACTOS__
 #ifndef _MSC_VER
 #include <cpuid.h>
 #else
 #include <intrin.h>
 #endif
-#endif
+#endif // __REACTOS__
 #include <ntddscsi.h>
 #include "btrfs.h"
 #include <ata.h>
@@ -55,7 +57,7 @@ NTSTATUS RtlStringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat
 #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_COMPRESS_ZSTD)
+                            BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD | BTRFS_INCOMPAT_FLAGS_METADATA_UUID | BTRFS_INCOMPAT_FLAGS_RAID1C34)
 #define COMPAT_RO_SUPPORTED (BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE | BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID)
 
 static const WCHAR device_name[] = {'\\','B','t','r','f','s',0};
@@ -66,7 +68,7 @@ DEFINE_GUID(BtrfsBusInterface, 0x4d414874, 0x6865, 0x6761, 0x6d, 0x65, 0x83, 0x6
 PDRIVER_OBJECT drvobj;
 PDEVICE_OBJECT master_devobj, busobj;
 #ifndef __REACTOS__
-bool have_sse42 = false, have_sse2 = false;
+bool have_sse2 = false;
 #endif
 uint64_t num_reads = 0;
 LIST_ENTRY uid_map_list, gid_map_list;
@@ -111,6 +113,7 @@ bool degraded_wait = true;
 KEVENT mountmgr_thread_event;
 bool shutting_down = false;
 ERESOURCE boot_lock;
+extern uint64_t boot_subvol;
 
 #ifdef _DEBUG
 PFILE_OBJECT comfo = NULL;
@@ -169,9 +172,9 @@ void _debug_message(_In_ const char* func, _In_ char* s, ...) {
     }
 
 #ifdef DEBUG_LONG_MESSAGES
-    sprintf(buf2, "%p:%s:%s:%u:", PsGetCurrentThread(), func, file, line);
+    sprintf(buf2, "%p:%s:%s:%u:", (void*)PsGetCurrentThread(), func, file, line);
 #else
-    sprintf(buf2, "%p:%s:", PsGetCurrentThread(), func);
+    sprintf(buf2, "%p:%s:", (void*)PsGetCurrentThread(), func);
 #endif
     buf = &buf2[strlen(buf2)];
 
@@ -245,7 +248,7 @@ void _debug_message(_In_ const char* func, _In_ char* s, ...) {
             IoFreeMdl(Irp->MdlAddress);
 
         if (!NT_SUCCESS(Status)) {
-            DbgPrint("failed to write to COM1 - error %08x\n", Status);
+            DbgPrint("failed to write to COM1 - error %08lx\n", Status);
             goto exit;
         }
 
@@ -259,7 +262,7 @@ exit:
         Status = ZwWriteFile(log_handle, NULL, NULL, NULL, &iosb, buf2, length, NULL, NULL);
 
         if (!NT_SUCCESS(Status)) {
-            DbgPrint("failed to write to file - error %08x\n", Status);
+            DbgPrint("failed to write to file - error %08lx\n", Status);
         }
     }
 
@@ -350,7 +353,7 @@ static bool get_last_inode(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock
 
     Status = find_item(Vcb, r, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         return false;
     }
 
@@ -438,7 +441,7 @@ bool get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
 
     Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         return false;
     }
 
@@ -448,7 +451,7 @@ bool get_xattr(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vc
     }
 
     if (tp.item->size < sizeof(DIR_ITEM)) {
-        ERR("(%I64x,%x,%I64x) 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, sizeof(DIR_ITEM));
+        ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
         return false;
     }
 
@@ -496,7 +499,7 @@ end:
     if (top_level)
         IoSetTopLevelIrp(NULL);
 
-    TRACE("returning %08x\n", Status);
+    TRACE("returning %08lx\n", Status);
 
     FsRtlExitFileSystem();
 
@@ -561,7 +564,7 @@ static NTSTATUS __stdcall drv_flush_buffers(_In_ PDEVICE_OBJECT DeviceObject, _I
 end:
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
-    TRACE("returning %08x\n", Status);
+    TRACE("returning %08lx\n", Status);
 
     if (top_level)
         IoSetTopLevelIrp(NULL);
@@ -583,12 +586,18 @@ static void calculate_total_space(_In_ device_extension* Vcb, _Out_ uint64_t* to
     } else if (Vcb->data_flags & BLOCK_FLAG_RAID6) {
         nfactor = Vcb->superblock.num_devices - 2;
         dfactor = Vcb->superblock.num_devices;
+    } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C3) {
+        nfactor = 1;
+        dfactor = 3;
+    } else if (Vcb->data_flags & BLOCK_FLAG_RAID1C4) {
+        nfactor = 1;
+        dfactor = 4;
     } else {
         nfactor = 1;
         dfactor = 1;
     }
 
-    sectors_used = Vcb->superblock.bytes_used / Vcb->superblock.sector_size;
+    sectors_used = (Vcb->superblock.bytes_used / Vcb->superblock.sector_size) * nfactor / dfactor;
 
     *totalsize = (Vcb->superblock.total_bytes / Vcb->superblock.sector_size) * nfactor / dfactor;
     *freespace = sectors_used > *totalsize ? 0 : (*totalsize - sectors_used);
@@ -631,7 +640,7 @@ static bool lie_about_fs_type() {
     Status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &retlen);
 
     if (!NT_SUCCESS(Status)) {
-        ERR("ZwQueryInformationProcess returned %08x\n", Status);
+        ERR("ZwQueryInformationProcess returned %08lx\n", Status);
         return false;
     }
 
@@ -712,7 +721,7 @@ static bool lie_about_fs_type() {
 
     return false;
 }
-#endif
+#endif // __REACTOS__
 
 // version of RtlUTF8ToUnicodeN for Vista and below
 NTSTATUS utf8_to_utf16(WCHAR* dest, ULONG dest_max, ULONG* dest_len, char* src, ULONG src_len) {
@@ -1072,13 +1081,13 @@ static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT Devic
             ULONG label_len, orig_label_len;
 
             TRACE("FileFsVolumeInformation\n");
-            TRACE("max length = %u\n", IrpSp->Parameters.QueryVolume.Length);
+            TRACE("max length = %lu\n", IrpSp->Parameters.QueryVolume.Length);
 
             ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
 
             Status = utf8_to_utf16(NULL, 0, &label_len, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
             if (!NT_SUCCESS(Status)) {
-                ERR("utf8_to_utf16 returned %08x\n", Status);
+                ERR("utf8_to_utf16 returned %08lx\n", Status);
                 ExReleaseResourceLite(&Vcb->tree_lock);
                 break;
             }
@@ -1094,7 +1103,7 @@ static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT Devic
                 overflow = true;
             }
 
-            TRACE("label_len = %u\n", label_len);
+            TRACE("label_len = %lu\n", label_len);
 
             ffvi.VolumeCreationTime.QuadPart = 0; // FIXME
             ffvi.VolumeSerialNumber = Vcb->superblock.uuid.uuid[12] << 24 | Vcb->superblock.uuid.uuid[13] << 16 | Vcb->superblock.uuid.uuid[14] << 8 | Vcb->superblock.uuid.uuid[15];
@@ -1108,12 +1117,12 @@ static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT Devic
 
                 Status = utf8_to_utf16(&data->VolumeLabel[0], label_len, &bytecount, Vcb->superblock.label, (ULONG)strlen(Vcb->superblock.label));
                 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
-                    ERR("utf8_to_utf16 returned %08x\n", Status);
+                    ERR("utf8_to_utf16 returned %08lx\n", Status);
                     ExReleaseResourceLite(&Vcb->tree_lock);
                     break;
                 }
 
-                TRACE("label = %.*S\n", label_len / sizeof(WCHAR), data->VolumeLabel);
+                TRACE("label = %.*S\n", (int)(label_len / sizeof(WCHAR)), data->VolumeLabel);
             }
 
             ExReleaseResourceLite(&Vcb->tree_lock);
@@ -1142,6 +1151,7 @@ static NTSTATUS __stdcall drv_query_volume_information(_In_ PDEVICE_OBJECT Devic
                 data->Flags |= SSINFO_FLAGS_TRIM_ENABLED;
 
             BytesCopied = sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
+            Status = STATUS_SUCCESS;
 
             break;
         }
@@ -1167,7 +1177,7 @@ end:
     if (top_level)
         IoSetTopLevelIrp(NULL);
 
-    TRACE("query volume information returning %08x\n", Status);
+    TRACE("query volume information returning %08lx\n", Status);
 
     FsRtlExitFileSystem();
 
@@ -1249,6 +1259,7 @@ NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) devi
     r->root_item.num_references = 1;
     r->fcbs_version = 0;
     r->checked_for_orphans = true;
+    r->dropped = false;
     InitializeListHead(&r->fcbs);
     RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
 
@@ -1259,7 +1270,7 @@ NTSTATUS create_root(_In_ _Requires_exclusive_lock_held_(_Curr_->tree_lock) devi
 
     Status = insert_tree_item(Vcb, Vcb->root_root, id, TYPE_ROOT_ITEM, offset, ri, sizeof(ROOT_ITEM), &tp, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("insert_tree_item returned %08x\n", Status);
+        ERR("insert_tree_item returned %08lx\n", Status);
         ExFreePool(ri);
 
         if (t)
@@ -1315,7 +1326,7 @@ static NTSTATUS set_label(_In_ device_extension* Vcb, _In_ FILE_FS_LABEL_INFORMA
     NTSTATUS Status;
     ULONG vollen, i;
 
-    TRACE("label = %.*S\n", ffli->VolumeLabelLength / sizeof(WCHAR), ffli->VolumeLabel);
+    TRACE("label = %.*S\n", (int)(ffli->VolumeLabelLength / sizeof(WCHAR)), ffli->VolumeLabel);
 
     vollen = ffli->VolumeLabelLength;
 
@@ -1360,7 +1371,7 @@ release:
     ExReleaseResourceLite(&Vcb->tree_lock);
 
 end:
-    TRACE("returning %08x\n", Status);
+    TRACE("returning %08lx\n", Status);
 
     return Status;
 }
@@ -1423,7 +1434,7 @@ end:
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
 
-    TRACE("returning %08x\n", Status);
+    TRACE("returning %08lx\n", Status);
 
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
 
@@ -1445,7 +1456,7 @@ void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match,
     fn.Length = fn.MaximumLength = 0;
     Status = fileref_get_filename(fileref, &fn, NULL, &reqlen);
     if (Status != STATUS_BUFFER_OVERFLOW) {
-        ERR("fileref_get_filename returned %08x\n", Status);
+        ERR("fileref_get_filename returned %08lx\n", Status);
         return;
     }
 
@@ -1465,7 +1476,7 @@ void send_notification_fileref(_In_ file_ref* fileref, _In_ ULONG filter_match,
 
     Status = fileref_get_filename(fileref, &fn, &name_offset, &reqlen);
     if (!NT_SUCCESS(Status)) {
-        ERR("fileref_get_filename returned %08x\n", Status);
+        ERR("fileref_get_filename returned %08lx\n", Status);
         ExFreePool(fn.Buffer);
         return;
     }
@@ -1496,7 +1507,7 @@ static void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_matc
         Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, NULL);
 
         if (!NT_SUCCESS(Status))
-            ERR("open_fileref_by_inode returned %08x\n", Status);
+            ERR("open_fileref_by_inode returned %08lx\n", Status);
         else if (!parfr->deleted) {
             UNICODE_STRING fn;
             ULONG pathlen;
@@ -1504,7 +1515,7 @@ static void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_matc
             fn.Length = fn.MaximumLength = 0;
             Status = fileref_get_filename(parfr, &fn, NULL, &pathlen);
             if (Status != STATUS_BUFFER_OVERFLOW) {
-                ERR("fileref_get_filename returned %08x\n", Status);
+                ERR("fileref_get_filename returned %08lx\n", Status);
                 free_fileref(parfr);
                 break;
             }
@@ -1528,7 +1539,7 @@ static void send_notification_fcb(_In_ file_ref* fileref, _In_ ULONG filter_matc
 
             Status = fileref_get_filename(parfr, &fn, NULL, NULL);
             if (!NT_SUCCESS(Status)) {
-                ERR("fileref_get_filename returned %08x\n", Status);
+                ERR("fileref_get_filename returned %08lx\n", Status);
                 free_fileref(parfr);
                 ExFreePool(fn.Buffer);
                 break;
@@ -1669,9 +1680,16 @@ void reap_fcb(fcb* fcb) {
             fcb->subvol->fcbs_ptrs[c] = NULL;
     }
 
-    if (fcb->list_entry.Flink)
+    if (fcb->list_entry.Flink) {
         RemoveEntryList(&fcb->list_entry);
 
+        if (fcb->subvol && fcb->subvol->dropped && IsListEmpty(&fcb->subvol->fcbs)) {
+            ExDeleteResourceLite(&fcb->subvol->nonpaged->load_tree_lock);
+            ExFreePool(fcb->subvol->nonpaged);
+            ExFreePool(fcb->subvol);
+        }
+    }
+
     if (fcb->list_entry_all.Flink)
         RemoveEntryList(&fcb->list_entry_all);
 
@@ -1779,7 +1797,7 @@ void free_fileref(_Inout_ file_ref* fr) {
 
 #ifdef _DEBUG
     if (rc < 0) {
-        ERR("fileref %p: refcount now %i\n", fr, rc);
+        ERR("fileref %p: refcount now %li\n", fr, rc);
         int3;
     }
 #endif
@@ -1937,7 +1955,9 @@ void uninit(_In_ device_extension* Vcb) {
     Vcb->Vpb->DeviceObject = NULL;
     IoReleaseVpbSpinLock(irql);
 
-    RemoveEntryList(&Vcb->list_entry);
+    // FIXME - needs global_loading_lock to be held
+    if (Vcb->list_entry.Flink)
+        RemoveEntryList(&Vcb->list_entry);
 
     if (Vcb->balance.thread) {
         Vcb->balance.paused = false;
@@ -1984,7 +2004,7 @@ void uninit(_In_ device_extension* Vcb) {
 
     Status = registry_mark_volume_unmounted(&Vcb->superblock.uuid);
     if (!NT_SUCCESS(Status) && Status != STATUS_TOO_LATE)
-        WARN("registry_mark_volume_unmounted returned %08x\n", Status);
+        WARN("registry_mark_volume_unmounted returned %08lx\n", Status);
 
     for (i = 0; i < Vcb->calcthreads.num_threads; i++) {
         Vcb->calcthreads.threads[i].quit = true;
@@ -1998,7 +2018,6 @@ void uninit(_In_ device_extension* Vcb) {
         ZwClose(Vcb->calcthreads.threads[i].handle);
     }
 
-    ExDeleteResourceLite(&Vcb->calcthreads.lock);
     ExFreePool(Vcb->calcthreads.threads);
 
     time.QuadPart = 0;
@@ -2137,7 +2156,7 @@ static NTSTATUS delete_fileref_fcb(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT
     if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY && fileref->fcb->inode_item.st_size > 0) {
         Status = excise_extents(fileref->fcb->Vcb, fileref->fcb, 0, sector_align(fileref->fcb->inode_item.st_size, fileref->fcb->Vcb->superblock.sector_size), Irp, rollback);
         if (!NT_SUCCESS(Status)) {
-            ERR("excise_extents returned %08x\n", Status);
+            ERR("excise_extents returned %08lx\n", Status);
             return Status;
         }
     }
@@ -2162,7 +2181,7 @@ static NTSTATUS delete_fileref_fcb(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT
         } _SEH2_END;
 
         if (!NT_SUCCESS(Status)) {
-            ERR("CcSetFileSizes threw exception %08x\n", Status);
+            ERR("CcSetFileSizes threw exception %08lx\n", Status);
             return Status;
         }
     }
@@ -2228,7 +2247,7 @@ NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject
             } else {
                 Status = delete_fileref_fcb(fileref, FileObject, Irp, rollback);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("delete_fileref_fcb returned %08x\n", Status);
+                    ERR("delete_fileref_fcb returned %08lx\n", Status);
                     ExReleaseResourceLite(fileref->fcb->Header.Resource);
                     return Status;
                 }
@@ -2290,7 +2309,7 @@ NTSTATUS delete_fileref(_In_ file_ref* fileref, _In_opt_ PFILE_OBJECT FileObject
     // remove dir_child from parent
 
     if (fileref->dc) {
-        TRACE("delete file %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer);
+        TRACE("delete file %.*S\n", (int)(fileref->dc->name.Length / sizeof(WCHAR)), fileref->dc->name.Buffer);
 
         ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true);
         RemoveEntryList(&fileref->dc->list_entry_index);
@@ -2407,7 +2426,7 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
         fileref = ccb ? ccb->fileref : NULL;
 
         TRACE("cleanup called for FileObject %p\n", FileObject);
-        TRACE("fileref %p, refcount = %u, open_count = %u\n", fileref, fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
+        TRACE("fileref %p, refcount = %li, open_count = %li\n", fileref, fileref ? fileref->refcount : 0, fileref ? fileref->open_count : 0);
 
         ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true);
 
@@ -2455,7 +2474,7 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
 
                     Status = delete_fileref_fcb(fileref, FileObject, Irp, &rollback);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("delete_fileref_fcb returned %08x\n", Status);
+                        ERR("delete_fileref_fcb returned %08lx\n", Status);
                         do_rollback(fcb->Vcb, &rollback);
                         ExReleaseResourceLite(fileref->fcb->Header.Resource);
                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
@@ -2486,7 +2505,7 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
 
                     Status = delete_fileref(fileref, FileObject, oc > 0 && fileref->posix_delete, Irp, &rollback);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("delete_fileref returned %08x\n", Status);
+                        ERR("delete_fileref returned %08lx\n", Status);
                         do_rollback(fcb->Vcb, &rollback);
                         ExReleaseResourceLite(&fcb->Vcb->fileref_lock);
                         ExReleaseResourceLite(&fcb->Vcb->tree_lock);
@@ -2498,12 +2517,17 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
                     clear_rollback(&rollback);
                 } else if (FileObject->Flags & FO_CACHE_SUPPORTED && FileObject->SectionObjectPointer->DataSectionObject) {
                     IO_STATUS_BLOCK iosb;
-                    CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
 
-                    if (!NT_SUCCESS(iosb.Status)) {
-                        ERR("CcFlushCache returned %08x\n", iosb.Status);
+                    if (locked) {
+                        ExReleaseResourceLite(fcb->Header.Resource);
+                        locked = false;
                     }
 
+                    CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);
+
+                    if (!NT_SUCCESS(iosb.Status))
+                        ERR("CcFlushCache returned %08lx\n", iosb.Status);
+
                     if (!ExIsResourceAcquiredSharedLite(fcb->Header.PagingIoResource)) {
                         ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, true);
                         ExReleaseResourceLite(fcb->Header.PagingIoResource);
@@ -2531,7 +2555,7 @@ static NTSTATUS __stdcall drv_cleanup(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIR
     Status = STATUS_SUCCESS;
 
 exit:
-    TRACE("returning %08x\n", Status);
+    TRACE("returning %08lx\n", Status);
 
     Irp->IoStatus.Status = Status;
     Irp->IoStatus.Information = 0;
@@ -2563,7 +2587,7 @@ bool get_file_attributes_from_xattr(_In_reads_bytes_(len) char* val, _In_ uint16
                 dosnum |= val[i] + 10 - 'a';
         }
 
-        TRACE("DOSATTRIB: %08x\n", dosnum);
+        TRACE("DOSATTRIB: %08lx\n", dosnum);
 
         *atts = dosnum;
 
@@ -2700,7 +2724,7 @@ NTSTATUS sync_read_phys(_In_ PDEVICE_OBJECT DeviceObject, _In_ PFILE_OBJECT File
         } _SEH2_END;
 
         if (!NT_SUCCESS(Status)) {
-            ERR("MmProbeAndLockPages threw exception %08x\n", Status);
+            ERR("MmProbeAndLockPages threw exception %08lx\n", Status);
             IoFreeMdl(Irp->MdlAddress);
             goto exit;
         }
@@ -2734,6 +2758,63 @@ exit:
     return Status;
 }
 
+bool check_superblock_checksum(superblock* sb) {
+    switch (sb->csum_type) {
+        case CSUM_TYPE_CRC32C: {
+            uint32_t crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
+
+            if (crc32 == *((uint32_t*)sb->checksum))
+                return true;
+
+            WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
+
+            break;
+        }
+
+        case CSUM_TYPE_XXHASH: {
+            uint64_t hash = XXH64(&sb->uuid, sizeof(superblock) - sizeof(sb->checksum), 0);
+
+            if (hash == *((uint64_t*)sb->checksum))
+                return true;
+
+            WARN("superblock hash was %I64x, expected %I64x\n", hash, *((uint64_t*)sb->checksum));
+
+            break;
+        }
+
+        case CSUM_TYPE_SHA256: {
+            uint8_t hash[SHA256_HASH_SIZE];
+
+            calc_sha256(hash, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
+
+            if (RtlCompareMemory(hash, sb, SHA256_HASH_SIZE) == SHA256_HASH_SIZE)
+                return true;
+
+            WARN("superblock hash was invalid\n");
+
+            break;
+        }
+
+        case CSUM_TYPE_BLAKE2: {
+            uint8_t hash[BLAKE2_HASH_SIZE];
+
+            blake2b(hash, sizeof(hash), &sb->uuid, sizeof(superblock) - sizeof(sb->checksum));
+
+            if (RtlCompareMemory(hash, sb, BLAKE2_HASH_SIZE) == BLAKE2_HASH_SIZE)
+                return true;
+
+            WARN("superblock hash was invalid\n");
+
+            break;
+        }
+
+        default:
+            WARN("unrecognized csum type %x\n", sb->csum_type);
+    }
+
+    return false;
+}
+
 static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT fileobj, _In_ uint64_t length) {
     NTSTATUS Status;
     superblock* sb;
@@ -2758,14 +2839,12 @@ static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT
     valid_superblocks = 0;
 
     while (superblock_addrs[i] > 0) {
-        uint32_t crc32;
-
         if (i > 0 && superblock_addrs[i] + to_read > length)
             break;
 
         Status = sync_read_phys(device, fileobj, superblock_addrs[i], to_read, (PUCHAR)sb, false);
         if (!NT_SUCCESS(Status)) {
-            ERR("Failed to read superblock %u: %08x\n", i, Status);
+            ERR("Failed to read superblock %lu: %08lx\n", i, Status);
             ExFreePool(sb);
             return Status;
         }
@@ -2777,19 +2856,15 @@ static NTSTATUS read_superblock(_In_ device_extension* Vcb, _In_ PDEVICE_OBJECT
                 return STATUS_UNRECOGNIZED_VOLUME;
             }
         } else {
-            TRACE("got superblock %u!\n", i);
+            TRACE("got superblock %lu!\n", i);
 
-            crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
-
-            if (crc32 != *((uint32_t*)sb->checksum))
-                WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
-            else if (sb->sector_size == 0)
+            if (sb->sector_size == 0)
                 WARN("superblock sector size was 0\n");
             else if (sb->node_size < sizeof(tree_header) + sizeof(internal_node) || sb->node_size > 0x10000)
                 WARN("invalid node size %x\n", sb->node_size);
             else if ((sb->node_size % sb->sector_size) != 0)
                 WARN("node size %x was not a multiple of sector_size %x\n", sb->node_size, sb->sector_size);
-            else if (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation) {
+            else if (check_superblock_checksum(sb) && (valid_superblocks == 0 || sb->generation > Vcb->superblock.generation)) {
                 RtlCopyMemory(&Vcb->superblock, sb, sizeof(superblock));
                 valid_superblocks++;
             }
@@ -2870,6 +2945,7 @@ static NTSTATUS add_root(_Inout_ device_extension* Vcb, _In_ uint64_t id, _In_ u
     r->send_ops = 0;
     r->fcbs_version = 0;
     r->checked_for_orphans = false;
+    r->dropped = false;
     InitializeListHead(&r->fcbs);
     RtlZeroMemory(r->fcbs_ptrs, sizeof(LIST_ENTRY*) * 256);
 
@@ -2950,7 +3026,7 @@ static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
     Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -2961,13 +3037,13 @@ static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
             ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
 
             if (tp.item->size < offsetof(ROOT_ITEM, byte_limit)) {
-                ERR("(%I64x,%x,%I64x) 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(ROOT_ITEM, byte_limit));
+                ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(ROOT_ITEM, byte_limit));
             } else {
                 TRACE("root %I64x - address %I64x\n", tp.item->key.obj_id, ri->block_number);
 
                 Status = add_root(Vcb, tp.item->key.obj_id, ri->block_number, ri->generation, &tp);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("add_root returned %08x\n", Status);
+                    ERR("add_root returned %08lx\n", Status);
                     return Status;
                 }
             }
@@ -2997,7 +3073,7 @@ static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
         Status = create_root(Vcb, BTRFS_ROOT_DATA_RELOC, &reloc_root, false, 0, Irp);
 
         if (!NT_SUCCESS(Status)) {
-            ERR("create_root returned %08x\n", Status);
+            ERR("create_root returned %08lx\n", Status);
             return Status;
         }
 
@@ -3030,7 +3106,7 @@ static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
         Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("insert_tree_item returned %08x\n", Status);
+            ERR("insert_tree_item returned %08lx\n", Status);
             ExFreePool(ii);
             return Status;
         }
@@ -3049,7 +3125,7 @@ static NTSTATUS look_for_roots(_Requires_exclusive_lock_held_(_Curr_->tree_lock)
 
         Status = insert_tree_item(Vcb, reloc_root, SUBVOL_ROOT_INODE, TYPE_INODE_REF, SUBVOL_ROOT_INODE, ir, irlen, NULL, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("insert_tree_item returned %08x\n", Status);
+            ERR("insert_tree_item returned %08lx\n", Status);
             ExFreePool(ir);
             return Status;
         }
@@ -3084,7 +3160,7 @@ static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) dev
 
     Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -3098,14 +3174,14 @@ static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) dev
                 if (tp.item->key.offset > lastaddr) {
                     Status = add_space_entry(&dev->space, NULL, lastaddr, tp.item->key.offset - lastaddr);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("add_space_entry returned %08x\n", Status);
+                        ERR("add_space_entry returned %08lx\n", Status);
                         return Status;
                     }
                 }
 
                 lastaddr = tp.item->key.offset + de->length;
             } else {
-                ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
+                ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_EXTENT));
             }
         }
 
@@ -3121,7 +3197,7 @@ static NTSTATUS find_disk_holes(_In_ _Requires_lock_held_(_Curr_->tree_lock) dev
     if (lastaddr < dev->devitem.num_bytes) {
         Status = add_space_entry(&dev->space, NULL, lastaddr, dev->devitem.num_bytes - lastaddr);
         if (!NT_SUCCESS(Status)) {
-            ERR("add_space_entry returned %08x\n", Status);
+            ERR("add_space_entry returned %08lx\n", Status);
             return Status;
         }
     }
@@ -3243,7 +3319,7 @@ static bool is_device_removable(_In_ PDEVICE_OBJECT devobj) {
     Status = dev_ioctl(devobj, IOCTL_STORAGE_GET_HOTPLUG_INFO, NULL, 0, &shi, sizeof(STORAGE_HOTPLUG_INFO), true, NULL);
 
     if (!NT_SUCCESS(Status)) {
-        ERR("dev_ioctl returned %08x\n", Status);
+        ERR("dev_ioctl returned %08lx\n", Status);
         return false;
     }
 
@@ -3258,7 +3334,7 @@ static ULONG get_device_change_count(_In_ PDEVICE_OBJECT devobj) {
     Status = dev_ioctl(devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
 
     if (!NT_SUCCESS(Status)) {
-        ERR("dev_ioctl returned %08x\n", Status);
+        ERR("dev_ioctl returned %08lx\n", Status);
         return 0;
     }
 
@@ -3287,7 +3363,7 @@ void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ bool get_
                            &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL);
 
         if (!NT_SUCCESS(Status)) {
-            WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08x\n", Status);
+            WARN("IOCTL_STORAGE_GET_DEVICE_NUMBER returned %08lx\n", Status);
             dev->disk_num = 0xffffffff;
             dev->part_num = 0xffffffff;
         } else {
@@ -3330,7 +3406,7 @@ void init_device(_In_ device_extension* Vcb, _Inout_ device* dev, _In_ bool get_
                        apte, aptelen, true, NULL);
 
     if (!NT_SUCCESS(Status))
-        TRACE("IOCTL_ATA_PASS_THROUGH returned %08x for IDENTIFY DEVICE\n", Status);
+        TRACE("IOCTL_ATA_PASS_THROUGH returned %08lx for IDENTIFY DEVICE\n", Status);
     else {
         IDENTIFY_DEVICE_DATA* idd = (IDENTIFY_DEVICE_DATA*)((uint8_t*)apte + sizeof(ATA_PASS_THROUGH_EX));
 
@@ -3384,7 +3460,7 @@ static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) dev
 
     Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -3393,7 +3469,7 @@ static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) dev
 
         if (tp.item->key.obj_id == 1 && tp.item->key.obj_type == TYPE_DEV_ITEM) {
             if (tp.item->size < sizeof(DEV_ITEM)) {
-                ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
+                ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DEV_ITEM));
             } else {
                 DEV_ITEM* di = (DEV_ITEM*)tp.item->data;
                 LIST_ENTRY* le;
@@ -3498,7 +3574,7 @@ static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) dev
             }
         } else if (tp.item->key.obj_type == TYPE_CHUNK_ITEM) {
             if (tp.item->size < sizeof(CHUNK_ITEM)) {
-                ERR("(%I64x,%x,%I64x) 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, sizeof(CHUNK_ITEM));
+                ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(CHUNK_ITEM));
             } else {
                 c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
 
@@ -3562,7 +3638,7 @@ static NTSTATUS load_chunk_root(_In_ _Requires_lock_held_(_Curr_->tree_lock) dev
 
                     for (i = 0; i < c->chunk_item->num_stripes; i++) {
                         c->devices[i] = find_device_from_uuid(Vcb, &cis[i].dev_uuid);
-                        TRACE("device %I64u = %p\n", i, c->devices[i]);
+                        TRACE("device %u = %p\n", i, c->devices[i]);
 
                         if (!c->devices[i]) {
                             ERR("missing device\n");
@@ -3711,7 +3787,7 @@ void protect_superblocks(_Inout_ chunk* c) {
                     space_list_subtract(c, false, c->offset + off_start, off_end - off_start, NULL);
                 }
             }
-        } else { // SINGLE, DUPLICATE, RAID1
+        } else { // SINGLE, DUPLICATE, RAID1, RAID1C3, RAID1C4
             for (j = 0; j < ci->num_stripes; j++) {
                 if (cis[j].offset + ci->size > superblock_addrs[i] && cis[j].offset <= superblock_addrs[i] + sizeof(superblock)) {
                     TRACE("cut out superblock in chunk %I64x\n", c->offset);
@@ -3742,6 +3818,12 @@ uint64_t chunk_estimate_phys_size(device_extension* Vcb, chunk* c, uint64_t u) {
     } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
         nfactor = Vcb->superblock.num_devices - 2;
         dfactor = Vcb->superblock.num_devices;
+    } else if (c->chunk_item->type & BLOCK_FLAG_RAID1C3) {
+        nfactor = 1;
+        dfactor = 3;
+    } else if (c->chunk_item->type & BLOCK_FLAG_RAID1C4) {
+        nfactor = 1;
+        dfactor = 4;
     } else {
         nfactor = 1;
         dfactor = 1;
@@ -3770,7 +3852,7 @@ NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_ex
 
         Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("error - find_item returned %08x\n", Status);
+            ERR("error - find_item returned %08lx\n", Status);
             return Status;
         }
 
@@ -3784,7 +3866,7 @@ NTSTATUS find_chunk_usage(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_ex
 
                 Vcb->superblock.bytes_used += chunk_estimate_phys_size(Vcb, c, bgi->used);
             } else {
-                ERR("(%I64x;%I64x,%x,%I64x) is %u bytes, expected %u\n",
+                ERR("(%I64x;%I64x,%x,%I64x) is %u bytes, expected %Iu\n",
                     Vcb->extent_root->id, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
             }
         }
@@ -3885,7 +3967,7 @@ root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_ex
 
         Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("error - find_item returned %08x\n", Status);
+            ERR("error - find_item returned %08lx\n", Status);
             goto end;
         }
 
@@ -3895,14 +3977,14 @@ root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_ex
         }
 
         if (tp.item->size < sizeof(DIR_ITEM)) {
-            ERR("(%I64x,%x,%I64x) 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, sizeof(DIR_ITEM));
+            ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
             goto end;
         }
 
         di = (DIR_ITEM*)tp.item->data;
 
         if (tp.item->size < sizeof(DIR_ITEM) - 1 + di->n) {
-            ERR("(%I64x,%x,%I64x) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM) - 1 + di->n);
+            ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM) - 1 + di->n);
             goto end;
         }
 
@@ -3982,7 +4064,7 @@ static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
     }
 
     InitializeListHead(&Vcb->calcthreads.job_list);
-    ExInitializeResourceLite(&Vcb->calcthreads.lock);
+    KeInitializeSpinLock(&Vcb->calcthreads.spinlock);
     KeInitializeEvent(&Vcb->calcthreads.event, NotificationEvent, false);
 
     RtlZeroMemory(Vcb->calcthreads.threads, sizeof(drv_calc_thread) * Vcb->calcthreads.num_threads);
@@ -3993,13 +4075,14 @@ static NTSTATUS create_calc_threads(_In_ PDEVICE_OBJECT DeviceObject) {
         NTSTATUS Status;
 
         Vcb->calcthreads.threads[i].DeviceObject = DeviceObject;
+        Vcb->calcthreads.threads[i].number = i;
         KeInitializeEvent(&Vcb->calcthreads.threads[i].finished, NotificationEvent, false);
 
         Status = PsCreateSystemThread(&Vcb->calcthreads.threads[i].handle, 0, &oa, NULL, NULL, calc_thread, &Vcb->calcthreads.threads[i]);
         if (!NT_SUCCESS(Status)) {
             ULONG j;
 
-            ERR("PsCreateSystemThread returned %08x\n", Status);
+            ERR("PsCreateSystemThread returned %08lx\n", Status);
 
             for (j = 0; j < i; j++) {
                 Vcb->calcthreads.threads[i].quit = true;
@@ -4021,7 +4104,7 @@ static bool is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
 
     Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), true, NULL);
     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
         return false;
     }
 
@@ -4035,7 +4118,7 @@ static bool is_btrfs_volume(_In_ PDEVICE_OBJECT DeviceObject) {
 
     Status = dev_ioctl(DeviceObject, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, mdn2, mdnsize, true, NULL);
     if (!NT_SUCCESS(Status)) {
-        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
+        ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08lx\n", Status);
         ExFreePool(mdn2);
         return false;
     }
@@ -4057,7 +4140,7 @@ static NTSTATUS get_device_pnp_name_guid(_In_ PDEVICE_OBJECT DeviceObject, _Out_
 
     Status = IoGetDeviceInterfaces((PVOID)guid, NULL, 0, &list);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoGetDeviceInterfaces returned %08x\n", Status);
+        ERR("IoGetDeviceInterfaces returned %08lx\n", Status);
         return Status;
     }
 
@@ -4135,7 +4218,6 @@ static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ bool*
     NTSTATUS Status;
     ULONG to_read;
     superblock* sb;
-    uint32_t crc32;
     UNICODE_STRING pnp_name;
     const GUID* guid;
 
@@ -4149,7 +4231,7 @@ static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ bool*
 
     Status = sync_read_phys(DeviceObject, NULL, superblock_addrs[0], to_read, (PUCHAR)sb, true);
     if (!NT_SUCCESS(Status)) {
-        ERR("sync_read_phys returned %08x\n", Status);
+        ERR("sync_read_phys returned %08lx\n", Status);
         goto end;
     }
 
@@ -4158,10 +4240,7 @@ static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ bool*
         goto end;
     }
 
-    crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
-
-    if (crc32 != *((uint32_t*)sb->checksum)) {
-        WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
+    if (!check_superblock_checksum(sb)) {
         Status = STATUS_SUCCESS;
         goto end;
     }
@@ -4172,7 +4251,7 @@ static NTSTATUS check_mount_device(_In_ PDEVICE_OBJECT DeviceObject, _Out_ bool*
 
     Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
     if (!NT_SUCCESS(Status)) {
-        WARN("get_device_pnp_name returned %08x\n", Status);
+        WARN("get_device_pnp_name returned %08lx\n", Status);
         pnp_name.Length = 0;
     }
 
@@ -4207,7 +4286,7 @@ static bool still_has_superblock(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT f
 
     Status = sync_read_phys(device, fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, true);
     if (!NT_SUCCESS(Status)) {
-        ERR("Failed to read superblock: %08x\n", Status);
+        ERR("Failed to read superblock: %08lx\n", Status);
         ExFreePool(sb);
         return false;
     }
@@ -4217,10 +4296,7 @@ static bool still_has_superblock(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT f
         ExFreePool(sb);
         return false;
     } else {
-        uint32_t crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
-
-        if (crc32 != *((uint32_t*)sb->checksum)) {
-            WARN("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
+        if (!check_superblock_checksum(sb)) {
             ExFreePool(sb);
             return false;
         }
@@ -4286,7 +4362,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
         Status = check_mount_device(DeviceToMount, &not_pnp);
         if (!NT_SUCCESS(Status))
-            WARN("check_mount_device returned %08x\n", Status);
+            WARN("check_mount_device returned %08lx\n", Status);
 
         if (!not_pnp) {
             Status = STATUS_UNRECOGNIZED_VOLUME;
@@ -4386,7 +4462,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
                            &gli, sizeof(gli), true, NULL);
 
         if (!NT_SUCCESS(Status)) {
-            ERR("error reading length information: %08x\n", Status);
+            ERR("error reading length information: %08lx\n", Status);
             goto exit;
         }
 
@@ -4395,7 +4471,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = IoCreateDevice(drvobj, sizeof(device_extension), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, false, &NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoCreateDevice returned %08x\n", Status);
+        ERR("IoCreateDevice returned %08lx\n", Status);
         Status = STATUS_UNRECOGNIZED_VOLUME;
         goto exit;
     }
@@ -4447,12 +4523,15 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = registry_load_volume_options(Vcb);
     if (!NT_SUCCESS(Status)) {
-        ERR("registry_load_volume_options returned %08x\n", Status);
+        ERR("registry_load_volume_options returned %08lx\n", Status);
         goto exit;
     }
 
+    if (pdode && RtlCompareMemory(&boot_uuid, &pdode->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID) && boot_subvol != 0)
+        Vcb->options.subvol_id = boot_subvol;
+
     if (pdode && pdode->children_loaded < pdode->num_children && (!Vcb->options.allow_degraded || !finished_probing || degraded_wait)) {
-        ERR("could not mount as %u device(s) missing\n", pdode->num_children - pdode->children_loaded);
+        ERR("could not mount as %I64u device(s) missing\n", pdode->num_children - pdode->children_loaded);
         Status = STATUS_DEVICE_NOT_READY;
         goto exit;
     }
@@ -4469,6 +4548,9 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
         goto exit;
     }
 
+    if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_METADATA_UUID))
+        Vcb->superblock.metadata_uuid = Vcb->superblock.uuid;
+
     Vcb->readonly = false;
     if (Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED) {
         WARN("mounting read-only because of unsupported flags (%I64x)\n", Vcb->superblock.compat_ro_flags & ~COMPAT_RO_SUPPORTED);
@@ -4481,6 +4563,33 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     Vcb->superblock.generation++;
     Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF;
 
+    if (Vcb->superblock.log_tree_addr != 0) {
+        FIXME("FIXME - replay transaction log (clearing for now)\n");
+        Vcb->superblock.log_tree_addr = 0;
+    }
+
+    switch (Vcb->superblock.csum_type) {
+        case CSUM_TYPE_CRC32C:
+            Vcb->csum_size = sizeof(uint32_t);
+            break;
+
+        case CSUM_TYPE_XXHASH:
+            Vcb->csum_size = sizeof(uint64_t);
+            break;
+
+        case CSUM_TYPE_SHA256:
+            Vcb->csum_size = SHA256_HASH_SIZE;
+            break;
+
+        case CSUM_TYPE_BLAKE2:
+            Vcb->csum_size = BLAKE2_HASH_SIZE;
+            break;
+
+        default:
+            ERR("unrecognized csum type %x\n", Vcb->superblock.csum_type);
+            break;
+    }
+
     InitializeListHead(&Vcb->devices);
     dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG);
     if (!dev) {
@@ -4532,7 +4641,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
     InitializeListHead(&Vcb->sys_chunks);
     Status = load_sys_chunks(Vcb);
     if (!NT_SUCCESS(Status)) {
-        ERR("load_sys_chunks returned %08x\n", Status);
+        ERR("load_sys_chunks returned %08lx\n", Status);
         goto exit;
     }
 
@@ -4567,13 +4676,13 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = load_chunk_root(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("load_chunk_root returned %08x\n", Status);
+        ERR("load_chunk_root returned %08lx\n", Status);
         goto exit;
     }
 
     if (Vcb->superblock.num_devices > 1) {
         if (Vcb->devices_loaded < Vcb->superblock.num_devices && (!Vcb->options.allow_degraded || !finished_probing)) {
-            ERR("could not mount as %u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
+            ERR("could not mount as %I64u device(s) missing\n", Vcb->superblock.num_devices - Vcb->devices_loaded);
 
             IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR, NULL, NULL);
 
@@ -4619,14 +4728,14 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = look_for_roots(Vcb, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("look_for_roots returned %08x\n", Status);
+        ERR("look_for_roots returned %08lx\n", Status);
         goto exit;
     }
 
     if (!Vcb->readonly) {
         Status = find_chunk_usage(Vcb, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("find_chunk_usage returned %08x\n", Status);
+            ERR("find_chunk_usage returned %08lx\n", Status);
             goto exit;
         }
     }
@@ -4647,7 +4756,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
         Status = clear_free_space_cache(Vcb, &batchlist, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("clear_free_space_cache returned %08x\n", Status);
+            ERR("clear_free_space_cache returned %08lx\n", Status);
             clear_batch_list(Vcb, &batchlist);
             goto exit;
         }
@@ -4655,7 +4764,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = commit_batch_list(Vcb, &batchlist, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("commit_batch_list returned %08x\n", Status);
+        ERR("commit_batch_list returned %08lx\n", Status);
         goto exit;
     }
 
@@ -4729,7 +4838,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = load_dir_children(Vcb, root_fcb, true, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("load_dir_children returned %08x\n", Status);
+        ERR("load_dir_children returned %08lx\n", Status);
         goto exit;
     }
 
@@ -4739,7 +4848,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = find_item(Vcb, root_fcb->subvol, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         goto exit;
     }
 
@@ -4756,6 +4865,9 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     root_fcb->atts = get_file_attributes(Vcb, root_fcb->subvol, root_fcb->inode, root_fcb->type, false, false, Irp);
 
+    if (root_fcb->subvol->id == BTRFS_ROOT_FSTREE)
+        root_fcb->atts &= ~FILE_ATTRIBUTE_HIDDEN;
+
     Vcb->root_fileref = create_fileref(Vcb);
     if (!Vcb->root_fileref) {
         ERR("out of memory\n");
@@ -4802,7 +4914,7 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
         Status = find_disk_holes(Vcb, dev2, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("find_disk_holes returned %08x\n", Status);
+            ERR("find_disk_holes returned %08lx\n", Status);
             goto exit;
         }
 
@@ -4827,23 +4939,23 @@ static NTSTATUS mount_vol(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
 
     Status = PsCreateSystemThread(&Vcb->flush_thread_handle, 0, &oa, NULL, NULL, flush_thread, NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
-        ERR("PsCreateSystemThread returned %08x\n", Status);
+        ERR("PsCreateSystemThread returned %08lx\n", Status);
         goto exit;
     }
 
     Status = create_calc_threads(NewDeviceObject);
     if (!NT_SUCCESS(Status)) {
-        ERR("create_calc_threads returned %08x\n", Status);
+        ERR("create_calc_threads returned %08lx\n", Status);
         goto exit;
     }
 
     Status = registry_mark_volume_mounted(&Vcb->superblock.uuid);
     if (!NT_SUCCESS(Status))
-        WARN("registry_mark_volume_mounted returned %08x\n", Status);
+        WARN("registry_mark_volume_mounted returned %08lx\n", Status);
 
     Status = look_for_balance_item(Vcb);
     if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND)
-        WARN("look_for_balance_item returned %08x\n", Status);
+        WARN("look_for_balance_item returned %08lx\n", Status);
 
     Status = STATUS_SUCCESS;
 
@@ -4928,7 +5040,6 @@ exit2:
 static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
     NTSTATUS Status;
     superblock* sb;
-    uint32_t crc32;
     ULONG to_read, cc;
 
     if (!dev->devobj)
@@ -4940,7 +5051,7 @@ static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
         Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), true, &iosb);
 
         if (IoIsErrorUserInduced(Status)) {
-            ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x (user-induced)\n", Status);
+            ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx (user-induced)\n", Status);
 
             if (Vcb->vde) {
                 pdo_device_extension* pdode = Vcb->vde->pdode;
@@ -4969,7 +5080,7 @@ static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
                     ExReleaseResourceLite(&pdode->child_lock);
             }
         } else if (!NT_SUCCESS(Status)) {
-            ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08x\n", Status);
+            ERR("IOCTL_STORAGE_CHECK_VERIFY returned %08lx\n", Status);
             return Status;
         } else if (iosb.Information < sizeof(ULONG)) {
             ERR("iosb.Information was too short\n");
@@ -4989,7 +5100,7 @@ static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
 
     Status = sync_read_phys(dev->devobj, dev->fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, true);
     if (!NT_SUCCESS(Status)) {
-        ERR("Failed to read superblock: %08x\n", Status);
+        ERR("Failed to read superblock: %08lx\n", Status);
         ExFreePool(sb);
         return Status;
     }
@@ -5000,11 +5111,7 @@ static NTSTATUS verify_device(_In_ device_extension* Vcb, _Inout_ device* dev) {
         return STATUS_WRONG_VOLUME;
     }
 
-    crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
-    TRACE("crc32 was %08x, expected %08x\n", crc32, *((uint32_t*)sb->checksum));
-
-    if (crc32 != *((uint32_t*)sb->checksum)) {
-        ERR("checksum error\n");
+    if (!check_superblock_checksum(sb)) {
         ExFreePool(sb);
         return STATUS_WRONG_VOLUME;
     }
@@ -5146,7 +5253,7 @@ static NTSTATUS __stdcall drv_file_system_control(_In_ PDEVICE_OBJECT DeviceObje
     }
 
 end:
-    TRACE("returning %08x\n", Status);
+    TRACE("returning %08lx\n", Status);
 
     if (Irp) {
         Irp->IoStatus.Status = Status;
@@ -5199,7 +5306,7 @@ static NTSTATUS __stdcall drv_lock_control(_In_ PDEVICE_OBJECT DeviceObject, _In
     fcb->Header.IsFastIoPossible = fast_io_possible(fcb);
 
 exit:
-    TRACE("returning %08x\n", Status);
+    TRACE("returning %08lx\n", Status);
 
     if (top_level)
         IoSetTopLevelIrp(NULL);
@@ -5222,12 +5329,16 @@ void do_shutdown(PIRP Irp) {
 
         device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
         volume_device_extension* vde = Vcb->vde;
+        PDEVICE_OBJECT devobj = vde ? vde->device : NULL;
 
         TRACE("shutting down Vcb %p\n", Vcb);
 
         if (vde)
             InterlockedIncrement(&vde->open_count);
 
+        if (devobj)
+            ObReferenceObject(devobj);
+
         dismount_volume(Vcb, true, Irp);
 
         if (vde) {
@@ -5236,11 +5347,12 @@ void do_shutdown(PIRP Irp) {
             PDEVICE_OBJECT mountmgr;
             PFILE_OBJECT mountmgrfo;
             KIRQL irql;
+            PVPB newvpb;
 
             RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME);
             Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr);
             if (!NT_SUCCESS(Status))
-                ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+                ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
             else {
                 remove_drive_letter(mountmgr, &vde->name);
 
@@ -5249,14 +5361,30 @@ void do_shutdown(PIRP Irp) {
 
             vde->removing = true;
 
+            newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
+            if (!newvpb) {
+                ERR("out of memory\n");
+                return;
+            }
+
+            RtlZeroMemory(newvpb, sizeof(VPB));
+
+            newvpb->Type = IO_TYPE_VPB;
+            newvpb->Size = sizeof(VPB);
+            newvpb->RealDevice = newvpb->DeviceObject = vde->device;
+            newvpb->Flags = VPB_DIRECT_WRITES_ALLOWED;
+
             IoAcquireVpbSpinLock(&irql);
-            vde->device->Vpb->DeviceObject = vde->device;
+            vde->device->Vpb = newvpb;
             IoReleaseVpbSpinLock(irql);
 
             if (InterlockedDecrement(&vde->open_count) == 0)
                 free_vol(vde);
         }
 
+        if (devobj)
+            ObDereferenceObject(devobj);
+
         le = le2;
     }
 
@@ -5342,6 +5470,114 @@ end:
     return Status;
 }
 
+static bool device_still_valid(device* dev, uint64_t expected_generation) {
+    NTSTATUS Status;
+    unsigned int to_read;
+    superblock* sb;
+
+    to_read = (unsigned int)(dev->devobj->SectorSize == 0 ? sizeof(superblock) : sector_align(sizeof(superblock), dev->devobj->SectorSize));
+
+    sb = ExAllocatePoolWithTag(NonPagedPool, to_read, ALLOC_TAG);
+    if (!sb) {
+        ERR("out of memory\n");
+        return false;
+    }
+
+    Status = sync_read_phys(dev->devobj, dev->fileobj, superblock_addrs[0], to_read, (PUCHAR)sb, false);
+    if (!NT_SUCCESS(Status)) {
+        ERR("sync_read_phys returned %08lx\n", Status);
+        ExFreePool(sb);
+        return false;
+    }
+
+    if (sb->magic != BTRFS_MAGIC) {
+        ERR("magic not found\n");
+        ExFreePool(sb);
+        return false;
+    }
+
+    if (!check_superblock_checksum(sb)) {
+        ExFreePool(sb);
+        return false;
+    }
+
+    if (sb->generation > expected_generation) {
+        ERR("generation was %I64x, expected %I64x\n", sb->generation, expected_generation);
+        ExFreePool(sb);
+        return false;
+    }
+
+    ExFreePool(sb);
+
+    return true;
+}
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+static void __stdcall check_after_wakeup(PDEVICE_OBJECT DeviceObject, PVOID con) {
+    device_extension* Vcb = (device_extension*)con;
+    LIST_ENTRY* le;
+
+    UNUSED(DeviceObject);
+
+    ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true);
+
+    le = Vcb->devices.Flink;
+
+    // FIXME - do reads in parallel?
+
+    while (le != &Vcb->devices) {
+        device* dev = CONTAINING_RECORD(le, device, list_entry);
+
+        if (dev->devobj) {
+            if (!device_still_valid(dev, Vcb->superblock.generation - 1)) {
+                PDEVICE_OBJECT voldev = Vcb->Vpb->RealDevice;
+                KIRQL irql;
+                PVPB newvpb;
+
+                WARN("forcing remount\n");
+
+                newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG);
+                if (!newvpb) {
+                    ERR("out of memory\n");
+                    return;
+                }
+
+                RtlZeroMemory(newvpb, sizeof(VPB));
+
+                newvpb->Type = IO_TYPE_VPB;
+                newvpb->Size = sizeof(VPB);
+                newvpb->RealDevice = voldev;
+                newvpb->Flags = VPB_DIRECT_WRITES_ALLOWED;
+
+                Vcb->removing = true;
+
+                IoAcquireVpbSpinLock(&irql);
+                voldev->Vpb = newvpb;
+                IoReleaseVpbSpinLock(irql);
+
+                Vcb->vde = NULL;
+
+                ExReleaseResourceLite(&Vcb->tree_lock);
+
+                if (Vcb->open_files == 0)
+                    uninit(Vcb);
+                else { // remove from VcbList
+                    ExAcquireResourceExclusiveLite(&global_loading_lock, true);
+                    RemoveEntryList(&Vcb->list_entry);
+                    Vcb->list_entry.Flink = NULL;
+                    ExReleaseResourceLite(&global_loading_lock);
+                }
+
+                return;
+            }
+        }
+
+        le = le->Flink;
+    }
+
+    ExReleaseResourceLite(&Vcb->tree_lock);
+}
+
 _Dispatch_type_(IRP_MJ_POWER)
 _Function_class_(DRIVER_DISPATCH)
 static NTSTATUS __stdcall drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
@@ -5350,7 +5586,7 @@ static NTSTATUS __stdcall drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP
     device_extension* Vcb = DeviceObject->DeviceExtension;
     bool top_level;
 
-    FsRtlEnterFileSystem();
+    // no need for FsRtlEnterFileSystem, as this only ever gets called in a system thread
 
     top_level = is_top_level(Irp);
 
@@ -5359,6 +5595,46 @@ static NTSTATUS __stdcall drv_power(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP
     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
         volume_device_extension* vde = DeviceObject->DeviceExtension;
 
+        if (IrpSp->MinorFunction == IRP_MN_QUERY_POWER && IrpSp->Parameters.Power.Type == SystemPowerState &&
+            IrpSp->Parameters.Power.State.SystemState != PowerSystemWorking && vde->mounted_device) {
+            device_extension* Vcb2 = vde->mounted_device->DeviceExtension;
+
+            /* If power state is about to go to sleep or hibernate, do a flush. We do this on IRP_MJ_QUERY_POWER
+            * rather than IRP_MJ_SET_POWER because we know that the hard disks are still awake. */
+
+            if (Vcb2) {
+                ExAcquireResourceExclusiveLite(&Vcb2->tree_lock, true);
+
+                if (Vcb2->need_write && !Vcb2->readonly) {
+                    TRACE("doing protective flush on power state change\n");
+                    Status = do_write(Vcb2, NULL);
+                } else
+                    Status = STATUS_SUCCESS;
+
+                free_trees(Vcb2);
+
+                if (!NT_SUCCESS(Status))
+                    ERR("do_write returned %08lx\n", Status);
+
+                ExReleaseResourceLite(&Vcb2->tree_lock);
+            }
+        } else if (IrpSp->MinorFunction == IRP_MN_SET_POWER && IrpSp->Parameters.Power.Type == SystemPowerState &&
+            IrpSp->Parameters.Power.State.SystemState == PowerSystemWorking && vde->mounted_device) {
+            device_extension* Vcb2 = vde->mounted_device->DeviceExtension;
+
+            /* If waking up, make sure that the FS hasn't been changed while we've been out (e.g., by dual-boot Linux) */
+
+            if (Vcb2) {
+                PIO_WORKITEM work_item;
+
+                work_item = IoAllocateWorkItem(DeviceObject);
+                if (!work_item) {
+                    ERR("out of memory\n");
+                } else
+                    IoQueueWorkItem(work_item, check_after_wakeup, DelayedWorkQueue, Vcb2);
+            }
+        }
+
         PoStartNextPowerIrp(Irp);
         IoSkipCurrentIrpStackLocation(Irp);
         Status = PoCallDriver(vde->attached_device, Irp);
@@ -5393,8 +5669,6 @@ exit:
     if (top_level)
         IoSetTopLevelIrp(NULL);
 
-    FsRtlExitFileSystem();
-
     return Status;
 }
 
@@ -5583,7 +5857,7 @@ static void init_serial(bool first_time) {
 
     Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoGetDeviceObjectPointer returned %08x\n", Status);
+        ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
 
         if (first_time) {
             OBJECT_ATTRIBUTES oa;
@@ -5592,7 +5866,7 @@ static void init_serial(bool first_time) {
 
             Status = PsCreateSystemThread(&serial_thread_handle, 0, &oa, NULL, NULL, serial_thread, NULL);
             if (!NT_SUCCESS(Status)) {
-                ERR("PsCreateSystemThread returned %08x\n", Status);
+                ERR("PsCreateSystemThread returned %08lx\n", Status);
                 return;
             }
         }
@@ -5600,22 +5874,25 @@ static void init_serial(bool first_time) {
 }
 #endif
 
-#ifndef __REACTOS__
+#if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
 static void check_cpu() {
     unsigned int cpuInfo[4];
+    bool have_sse42;
+
 #ifndef _MSC_VER
     __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
     have_sse42 = cpuInfo[2] & bit_SSE4_2;
     have_sse2 = cpuInfo[3] & bit_SSE2;
 #else
-   __cpuid(cpuInfo, 1);
-   have_sse42 = cpuInfo[2] & (1 << 20);
-   have_sse2 = cpuInfo[3] & (1 << 26);
+    __cpuid(cpuInfo, 1);
+    have_sse42 = cpuInfo[2] & (1 << 20);
+    have_sse2 = cpuInfo[3] & (1 << 26);
 #endif
 
-    if (have_sse42)
+    if (have_sse42) {
         TRACE("SSE4.2 is supported\n");
-    else
+        calc_crc32c = calc_crc32c_hw;
+    } else
         TRACE("SSE4.2 not supported\n");
 
     if (have_sse2)
@@ -5645,7 +5922,7 @@ static void init_logging() {
                               FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
 
         if (!NT_SUCCESS(Status)) {
-            ERR("ZwCreateFile returned %08x\n", Status);
+            ERR("ZwCreateFile returned %08lx\n", Status);
             goto end;
         }
 
@@ -5660,7 +5937,7 @@ static void init_logging() {
             Status = ZwQueryInformationFile(log_handle, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
 
             if (!NT_SUCCESS(Status)) {
-                ERR("ZwQueryInformationFile returned %08x\n", Status);
+                ERR("ZwQueryInformationFile returned %08lx\n", Status);
                 goto end;
             }
 
@@ -5669,14 +5946,14 @@ static void init_logging() {
             Status = ZwSetInformationFile(log_handle, &iosb, &fpi, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation);
 
             if (!NT_SUCCESS(Status)) {
-                ERR("ZwSetInformationFile returned %08x\n", Status);
+                ERR("ZwSetInformationFile returned %08lx\n", Status);
                 goto end;
             }
 
             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);
+                ERR("ZwWriteFile returned %08lx\n", Status);
                 goto end;
             }
         }
@@ -5699,7 +5976,7 @@ static void init_logging() {
         ExFreePool(dateline);
 
         if (!NT_SUCCESS(Status)) {
-            ERR("ZwWriteFile returned %08x\n", Status);
+            ERR("ZwWriteFile returned %08lx\n", Status);
             goto end;
         }
     }
@@ -5737,10 +6014,17 @@ NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT Physica
     LIST_ENTRY* le;
     NTSTATUS Status;
     UNICODE_STRING volname;
-    ULONG i, j;
+    ULONG i;
+    WCHAR* s;
     pdo_device_extension* pdode = NULL;
     PDEVICE_OBJECT voldev;
     volume_device_extension* vde;
+    UNICODE_STRING arc_name_us;
+    WCHAR* anp;
+
+    static const WCHAR arc_name_prefix[] = L"\\ArcName\\btrfs(";
+
+    WCHAR arc_name[(sizeof(arc_name_prefix) / sizeof(WCHAR)) - 1 + 37];
 
     TRACE("(%p, %p)\n", DriverObject, PhysicalDeviceObject);
 
@@ -5781,27 +6065,44 @@ NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT Physica
     }
 
     RtlCopyMemory(volname.Buffer, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
+    RtlCopyMemory(arc_name, arc_name_prefix, sizeof(arc_name_prefix) - sizeof(WCHAR));
+
+    anp = &arc_name[(sizeof(arc_name_prefix) / sizeof(WCHAR)) - 1];
+    s = &volname.Buffer[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
 
-    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++;
+        *s = *anp = hex_digit(pdode->uuid.uuid[i] >> 4);
+        s++;
+        anp++;
+
+        *s = *anp = hex_digit(pdode->uuid.uuid[i] & 0xf);
+        s++;
+        anp++;
 
         if (i == 3 || i == 5 || i == 7 || i == 9) {
-            volname.Buffer[j] = '-';
-            j++;
+            *s = *anp = '-';
+            s++;
+            anp++;
         }
     }
 
-    volname.Buffer[j] = '}';
+    *s = '}';
+    *anp = ')';
 
     Status = IoCreateDevice(drvobj, sizeof(volume_device_extension), &volname, FILE_DEVICE_DISK,
                             WdmlibRtlIsNtDdiVersionAvailable(NTDDI_WIN8) ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL : 0, false, &voldev);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoCreateDevice returned %08x\n", Status);
+        ERR("IoCreateDevice returned %08lx\n", Status);
         goto end2;
     }
 
+    arc_name_us.Buffer = arc_name;
+    arc_name_us.Length = arc_name_us.MaximumLength = sizeof(arc_name);
+
+    Status = IoCreateSymbolicLink(&arc_name_us, &volname);
+    if (!NT_SUCCESS(Status))
+        WARN("IoCreateSymbolicLink returned %08lx\n", Status);
+
     voldev->SectorSize = PhysicalDeviceObject->SectorSize;
     voldev->Flags |= DO_DIRECT_IO;
 
@@ -5818,7 +6119,7 @@ NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT Physica
 
     Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_VOLUME, NULL, &vde->bus_name);
     if (!NT_SUCCESS(Status))
-        WARN("IoRegisterDeviceInterface returned %08x\n", Status);
+        WARN("IoRegisterDeviceInterface returned %08lx\n", Status);
 
     vde->attached_device = IoAttachDeviceToDeviceStack(voldev, PhysicalDeviceObject);
 
@@ -5827,11 +6128,16 @@ NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT Physica
     if (pdode->removable)
         voldev->Characteristics |= FILE_REMOVABLE_MEDIA;
 
+    if (RtlCompareMemory(&boot_uuid, &pdode->uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+        voldev->Flags |= DO_SYSTEM_BOOT_PARTITION;
+        PhysicalDeviceObject->Flags |= DO_SYSTEM_BOOT_PARTITION;
+    }
+
     voldev->Flags &= ~DO_DEVICE_INITIALIZING;
 
     Status = IoSetDeviceInterfaceState(&vde->bus_name, true);
     if (!NT_SUCCESS(Status))
-        WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+        WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
 
     Status = STATUS_SUCCESS;
 
@@ -5890,7 +6196,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
     TRACE("DriverEntry\n");
 
-#ifndef __REACTOS__
+#if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
     check_cpu();
 #endif
 
@@ -6003,7 +6309,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
     Status = IoCreateDevice(DriverObject, sizeof(control_device_extension), &device_nameW, FILE_DEVICE_DISK_FILE_SYSTEM,
                             FILE_DEVICE_SECURE_OPEN, false, &DeviceObject);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoCreateDevice returned %08x\n", Status);
+        ERR("IoCreateDevice returned %08lx\n", Status);
         return Status;
     }
 
@@ -6018,7 +6324,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
     Status = IoCreateSymbolicLink(&dosdevice_nameW, &device_nameW);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoCreateSymbolicLink returned %08x\n", Status);
+        ERR("IoCreateSymbolicLink returned %08lx\n", Status);
         return Status;
     }
 
@@ -6033,7 +6339,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
     InitializeObjectAttributes(&oa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
     Status = ZwCreateKey(&regh, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
     if (!NT_SUCCESS(Status)) {
-        ERR("ZwCreateKey returned %08x\n", Status);
+        ERR("ZwCreateKey returned %08lx\n", Status);
         return Status;
     }
 
@@ -6042,7 +6348,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
     Status = IoCreateDevice(DriverObject, sizeof(bus_device_extension), NULL, FILE_DEVICE_UNKNOWN,
                             FILE_DEVICE_SECURE_OPEN, false, &busobj);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoCreateDevice returned %08x\n", Status);
+        ERR("IoCreateDevice returned %08lx\n", Status);
         return Status;
     }
 
@@ -6055,13 +6361,13 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
     Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF,
                                     NULL, NULL, 0, &bde->buspdo);
     if (!NT_SUCCESS(Status)) {
-        ERR("IoReportDetectedDevice returned %08x\n", Status);
+        ERR("IoReportDetectedDevice returned %08lx\n", Status);
         return Status;
     }
 
     Status = IoRegisterDeviceInterface(bde->buspdo, &BtrfsBusInterface, NULL, &bde->bus_name);
     if (!NT_SUCCESS(Status))
-        WARN("IoRegisterDeviceInterface returned %08x\n", Status);
+        WARN("IoRegisterDeviceInterface returned %08lx\n", Status);
 
     bde->attached_device = IoAttachDeviceToDeviceStack(busobj, bde->buspdo);
 
@@ -6069,7 +6375,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
     Status = IoSetDeviceInterfaceState(&bde->bus_name, true);
     if (!NT_SUCCESS(Status))
-        WARN("IoSetDeviceInterfaceState returned %08x\n", Status);
+        WARN("IoSetDeviceInterfaceState returned %08lx\n", Status);
 
     IoInvalidateDeviceRelations(bde->buspdo, BusRelations);
 
@@ -6077,24 +6383,24 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
     Status = PsCreateSystemThread(&degraded_wait_handle, 0, &system_thread_attributes, NULL, NULL, degraded_wait_thread, NULL);
     if (!NT_SUCCESS(Status))
-        WARN("PsCreateSystemThread returned %08x\n", Status);
+        WARN("PsCreateSystemThread returned %08lx\n", Status);
 
     ExInitializeResourceLite(&boot_lock);
 
     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
                                             (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry2);
     if (!NT_SUCCESS(Status))
-        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+        ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
 
     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
                                             (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, &notification_entry3);
     if (!NT_SUCCESS(Status))
-        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+        ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
 
     Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
                                             (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, pnp_notification, DriverObject, &notification_entry);
     if (!NT_SUCCESS(Status))
-        ERR("IoRegisterPlugPlayNotification returned %08x\n", Status);
+        ERR("IoRegisterPlugPlayNotification returned %08lx\n", Status);
 
     finished_probing = true;
 
@@ -6102,7 +6408,7 @@ NTSTATUS __stdcall DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_S
 
     Status = PsCreateSystemThread(&mountmgr_thread_handle, 0, &system_thread_attributes, NULL, NULL, mountmgr_thread, NULL);
     if (!NT_SUCCESS(Status))
-        WARN("PsCreateSystemThread returned %08x\n", Status);
+        WARN("PsCreateSystemThread returned %08lx\n", Status);
 
     IoRegisterFileSystem(DeviceObject);
 
index 780bb5d..6ff6802 100644 (file)
@@ -80,6 +80,8 @@ static const uint64_t superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0
 #define BLOCK_FLAG_RAID10       0x040
 #define BLOCK_FLAG_RAID5        0x080
 #define BLOCK_FLAG_RAID6        0x100
+#define BLOCK_FLAG_RAID1C3      0x200
+#define BLOCK_FLAG_RAID1C4      0x400
 
 #define FREE_SPACE_CACHE_ID     0xFFFFFFFFFFFFFFF5
 #define EXTENT_CSUM_ID          0xFFFFFFFFFFFFFFF6
@@ -113,11 +115,18 @@ static const uint64_t superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0
 #define BTRFS_INCOMPAT_FLAGS_RAID56             0x0080
 #define BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA    0x0100
 #define BTRFS_INCOMPAT_FLAGS_NO_HOLES           0x0200
+#define BTRFS_INCOMPAT_FLAGS_METADATA_UUID      0x0400
+#define BTRFS_INCOMPAT_FLAGS_RAID1C34           0x0800
 
 #define BTRFS_SUPERBLOCK_FLAGS_SEEDING   0x100000000
 
 #define BTRFS_ORPHAN_INODE_OBJID         0xFFFFFFFFFFFFFFFB
 
+#define CSUM_TYPE_CRC32C        0
+#define CSUM_TYPE_XXHASH        1
+#define CSUM_TYPE_SHA256        2
+#define CSUM_TYPE_BLAKE2        3
+
 #pragma pack(push, 1)
 
 typedef struct {
@@ -236,7 +245,8 @@ typedef struct {
     char label[MAX_LABEL_SIZE];
     uint64_t cache_generation;
     uint64_t uuid_tree_generation;
-    uint64_t reserved[30];
+    BTRFS_UUID metadata_uuid;
+    uint64_t reserved[28];
     uint8_t sys_chunk_array[SYS_CHUNK_ARRAY_SIZE];
     superblock_backup backup[BTRFS_NUM_BACKUP_ROOTS];
     uint8_t reserved2[565];
index a594c60..759ad42 100644 (file)
@@ -51,8 +51,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,5,0,0
- PRODUCTVERSION 1,5,0,0
+ FILEVERSION 1,7,2,0
+ PRODUCTVERSION 1,7,2,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -68,12 +68,12 @@ BEGIN
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs"
-            VALUE "FileVersion", "1.5"
+            VALUE "FileVersion", "1.7.2"
             VALUE "InternalName", "btrfs"
-            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-19"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-20"
             VALUE "OriginalFilename", "btrfs.sys"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "1.5"
+            VALUE "ProductVersion", "1.7.2"
         END
     END
     BLOCK "VarFileInfo"
index 24307d9..344223e 100644 (file)
 #include <stddef.h>
 #include <stdint.h>
 #include <stdbool.h>
-#ifndef __REACTOS__
-// Not actually used
-#include <emmintrin.h>
-#endif /* __REACTOS__ */
 #include "btrfs.h"
 #include "btrfsioctl.h"
 
+#if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_))
+#include <emmintrin.h>
+#endif
+
 #ifdef __REACTOS__
 C_ASSERT(sizeof(bool) == 1);
 #endif
@@ -117,12 +117,16 @@ C_ASSERT(sizeof(bool) == 1);
 
 #define READ_AHEAD_GRANULARITY COMPRESSED_EXTENT_SIZE // really ought to be a multiple of COMPRESSED_EXTENT_SIZE
 
-#define IO_REPARSE_TAG_LXSS_SYMLINK 0xa000001d // undocumented?
+#ifndef IO_REPARSE_TAG_LX_SYMLINK
 
-#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 IO_REPARSE_TAG_LX_SYMLINK 0xa000001d
+
+#define IO_REPARSE_TAG_AF_UNIX          0x80000023
+#define IO_REPARSE_TAG_LX_FIFO          0x80000024
+#define IO_REPARSE_TAG_LX_CHR           0x80000025
+#define IO_REPARSE_TAG_LX_BLK           0x80000026
+
+#endif
 
 #define BTRFS_VOLUME_PREFIX L"\\Device\\Btrfs{"
 
@@ -140,7 +144,7 @@ C_ASSERT(sizeof(bool) == 1);
 #ifdef __GNUC__
 #define InterlockedIncrement64(a) __sync_add_and_fetch(a, 1)
 #endif
-#endif
+#endif // __REACTOS__
 
 #ifndef FILE_SUPPORTS_BLOCK_REFCOUNTING
 #define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000
@@ -154,10 +158,22 @@ C_ASSERT(sizeof(bool) == 1);
 #define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000
 #endif
 
+#ifndef __REACTOS__
+#ifndef _MSC_VER
 typedef struct _FILE_ID_128 {
     UCHAR Identifier[16];
 } FILE_ID_128, *PFILE_ID_128;
 
+#define FILE_CS_FLAG_CASE_SENSITIVE_DIR                 1
+#endif
+#else
+typedef struct _FILE_ID_128 {
+    UCHAR Identifier[16];
+} FILE_ID_128, *PFILE_ID_128;
+
+#define FILE_CS_FLAG_CASE_SENSITIVE_DIR                 1
+#endif // __REACTOS__
+
 typedef struct _DUPLICATE_EXTENTS_DATA {
     HANDLE FileHandle;
     LARGE_INTEGER SourceFileOffset;
@@ -190,9 +206,6 @@ typedef struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER {
 #define _Requires_lock_held_(a)
 #define _Requires_exclusive_lock_held_(a)
 #define _Releases_lock_(a)
-#define _Out_writes_bytes_opt_(a)
-#define _Pre_satisfies_(a)
-#define _Post_satisfies_(a)
 #define _Releases_exclusive_lock_(a)
 #define _Dispatch_type_(a)
 #define _Create_lock_level_(a)
@@ -202,12 +215,14 @@ typedef struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER {
 #define _Acquires_exclusive_lock_(a)
 #define _Acquires_shared_lock_(a)
 #endif
-#endif
+#endif // __REACTOS__
 
 _Create_lock_level_(tree_lock)
 _Create_lock_level_(fcb_lock)
 _Lock_level_order_(tree_lock, fcb_lock)
 
+#define MAX_HASH_SIZE 32
+
 struct _device_extension;
 
 typedef struct _fcb_nonpaged {
@@ -226,7 +241,7 @@ typedef struct {
     bool unique;
     bool ignore;
     bool inserted;
-    uint32_t* csum;
+    void* csum;
 
     LIST_ENTRY list_entry;
 
@@ -461,6 +476,7 @@ typedef struct _root {
     LONG send_ops;
     uint64_t fcbs_version;
     bool checked_for_orphans;
+    bool dropped;
     LIST_ENTRY fcbs;
     LIST_ENTRY* fcbs_ptrs[256];
     LIST_ENTRY list_entry;
@@ -617,27 +633,42 @@ typedef struct {
     LIST_ENTRY list_entry;
 } sys_chunk;
 
+enum calc_thread_type {
+    calc_thread_crc32c,
+    calc_thread_xxhash,
+    calc_thread_sha256,
+    calc_thread_blake2,
+    calc_thread_decomp_zlib,
+    calc_thread_decomp_lzo,
+    calc_thread_decomp_zstd,
+    calc_thread_comp_zlib,
+    calc_thread_comp_lzo,
+    calc_thread_comp_zstd,
+};
+
 typedef struct {
-    uint8_t* data;
-    uint32_t* csum;
-    uint32_t sectors;
-    LONG pos, done;
-    KEVENT event;
-    LONG refcount;
     LIST_ENTRY list_entry;
+    void* in;
+    void* out;
+    unsigned int inlen, outlen, off, space_left;
+    LONG left, not_started;
+    KEVENT event;
+    enum calc_thread_type type;
+    NTSTATUS Status;
 } calc_job;
 
 typedef struct {
     PDEVICE_OBJECT DeviceObject;
     HANDLE handle;
     KEVENT finished;
+    unsigned int number;
     bool quit;
 } drv_calc_thread;
 
 typedef struct {
     ULONG num_threads;
     LIST_ENTRY job_list;
-    ERESOURCE lock;
+    KSPIN_LOCK spinlock;
     drv_calc_thread* threads;
     KEVENT event;
 } drv_calc_threads;
@@ -744,6 +775,7 @@ typedef struct _device_extension {
 #endif
     uint64_t devices_loaded;
     superblock superblock;
+    unsigned int csum_size;
     bool readonly;
     bool removing;
     bool locked;
@@ -839,6 +871,7 @@ typedef struct {
     void* notification_entry;
     ULONG disk_num;
     ULONG part_num;
+    bool boot_volume;
     LIST_ENTRY list_entry;
 } volume_child;
 
@@ -1134,6 +1167,7 @@ _Ret_maybenull_
 root* find_default_subvol(_In_ _Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, _In_opt_ PIRP Irp);
 
 void do_shutdown(PIRP Irp);
+bool check_superblock_checksum(superblock* sb);
 
 #ifdef _MSC_VER
 #define funcname __FUNCTION__
@@ -1159,6 +1193,10 @@ extern uint32_t mount_readonly;
 extern uint32_t mount_no_root_dir;
 extern uint32_t no_pnp;
 
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
 #ifdef _DEBUG
 
 extern bool log_started;
@@ -1166,25 +1204,25 @@ extern uint32_t debug_log_level;
 
 #ifdef DEBUG_LONG_MESSAGES
 
-#define MSG(fn, file, line, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, file, line, s, ##__VA_ARGS__) : 0
+#define MSG(fn, file, line, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, file, line, s, ##__VA_ARGS__) : (void)0
 
 #define TRACE(s, ...) MSG(funcname, __FILE__, __LINE__, s, 3, ##__VA_ARGS__)
 #define WARN(s, ...) MSG(funcname, __FILE__, __LINE__, s, 2, ##__VA_ARGS__)
 #define FIXME(s, ...) MSG(funcname, __FILE__, __LINE__, s, 1, ##__VA_ARGS__)
 #define ERR(s, ...) MSG(funcname, __FILE__, __LINE__, s, 1, ##__VA_ARGS__)
 
-void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...);
+void _debug_message(_In_ const char* func, _In_ const char* file, _In_ unsigned int line, _In_ char* s, ...) __attribute__((format(printf, 4, 5)));
 
 #else
 
-#define MSG(fn, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, s, ##__VA_ARGS__) : 0
+#define MSG(fn, s, level, ...) (!log_started || level <= debug_log_level) ? _debug_message(fn, s, ##__VA_ARGS__) : (void)0
 
 #define TRACE(s, ...) MSG(funcname, s, 3, ##__VA_ARGS__)
 #define WARN(s, ...) MSG(funcname, s, 2, ##__VA_ARGS__)
 #define FIXME(s, ...) MSG(funcname, s, 1, ##__VA_ARGS__)
 #define ERR(s, ...) MSG(funcname, s, 1, ##__VA_ARGS__)
 
-void _debug_message(_In_ const char* func, _In_ char* s, ...);
+void _debug_message(_In_ const char* func, _In_ char* s, ...) __attribute__((format(printf, 2, 3)));
 
 #endif
 
@@ -1205,8 +1243,13 @@ void _free_fcb(_Inout_ fcb* fcb, _In_ const char* func);
 // in fastio.c
 void init_fast_io_dispatch(FAST_IO_DISPATCH** fiod);
 
-// in crc32c.c
-uint32_t calc_crc32c(_In_ uint32_t seed, _In_reads_bytes_(msglen) uint8_t* msg, _In_ ULONG msglen);
+// in sha256.c
+void calc_sha256(uint8_t* hash, const void* input, size_t len);
+#define SHA256_HASH_SIZE 32
+
+// in blake2b-ref.c
+void blake2b(void *out, size_t outlen, const void* in, size_t inlen);
+#define BLAKE2_HASH_SIZE 32
 
 typedef struct {
     LIST_ENTRY* list;
@@ -1315,14 +1358,11 @@ bool insert_extent_chunk(_In_ device_extension* Vcb, _In_ fcb* fcb, _In_ chunk*
                          _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback, _In_ uint8_t compression, _In_ uint64_t decoded_size, _In_ bool file_write, _In_ uint64_t irp_offset);
 
 NTSTATUS do_write_file(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, PIRP Irp, bool file_write, uint32_t irp_offset, LIST_ENTRY* rollback);
-NTSTATUS write_compressed(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, PIRP Irp, LIST_ENTRY* rollback);
 bool find_data_address_in_chunk(device_extension* Vcb, chunk* c, uint64_t length, uint64_t* address);
 void get_raid56_lock_range(chunk* c, uint64_t address, uint64_t length, uint64_t* lockaddr, uint64_t* locklen);
-NTSTATUS calc_csum(_In_ device_extension* Vcb, _In_reads_bytes_(sectors*Vcb->superblock.sector_size) uint8_t* data,
-                   _In_ uint32_t sectors, _Out_writes_bytes_(sectors*sizeof(uint32_t)) uint32_t* csum);
 void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext);
 NTSTATUS add_extent_to_fcb(_In_ fcb* fcb, _In_ uint64_t offset, _In_reads_bytes_(edsize) EXTENT_DATA* ed, _In_ uint16_t edsize,
-                           _In_ bool unique, _In_opt_ _When_(return >= 0, __drv_aliasesMem) uint32_t* csum, _In_ LIST_ENTRY* rollback);
+                           _In_ bool unique, _In_opt_ _When_(return >= 0, __drv_aliasesMem) void* csum, _In_ LIST_ENTRY* rollback);
 void add_extent(_In_ fcb* fcb, _In_ LIST_ENTRY* prevextle, _In_ __drv_aliasesMem extent* newext);
 
 // in dirctrl.c
@@ -1393,7 +1433,7 @@ NTSTATUS open_fileref(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusiv
                       _In_ bool case_sensitive, _In_opt_ PIRP Irp);
 NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb,
                   root* subvol, uint64_t inode, uint8_t type, PANSI_STRING utf8, bool always_add_hl, fcb* parent, fcb** pfcb, POOL_TYPE pooltype, PIRP Irp);
-NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, uint32_t* csum, uint64_t start, uint64_t length, PIRP Irp);
+NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, void* csum, uint64_t start, uint64_t length, PIRP Irp);
 NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, fcb* fcb, bool ignore_size, PIRP Irp);
 NTSTATUS add_dir_child(fcb* fcb, uint64_t inode, bool subvol, PANSI_STRING utf8, PUNICODE_STRING name, uint8_t type, dir_child** pdc);
 NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lock_held_(_Curr_->fcb_lock) _In_ device_extension* Vcb,
@@ -1425,13 +1465,14 @@ NTSTATUS write_data_phys(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT fileobj,
                          _In_reads_bytes_(length) void* data, _In_ uint32_t length);
 bool is_tree_unique(device_extension* Vcb, tree* t, PIRP Irp);
 NTSTATUS do_tree_writes(device_extension* Vcb, LIST_ENTRY* tree_writes, bool no_free);
-void add_checksum_entry(device_extension* Vcb, uint64_t address, ULONG length, uint32_t* csum, PIRP Irp);
+void add_checksum_entry(device_extension* Vcb, uint64_t address, ULONG length, void* csum, PIRP Irp);
 bool find_metadata_address_in_chunk(device_extension* Vcb, chunk* c, uint64_t* address);
 void add_trim_entry_avoid_sb(device_extension* Vcb, device* dev, uint64_t address, uint64_t size);
 NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, uint64_t objid, uint8_t objtype, uint64_t offset,
                                 _In_opt_ _When_(return >= 0, __drv_aliasesMem) void* data, uint16_t datalen, enum batch_operation operation);
 NTSTATUS flush_partial_stripe(device_extension* Vcb, chunk* c, partial_stripe* ps);
 NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp);
+void calc_tree_checksum(device_extension* Vcb, tree_header* th);
 
 // in read.c
 
@@ -1439,14 +1480,18 @@ _Dispatch_type_(IRP_MJ_READ)
 _Function_class_(DRIVER_DISPATCH)
 NTSTATUS __stdcall drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
-NTSTATUS read_data(_In_ device_extension* Vcb, _In_ uint64_t addr, _In_ uint32_t length, _In_reads_bytes_opt_(length*sizeof(uint32_t)/Vcb->superblock.sector_size) uint32_t* csum,
+NTSTATUS read_data(_In_ device_extension* Vcb, _In_ uint64_t addr, _In_ uint32_t length, _In_reads_bytes_opt_(length*sizeof(uint32_t)/Vcb->superblock.sector_size) void* csum,
                    _In_ bool is_tree, _Out_writes_bytes_(length) uint8_t* buf, _In_opt_ chunk* c, _Out_opt_ chunk** pc, _In_opt_ PIRP Irp, _In_ uint64_t generation, _In_ bool file_read,
                    _In_ ULONG priority);
 NTSTATUS read_file(fcb* fcb, uint8_t* data, uint64_t start, uint64_t length, ULONG* pbr, PIRP Irp);
 NTSTATUS read_stream(fcb* fcb, uint8_t* data, uint64_t start, ULONG length, ULONG* pbr);
 NTSTATUS do_read(PIRP Irp, bool wait, ULONG* bytes_read);
-NTSTATUS check_csum(device_extension* Vcb, uint8_t* data, uint32_t sectors, uint32_t* csum);
+NTSTATUS check_csum(device_extension* Vcb, uint8_t* data, uint32_t sectors, void* csum);
 void raid6_recover2(uint8_t* sectors, uint16_t num_stripes, ULONG sector_size, uint16_t missing1, uint16_t missing2, uint8_t* out);
+void get_tree_checksum(device_extension* Vcb, tree_header* th, void* csum);
+bool check_tree_checksum(device_extension* Vcb, tree_header* th);
+void get_sector_csum(device_extension* Vcb, void* buf, void* csum);
+bool check_sector_csum(device_extension* Vcb, void* buf, void* csum);
 
 // in pnp.c
 
@@ -1505,7 +1550,10 @@ void watch_registry(HANDLE regh);
 NTSTATUS zlib_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen);
 NTSTATUS lzo_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, uint32_t inpageoff);
 NTSTATUS zstd_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen);
-NTSTATUS write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, bool* compressed, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS write_compressed(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, PIRP Irp, LIST_ENTRY* rollback);
+NTSTATUS zlib_compress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, unsigned int level, unsigned int* space_left);
+NTSTATUS lzo_compress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, unsigned int* space_left);
+NTSTATUS zstd_compress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, uint32_t level, unsigned int* space_left);
 
 // in galois.c
 void galois_double(uint8_t* data, uint32_t len);
@@ -1525,8 +1573,12 @@ NTSTATUS __stdcall drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Ir
 _Function_class_(KSTART_ROUTINE)
 void __stdcall calc_thread(void* context);
 
-NTSTATUS add_calc_job(device_extension* Vcb, uint8_t* data, uint32_t sectors, uint32_t* csum, calc_job** pcj);
-void free_calc_job(calc_job* cj);
+void do_calc_job(device_extension* Vcb, uint8_t* data, uint32_t sectors, void* csum);
+NTSTATUS add_calc_job_decomp(device_extension* Vcb, uint8_t compression, void* in, unsigned int inlen,
+                             void* out, unsigned int outlen, unsigned int off, calc_job** pcj);
+NTSTATUS add_calc_job_comp(device_extension* Vcb, uint8_t compression, void* in, unsigned int inlen,
+                           void* out, unsigned int outlen, calc_job** pcj);
+void calc_thread_main(device_extension* Vcb, calc_job* cj);
 
 // in balance.c
 NTSTATUS start_balance(device_extension* Vcb, void* data, ULONG length, KPROCESSOR_MODE processor_mode);
@@ -1584,6 +1636,8 @@ NTSTATUS __stdcall compat_FsRtlValidateReparsePointBuffer(IN ULONG BufferLength,
 
 // in boot.c
 void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count);
+void boot_add_device(DEVICE_OBJECT* pdo);
+extern BTRFS_UUID boot_uuid;
 
 // based on function in sys/sysmacros.h
 #define makedev(major, minor) (((minor) & 0xFF) | (((major) & 0xFFF) << 8) | (((uint64_t)((minor) & ~0xFF)) << 12) | (((uint64_t)((major) & ~0xFFF)) << 32))
@@ -1624,7 +1678,7 @@ typedef struct {
 } FSRTL_ADVANCED_FCB_HEADER_NEW;
 
 #define FSRTL_FCB_HEADER_V2 2
-#endif
+#endif // __REACTOS__
 
 static __inline POPLOCK fcb_oplock(fcb* fcb) {
     if (fcb->Header.Version >= FSRTL_FCB_HEADER_V2)
@@ -1675,10 +1729,9 @@ static __inline bool write_fcb_compressed(fcb* fcb) {
 static __inline void do_xor(uint8_t* buf1, uint8_t* buf2, uint32_t len) {
     uint32_t j;
 #ifndef __REACTOS__
+#if defined(_X86_) || defined(_AMD64_)
     __m128i x1, x2;
-#endif
 
-#ifndef __REACTOS__
     if (have_sse2 && ((uintptr_t)buf1 & 0xf) == 0 && ((uintptr_t)buf2 & 0xf) == 0) {
         while (len >= 16) {
             x1 = _mm_load_si128((__m128i*)buf1);
@@ -1686,12 +1739,28 @@ static __inline void do_xor(uint8_t* buf1, uint8_t* buf2, uint32_t len) {
             x1 = _mm_xor_si128(x1, x2);
             _mm_store_si128((__m128i*)buf1, x1);
 
+            buf1 += 16;
+            buf2 += 16;
+            len -= 16;
+        }
+    }
+#elif defined(_ARM_) || defined(_ARM64_)
+    uint64x2_t x1, x2;
+
+    if (((uintptr_t)buf1 & 0xf) == 0 && ((uintptr_t)buf2 & 0xf) == 0) {
+        while (len >= 16) {
+            x1 = vld1q_u64((const uint64_t*)buf1);
+            x2 = vld1q_u64((const uint64_t*)buf2);
+            x1 = veorq_u64(x1, x2);
+            vst1q_u64((uint64_t*)buf1, x1);
+
             buf1 += 16;
             buf2 += 16;
             len -= 16;
         }
     }
 #endif
+#endif // __REACTOS__
 
     for (j = 0; j < len; j++) {
         *buf1 ^= *buf2;
@@ -1895,7 +1964,7 @@ typedef struct _PEB {
     PVOID Reserved7[1];
     ULONG SessionId;
 } PEB,*PPEB;
-#endif
+#endif /* __REACTOS__ */
 
 #ifdef _MSC_VER
 __kernel_entry
index 3784428..faeb9b3 100644 (file)
  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include "btrfs_drv.h"
+#include "xxhash.h"
+#include "crc32c.h"
 
-#define SECTOR_BLOCK 16
+void calc_thread_main(device_extension* Vcb, calc_job* cj) {
+    while (true) {
+        KIRQL irql;
+        calc_job* cj2;
+        uint8_t* src;
+        void* dest;
+        bool last_one = false;
+
+        KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
+
+        if (cj && cj->not_started == 0) {
+            KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
+            break;
+        }
+
+        if (cj)
+            cj2 = cj;
+        else {
+            if (IsListEmpty(&Vcb->calcthreads.job_list)) {
+                KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
+                break;
+            }
+
+            cj2 = CONTAINING_RECORD(Vcb->calcthreads.job_list.Flink, calc_job, list_entry);
+        }
+
+        src = cj2->in;
+        dest = cj2->out;
+
+        switch (cj2->type) {
+            case calc_thread_crc32c:
+            case calc_thread_xxhash:
+            case calc_thread_sha256:
+            case calc_thread_blake2:
+                cj2->in = (uint8_t*)cj2->in + Vcb->superblock.sector_size;
+                cj2->out = (uint8_t*)cj2->out + Vcb->csum_size;
+            break;
+
+            default:
+                break;
+        }
+
+        cj2->not_started--;
+
+        if (cj2->not_started == 0) {
+            RemoveEntryList(&cj2->list_entry);
+            last_one = true;
+        }
+
+        KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
+
+        switch (cj2->type) {
+            case calc_thread_crc32c:
+                *(uint32_t*)dest = ~calc_crc32c(0xffffffff, src, Vcb->superblock.sector_size);
+            break;
+
+            case calc_thread_xxhash:
+                *(uint64_t*)dest = XXH64(src, Vcb->superblock.sector_size, 0);
+            break;
+
+            case calc_thread_sha256:
+                calc_sha256(dest, src, Vcb->superblock.sector_size);
+            break;
+
+            case calc_thread_blake2:
+                blake2b(dest, BLAKE2_HASH_SIZE, src, Vcb->superblock.sector_size);
+            break;
+
+            case calc_thread_decomp_zlib:
+                cj2->Status = zlib_decompress(src, cj2->inlen, dest, cj2->outlen);
+
+                if (!NT_SUCCESS(cj2->Status))
+                    ERR("zlib_decompress returned %08lx\n", cj2->Status);
+            break;
+
+            case calc_thread_decomp_lzo:
+                cj2->Status = lzo_decompress(src, cj2->inlen, dest, cj2->outlen, cj2->off);
+
+                if (!NT_SUCCESS(cj2->Status))
+                    ERR("lzo_decompress returned %08lx\n", cj2->Status);
+            break;
+
+            case calc_thread_decomp_zstd:
+                cj2->Status = zstd_decompress(src, cj2->inlen, dest, cj2->outlen);
+
+                if (!NT_SUCCESS(cj2->Status))
+                    ERR("zstd_decompress returned %08lx\n", cj2->Status);
+            break;
+
+            case calc_thread_comp_zlib:
+                cj2->Status = zlib_compress(src, cj2->inlen, dest, cj2->outlen, Vcb->options.zlib_level, &cj2->space_left);
+
+                if (!NT_SUCCESS(cj2->Status))
+                    ERR("zlib_compress returned %08lx\n", cj2->Status);
+            break;
+
+            case calc_thread_comp_lzo:
+                cj2->Status = lzo_compress(src, cj2->inlen, dest, cj2->outlen, &cj2->space_left);
+
+                if (!NT_SUCCESS(cj2->Status))
+                    ERR("lzo_compress returned %08lx\n", cj2->Status);
+            break;
+
+            case calc_thread_comp_zstd:
+                cj2->Status = zstd_compress(src, cj2->inlen, dest, cj2->outlen, Vcb->options.zstd_level, &cj2->space_left);
+
+                if (!NT_SUCCESS(cj2->Status))
+                    ERR("zstd_compress returned %08lx\n", cj2->Status);
+            break;
+        }
+
+        if (InterlockedDecrement(&cj2->left) == 0)
+            KeSetEvent(&cj2->event, 0, false);
+
+        if (last_one)
+            break;
+    }
+}
+
+void do_calc_job(device_extension* Vcb, uint8_t* data, uint32_t sectors, void* csum) {
+    KIRQL irql;
+    calc_job cj;
+
+    cj.in = data;
+    cj.out = csum;
+    cj.left = cj.not_started = sectors;
+
+    switch (Vcb->superblock.csum_type) {
+        case CSUM_TYPE_CRC32C:
+            cj.type = calc_thread_crc32c;
+        break;
+
+        case CSUM_TYPE_XXHASH:
+            cj.type = calc_thread_xxhash;
+        break;
+
+        case CSUM_TYPE_SHA256:
+            cj.type = calc_thread_sha256;
+        break;
+
+        case CSUM_TYPE_BLAKE2:
+            cj.type = calc_thread_blake2;
+        break;
+    }
+
+    KeInitializeEvent(&cj.event, NotificationEvent, false);
+
+    KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
+
+    InsertTailList(&Vcb->calcthreads.job_list, &cj.list_entry);
+
+    KeSetEvent(&Vcb->calcthreads.event, 0, false);
+    KeClearEvent(&Vcb->calcthreads.event);
+
+    KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
+
+    calc_thread_main(Vcb, &cj);
 
-NTSTATUS add_calc_job(device_extension* Vcb, uint8_t* data, uint32_t sectors, uint32_t* csum, calc_job** pcj) {
+    KeWaitForSingleObject(&cj.event, Executive, KernelMode, false, NULL);
+}
+
+NTSTATUS add_calc_job_decomp(device_extension* Vcb, uint8_t compression, void* in, unsigned int inlen,
+                             void* out, unsigned int outlen, unsigned int off, calc_job** pcj) {
     calc_job* cj;
+    KIRQL irql;
 
     cj = ExAllocatePoolWithTag(NonPagedPool, sizeof(calc_job), ALLOC_TAG);
     if (!cj) {
@@ -28,67 +191,100 @@ NTSTATUS add_calc_job(device_extension* Vcb, uint8_t* data, uint32_t sectors, ui
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    cj->data = data;
-    cj->sectors = sectors;
-    cj->csum = csum;
-    cj->pos = 0;
-    cj->done = 0;
-    cj->refcount = 1;
+    cj->in = in;
+    cj->inlen = inlen;
+    cj->out = out;
+    cj->outlen = outlen;
+    cj->off = off;
+    cj->left = cj->not_started = 1;
+    cj->Status = STATUS_SUCCESS;
+
+    switch (compression) {
+        case BTRFS_COMPRESSION_ZLIB:
+            cj->type = calc_thread_decomp_zlib;
+        break;
+
+        case BTRFS_COMPRESSION_LZO:
+            cj->type = calc_thread_decomp_lzo;
+        break;
+
+        case BTRFS_COMPRESSION_ZSTD:
+            cj->type = calc_thread_decomp_zstd;
+        break;
+
+        default:
+            ERR("unexpected compression type %x\n", compression);
+            ExFreePool(cj);
+        return STATUS_NOT_SUPPORTED;
+    }
+
     KeInitializeEvent(&cj->event, NotificationEvent, false);
 
-    ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, true);
+    KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
 
     InsertTailList(&Vcb->calcthreads.job_list, &cj->list_entry);
 
     KeSetEvent(&Vcb->calcthreads.event, 0, false);
     KeClearEvent(&Vcb->calcthreads.event);
 
-    ExReleaseResourceLite(&Vcb->calcthreads.lock);
+    KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
 
     *pcj = cj;
 
     return STATUS_SUCCESS;
 }
 
-void free_calc_job(calc_job* cj) {
-    LONG rc = InterlockedDecrement(&cj->refcount);
-
-    if (rc == 0)
-        ExFreePool(cj);
-}
+NTSTATUS add_calc_job_comp(device_extension* Vcb, uint8_t compression, void* in, unsigned int inlen,
+                           void* out, unsigned int outlen, calc_job** pcj) {
+    calc_job* cj;
+    KIRQL irql;
 
-static bool do_calc(device_extension* Vcb, calc_job* cj) {
-    LONG pos, done;
-    uint32_t* csum;
-    uint8_t* data;
-    ULONG blocksize, i;
+    cj = ExAllocatePoolWithTag(NonPagedPool, sizeof(calc_job), ALLOC_TAG);
+    if (!cj) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-    pos = InterlockedIncrement(&cj->pos) - 1;
+    cj->in = in;
+    cj->inlen = inlen;
+    cj->out = out;
+    cj->outlen = outlen;
+    cj->left = cj->not_started = 1;
+    cj->Status = STATUS_SUCCESS;
+
+    switch (compression) {
+        case BTRFS_COMPRESSION_ZLIB:
+            cj->type = calc_thread_comp_zlib;
+        break;
+
+        case BTRFS_COMPRESSION_LZO:
+            cj->type = calc_thread_comp_lzo;
+        break;
+
+        case BTRFS_COMPRESSION_ZSTD:
+            cj->type = calc_thread_comp_zstd;
+        break;
+
+        default:
+            ERR("unexpected compression type %x\n", compression);
+            ExFreePool(cj);
+        return STATUS_NOT_SUPPORTED;
+    }
 
-    if ((uint32_t)pos * SECTOR_BLOCK >= cj->sectors)
-        return false;
+    KeInitializeEvent(&cj->event, NotificationEvent, false);
 
-    csum = &cj->csum[pos * SECTOR_BLOCK];
-    data = cj->data + (pos * SECTOR_BLOCK * Vcb->superblock.sector_size);
+    KeAcquireSpinLock(&Vcb->calcthreads.spinlock, &irql);
 
-    blocksize = min(SECTOR_BLOCK, cj->sectors - (pos * SECTOR_BLOCK));
-    for (i = 0; i < blocksize; i++) {
-        *csum = ~calc_crc32c(0xffffffff, data, Vcb->superblock.sector_size);
-        csum++;
-        data += Vcb->superblock.sector_size;
-    }
+    InsertTailList(&Vcb->calcthreads.job_list, &cj->list_entry);
 
-    done = InterlockedIncrement(&cj->done);
+    KeSetEvent(&Vcb->calcthreads.event, 0, false);
+    KeClearEvent(&Vcb->calcthreads.event);
 
-    if ((uint32_t)done * SECTOR_BLOCK >= cj->sectors) {
-        ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, true);
-        RemoveEntryList(&cj->list_entry);
-        ExReleaseResourceLite(&Vcb->calcthreads.lock);
+    KeReleaseSpinLock(&Vcb->calcthreads.spinlock, irql);
 
-        KeSetEvent(&cj->event, 0, false);
-    }
+    *pcj = cj;
 
-    return true;
+    return STATUS_SUCCESS;
 }
 
 _Function_class_(KSTART_ROUTINE)
@@ -98,32 +294,12 @@ void __stdcall calc_thread(void* context) {
 
     ObReferenceObject(thread->DeviceObject);
 
+    KeSetSystemAffinityThread((KAFFINITY)(1 << thread->number));
+
     while (true) {
         KeWaitForSingleObject(&Vcb->calcthreads.event, Executive, KernelMode, false, NULL);
 
-        while (true) {
-            calc_job* cj;
-            bool b;
-
-            ExAcquireResourceExclusiveLite(&Vcb->calcthreads.lock, true);
-
-            if (IsListEmpty(&Vcb->calcthreads.job_list)) {
-                ExReleaseResourceLite(&Vcb->calcthreads.lock);
-                break;
-            }
-
-            cj = CONTAINING_RECORD(Vcb->calcthreads.job_list.Flink, calc_job, list_entry);
-            cj->refcount++;
-
-            ExReleaseResourceLite(&Vcb->calcthreads.lock);
-
-            b = do_calc(Vcb, cj);
-
-            free_calc_job(cj);
-
-            if (!b)
-                break;
-        }
+        calc_thread_main(Vcb, NULL);
 
         if (thread->quit)
             break;
index 56ac3c5..eecc005 100644 (file)
@@ -37,7 +37,7 @@
 #include "zlib/inflate.h"
 #else
 #include <zlib.h>
-#endif
+#endif // __REACTOS__
 
 #define ZSTD_STATIC_LINKING_ONLY
 
@@ -287,7 +287,7 @@ NTSTATUS lzo_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_
         partlen = *(uint32_t*)&inbuf[inoff];
 
         if (partlen + inoff > inlen) {
-            ERR("overflow: %x + %x > %I64x\n", partlen, inoff, inlen);
+            ERR("overflow: %x + %x > %x\n", partlen, inoff, inlen);
             return STATUS_INTERNAL_ERROR;
         }
 
@@ -302,7 +302,7 @@ NTSTATUS lzo_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_
 
         Status = do_lzo_decompress(&stream);
         if (!NT_SUCCESS(Status)) {
-            ERR("do_lzo_decompress returned %08x\n", Status);
+            ERR("do_lzo_decompress returned %08lx\n", Status);
             return Status;
         }
 
@@ -333,7 +333,7 @@ static void zlib_free(void* opaque, void* ptr) {
     ExFreePool(ptr);
 }
 
-NTSTATUS zlib_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen) {
+NTSTATUS zlib_compress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, unsigned int level, unsigned int* space_left) {
     z_stream c_stream;
     int ret;
 
@@ -341,10 +341,10 @@ NTSTATUS zlib_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32
     c_stream.zfree = zlib_free;
     c_stream.opaque = (voidpf)0;
 
-    ret = inflateInit(&c_stream);
+    ret = deflateInit(&c_stream, level);
 
     if (ret != Z_OK) {
-        ERR("inflateInit returned %08x\n", ret);
+        ERR("deflateInit returned %i\n", ret);
         return STATUS_INTERNAL_ERROR;
     }
 
@@ -355,175 +355,69 @@ NTSTATUS zlib_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32
     c_stream.avail_out = outlen;
 
     do {
-        ret = inflate(&c_stream, Z_NO_FLUSH);
+        ret = deflate(&c_stream, Z_FINISH);
 
         if (ret != Z_OK && ret != Z_STREAM_END) {
-            ERR("inflate returned %08x\n", ret);
-            inflateEnd(&c_stream);
+            ERR("deflate returned %i\n", ret);
+            deflateEnd(&c_stream);
             return STATUS_INTERNAL_ERROR;
         }
 
-        if (c_stream.avail_out == 0)
+        if (c_stream.avail_in == 0 || c_stream.avail_out == 0)
             break;
     } while (ret != Z_STREAM_END);
 
-    ret = inflateEnd(&c_stream);
-
-    if (ret != Z_OK) {
-        ERR("inflateEnd returned %08x\n", ret);
-        return STATUS_INTERNAL_ERROR;
-    }
+    deflateEnd(&c_stream);
 
-    // FIXME - if we're short, should we zero the end of outbuf so we don't leak information into userspace?
+    *space_left = c_stream.avail_in > 0 ? 0 : c_stream.avail_out;
 
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS zlib_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, bool* compressed, PIRP Irp, LIST_ENTRY* rollback) {
-    NTSTATUS Status;
-    uint8_t compression;
-    uint32_t comp_length;
-    uint8_t* comp_data;
-    uint32_t out_left;
-    LIST_ENTRY* le;
-    chunk* c;
+NTSTATUS zlib_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen) {
     z_stream c_stream;
     int ret;
 
-    comp_data = ExAllocatePoolWithTag(PagedPool, (uint32_t)(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;
-    }
-
     c_stream.zalloc = zlib_alloc;
     c_stream.zfree = zlib_free;
     c_stream.opaque = (voidpf)0;
 
-    ret = deflateInit(&c_stream, fcb->Vcb->options.zlib_level);
+    ret = inflateInit(&c_stream);
 
     if (ret != Z_OK) {
-        ERR("deflateInit returned %08x\n", ret);
-        ExFreePool(comp_data);
+        ERR("inflateInit returned %i\n", ret);
         return STATUS_INTERNAL_ERROR;
     }
 
-    c_stream.avail_in = (uint32_t)(end_data - start_data);
-    c_stream.next_in = data;
-    c_stream.avail_out = (uint32_t)(end_data - start_data);
-    c_stream.next_out = comp_data;
+    c_stream.next_in = inbuf;
+    c_stream.avail_in = inlen;
+
+    c_stream.next_out = outbuf;
+    c_stream.avail_out = outlen;
 
     do {
-        ret = deflate(&c_stream, Z_FINISH);
+        ret = inflate(&c_stream, Z_NO_FLUSH);
 
-        if (ret == Z_STREAM_ERROR) {
-            ERR("deflate returned %x\n", ret);
-            ExFreePool(comp_data);
+        if (ret != Z_OK && ret != Z_STREAM_END) {
+            ERR("inflate returned %i\n", ret);
+            inflateEnd(&c_stream);
             return STATUS_INTERNAL_ERROR;
         }
-    } while (c_stream.avail_in > 0 && c_stream.avail_out > 0);
 
-    out_left = c_stream.avail_out;
+        if (c_stream.avail_out == 0)
+            break;
+    } while (ret != Z_STREAM_END);
 
-    ret = deflateEnd(&c_stream);
+    ret = inflateEnd(&c_stream);
 
     if (ret != Z_OK) {
-        ERR("deflateEnd returned %08x\n", ret);
-        ExFreePool(comp_data);
+        ERR("inflateEnd returned %i\n", ret);
         return STATUS_INTERNAL_ERROR;
     }
 
-    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_t)(end_data - start_data);
-        comp_data = data;
-        compression = BTRFS_COMPRESSION_NONE;
-
-        *compressed = false;
-    } else {
-        uint32_t cl;
-
-        compression = BTRFS_COMPRESSION_ZLIB;
-        cl = (uint32_t)(end_data - start_data - out_left);
-        comp_length = (uint32_t)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 %I64x bytes free\n", comp_length);
-
-    if (compression != BTRFS_COMPRESSION_NONE)
-        ExFreePool(comp_data);
+    // FIXME - if we're short, should we zero the end of outbuf so we don't leak information into userspace?
 
-    return STATUS_DISK_FULL;
+    return STATUS_SUCCESS;
 }
 
 static NTSTATUS lzo_do_compress(const uint8_t* in, uint32_t in_len, uint8_t* out, uint32_t* out_len, void* wrkmem) {
@@ -767,19 +661,76 @@ static __inline uint32_t lzo_max_outlen(uint32_t inlen) {
     return inlen + (inlen / 16) + 64 + 3; // formula comes from LZO.FAQ
 }
 
-static NTSTATUS lzo_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, bool* compressed, PIRP Irp, LIST_ENTRY* 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_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t 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;
+}
+
+NTSTATUS lzo_compress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, unsigned int* space_left) {
     NTSTATUS Status;
-    uint8_t compression;
-    uint64_t comp_length;
-    ULONG comp_data_len, num_pages, i;
+    unsigned int num_pages;
+    unsigned int comp_data_len;
     uint8_t* comp_data;
-    bool skip_compression = false;
     lzo_stream stream;
     uint32_t* out_size;
-    LIST_ENTRY* le;
-    chunk* c;
+#ifdef __REACTOS__
+    unsigned int i;
+#endif // __REACTOS__
 
-    num_pages = (ULONG)((sector_align(end_data - start_data, LZO_PAGE_SIZE)) / LZO_PAGE_SIZE);
+    num_pages = (unsigned int)sector_align(inlen, LZO_PAGE_SIZE) / LZO_PAGE_SIZE;
 
     // Four-byte overall header
     // Another four-byte header page
@@ -787,6 +738,8 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t
     // Plus another four bytes for possible padding
     comp_data_len = sizeof(uint32_t) + ((lzo_max_outlen(LZO_PAGE_SIZE) + (2 * sizeof(uint32_t))) * num_pages);
 
+    // FIXME - can we write this so comp_data isn't necessary?
+
     comp_data = ExAllocatePoolWithTag(PagedPool, comp_data_len, ALLOC_TAG);
     if (!comp_data) {
         ERR("out of memory\n");
@@ -800,30 +753,26 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t
         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);
-        ExFreePool(stream.wrkmem);
-        return Status;
-    }
-
     out_size = (uint32_t*)comp_data;
     *out_size = sizeof(uint32_t);
 
-    stream.in = data;
+    stream.in = inbuf;
     stream.out = comp_data + (2 * sizeof(uint32_t));
 
+#ifndef __REACTOS__
+    for (unsigned int i = 0; i < num_pages; i++) {
+#else
     for (i = 0; i < num_pages; i++) {
+#endif // __REACTOS__
         uint32_t* pagelen = (uint32_t*)(stream.out - sizeof(uint32_t));
 
-        stream.inlen = (uint32_t)min(LZO_PAGE_SIZE, end_data - start_data - (i * LZO_PAGE_SIZE));
+        stream.inlen = (uint32_t)min(LZO_PAGE_SIZE, outlen - (i * LZO_PAGE_SIZE));
 
         Status = lzo1x_1_compress(&stream);
         if (!NT_SUCCESS(Status)) {
-            ERR("lzo1x_1_compress returned %08x\n", Status);
-            skip_compression = true;
-            break;
+            ERR("lzo1x_1_compress returned %08lx\n", Status);
+            ExFreePool(comp_data);
+            return Status;
         }
 
         *pagelen = stream.outlen;
@@ -832,6 +781,7 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t
         stream.in += LZO_PAGE_SIZE;
         stream.out += stream.outlen + sizeof(uint32_t);
 
+        // new page needs to start at a 32-bit boundary
         if (LZO_PAGE_SIZE - (*out_size % LZO_PAGE_SIZE) < sizeof(uint32_t)) {
             RtlZeroMemory(stream.out, LZO_PAGE_SIZE - (*out_size % LZO_PAGE_SIZE));
             stream.out += LZO_PAGE_SIZE - (*out_size % LZO_PAGE_SIZE);
@@ -841,210 +791,301 @@ static NTSTATUS lzo_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t
 
     ExFreePool(stream.wrkmem);
 
-    if (skip_compression || *out_size >= end_data - start_data - fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
-        ExFreePool(comp_data);
+    if (*out_size >= outlen)
+        *space_left = 0;
+    else {
+        *space_left = outlen - *out_size;
+
+        RtlCopyMemory(outbuf, comp_data, *out_size);
+    }
 
-        comp_length = end_data - start_data;
-        comp_data = data;
-        compression = BTRFS_COMPRESSION_NONE;
+    ExFreePool(comp_data);
 
-        *compressed = false;
-    } else {
-        compression = BTRFS_COMPRESSION_LZO;
-        comp_length = sector_align(*out_size, fcb->Vcb->superblock.sector_size);
+    return STATUS_SUCCESS;
+}
 
-        RtlZeroMemory(comp_data + *out_size, (ULONG)(comp_length - *out_size));
+NTSTATUS zstd_compress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, uint32_t level, unsigned int* space_left) {
+    ZSTD_CStream* stream;
+    size_t init_res, written;
+    ZSTD_inBuffer input;
+    ZSTD_outBuffer output;
+    ZSTD_parameters params;
+
+    stream = ZSTD_createCStream_advanced(zstd_mem);
 
-        *compressed = true;
+    if (!stream) {
+        ERR("ZSTD_createCStream failed.\n");
+        return STATUS_INTERNAL_ERROR;
     }
 
-    ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, true);
+    params = ZSTD_getParams(level, inlen, 0);
 
-    le = fcb->Vcb->chunks.Flink;
-    while (le != &fcb->Vcb->chunks) {
-        c = CONTAINING_RECORD(le, chunk, list_entry);
+    if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG)
+        params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG;
 
-        if (!c->readonly && !c->reloc) {
-            acquire_chunk_lock(c, fcb->Vcb);
+    init_res = ZSTD_initCStream_advanced(stream, NULL, 0, params, inlen);
 
-            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 (ZSTD_isError(init_res)) {
+        ERR("ZSTD_initCStream_advanced failed: %s\n", ZSTD_getErrorName(init_res));
+        ZSTD_freeCStream(stream);
+        return STATUS_INTERNAL_ERROR;
+    }
 
-                    if (compression != BTRFS_COMPRESSION_NONE)
-                        ExFreePool(comp_data);
+    input.src = inbuf;
+    input.size = inlen;
+    input.pos = 0;
 
-                    return STATUS_SUCCESS;
-                }
-            }
+    output.dst = outbuf;
+    output.size = outlen;
+    output.pos = 0;
 
-            release_chunk_lock(c, fcb->Vcb);
-        }
+    while (input.pos < input.size && output.pos < output.size) {
+        written = ZSTD_compressStream(stream, &output, &input);
 
-        le = le->Flink;
+        if (ZSTD_isError(written)) {
+            ERR("ZSTD_compressStream failed: %s\n", ZSTD_getErrorName(written));
+            ZSTD_freeCStream(stream);
+            return STATUS_INTERNAL_ERROR;
+        }
     }
 
-    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    written = ZSTD_endStream(stream, &output);
+    if (ZSTD_isError(written)) {
+        ERR("ZSTD_endStream failed: %s\n", ZSTD_getErrorName(written));
+        ZSTD_freeCStream(stream);
+        return STATUS_INTERNAL_ERROR;
+    }
 
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, true);
+    ZSTD_freeCStream(stream);
 
-    Status = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags, &c, false);
+    if (input.pos < input.size) // output would be larger than input
+        *space_left = 0;
+    else
+        *space_left = output.size - output.pos;
 
-    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+    return STATUS_SUCCESS;
+}
 
-    if (!NT_SUCCESS(Status)) {
-        ERR("alloc_chunk returned %08x\n", Status);
+typedef struct {
+    uint8_t buf[COMPRESSED_EXTENT_SIZE];
+    uint8_t compression_type;
+    unsigned int inlen;
+    unsigned int outlen;
+    calc_job* cj;
+} comp_part;
+
+NTSTATUS write_compressed(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, PIRP Irp, LIST_ENTRY* rollback) {
+    NTSTATUS Status;
+    uint64_t i;
+    unsigned int num_parts = (unsigned int)sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE;
+    uint8_t type;
+    comp_part* parts;
+    unsigned int buflen = 0;
+    uint8_t* buf;
+    chunk* c = NULL;
+    LIST_ENTRY* le;
+    uint64_t address, extaddr;
+    void* csum = NULL;
+#ifdef __REACTOS__
+    int32_t i2;
+    uint32_t i3;
+#endif // __REACTOS__
 
-        if (compression != BTRFS_COMPRESSION_NONE)
-            ExFreePool(comp_data);
+    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_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)
+            type = BTRFS_COMPRESSION_LZO;
+        else
+            type = BTRFS_COMPRESSION_ZLIB;
+    }
 
+    Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
+    if (!NT_SUCCESS(Status)) {
+        ERR("excise_extents returned %08lx\n", Status);
         return Status;
     }
 
-    if (c) {
-        acquire_chunk_lock(c, fcb->Vcb);
+    parts = ExAllocatePoolWithTag(PagedPool, sizeof(comp_part) * num_parts, ALLOC_TAG);
+    if (!parts) {
+        ERR("out of memory\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    for (i = 0; i < num_parts; i++) {
+        if (i == num_parts - 1)
+            parts[i].inlen = ((unsigned int)(end_data - start_data) - ((num_parts - 1) * COMPRESSED_EXTENT_SIZE));
+        else
+            parts[i].inlen = COMPRESSED_EXTENT_SIZE;
 
-        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);
+        Status = add_calc_job_comp(fcb->Vcb, type, (uint8_t*)data + (i * COMPRESSED_EXTENT_SIZE), parts[i].inlen,
+                                   parts[i].buf, parts[i].inlen, &parts[i].cj);
+        if (!NT_SUCCESS(Status)) {
+            ERR("add_calc_job_comp returned %08lx\n", Status);
 
-                return STATUS_SUCCESS;
+            for (unsigned int j = 0; j < i; j++) {
+                KeWaitForSingleObject(&parts[j].cj->event, Executive, KernelMode, false, NULL);
+                ExFreePool(parts[j].cj);
             }
-        }
 
-        release_chunk_lock(c, fcb->Vcb);
+            ExFreePool(parts);
+            return Status;
+        }
     }
 
-    WARN("couldn't find any data chunks with %I64x bytes free\n", comp_length);
+    Status = STATUS_SUCCESS;
 
-    if (compression != BTRFS_COMPRESSION_NONE)
-        ExFreePool(comp_data);
+#ifndef __REACTOS__
+    for (int i = num_parts - 1; i >= 0; i--) {
+        calc_thread_main(fcb->Vcb, parts[i].cj);
 
-    return STATUS_DISK_FULL;
-}
+        KeWaitForSingleObject(&parts[i].cj->event, Executive, KernelMode, false, NULL);
 
-static NTSTATUS zstd_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, bool* compressed, PIRP Irp, LIST_ENTRY* rollback) {
-    NTSTATUS Status;
-    uint8_t compression;
-    uint32_t comp_length;
-    uint8_t* comp_data;
-    uint32_t out_left;
-    LIST_ENTRY* le;
-    chunk* c;
-    ZSTD_CStream* stream;
-    size_t init_res, written;
-    ZSTD_inBuffer input;
-    ZSTD_outBuffer output;
-    ZSTD_parameters params;
+        if (!NT_SUCCESS(parts[i].cj->Status))
+            Status = parts[i].cj->Status;
+    }
+#else
+    for (i2 = num_parts - 1; i2 >= 0; i2--) {
+        calc_thread_main(fcb->Vcb, parts[i].cj);
 
-    comp_data = ExAllocatePoolWithTag(PagedPool, (uint32_t)(end_data - start_data), ALLOC_TAG);
-    if (!comp_data) {
-        ERR("out of memory\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
+        KeWaitForSingleObject(&parts[i2].cj->event, Executive, KernelMode, false, NULL);
+
+        if (!NT_SUCCESS(parts[i2].cj->Status))
+            Status = parts[i2].cj->Status;
     }
+#endif // __REACTOS__
 
-    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);
+        ERR("calc job returned %08lx\n", Status);
+
+#ifndef __REACTOS__
+        for (unsigned int i = 0; i < num_parts; i++) {
+            ExFreePool(parts[i].cj);
+        }
+#else
+        for (i3 = 0; i3 < num_parts; i3++) {
+            ExFreePool(parts[i3].cj);
+        }
+#endif // __REACTOS__
+
+        ExFreePool(parts);
         return Status;
     }
 
-    stream = ZSTD_createCStream_advanced(zstd_mem);
+#ifndef __REACTOS__
+    for (unsigned int i = 0; i < num_parts; i++) {
+        if (parts[i].cj->space_left >= fcb->Vcb->superblock.sector_size) {
+            parts[i].compression_type = type;
+            parts[i].outlen = parts[i].inlen - parts[i].cj->space_left;
 
-    if (!stream) {
-        ERR("ZSTD_createCStream failed.\n");
-        ExFreePool(comp_data);
-        return STATUS_INTERNAL_ERROR;
-    }
+            if (type == BTRFS_COMPRESSION_LZO)
+                fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
+            else if (type == BTRFS_COMPRESSION_ZSTD)
+                fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD;
 
-    params = ZSTD_getParams(fcb->Vcb->options.zstd_level, (uint32_t)(end_data - start_data), 0);
+            if ((parts[i].outlen % fcb->Vcb->superblock.sector_size) != 0) {
+                unsigned int newlen = (unsigned int)sector_align(parts[i].outlen, fcb->Vcb->superblock.sector_size);
 
-    if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG)
-        params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG;
+                RtlZeroMemory(parts[i].buf + parts[i].outlen, newlen - parts[i].outlen);
 
-    init_res = ZSTD_initCStream_advanced(stream, NULL, 0, params, (uint32_t)(end_data - start_data));
+                parts[i].outlen = newlen;
+            }
+        } else {
+            parts[i].compression_type = BTRFS_COMPRESSION_NONE;
+            parts[i].outlen = (unsigned int)sector_align(parts[i].inlen, fcb->Vcb->superblock.sector_size);
+        }
 
-    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;
+        buflen += parts[i].outlen;
+        ExFreePool(parts[i].cj);
     }
+#else
+    for (i3 = 0; i3 < num_parts; i3++) {
+        if (parts[i3].cj->space_left >= fcb->Vcb->superblock.sector_size) {
+            parts[i3].compression_type = type;
+            parts[i3].outlen = parts[i3].inlen - parts[i3].cj->space_left;
 
-    input.src = data;
-    input.size = (uint32_t)(end_data - start_data);
-    input.pos = 0;
+            if (type == BTRFS_COMPRESSION_LZO)
+                fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO;
+            else if (type == BTRFS_COMPRESSION_ZSTD)
+                fcb->Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD;
 
-    output.dst = comp_data;
-    output.size = (uint32_t)(end_data - start_data);
-    output.pos = 0;
+            if ((parts[i3].outlen % fcb->Vcb->superblock.sector_size) != 0) {
+                unsigned int newlen = (unsigned int)sector_align(parts[i3].outlen, fcb->Vcb->superblock.sector_size);
 
-    while (input.pos < input.size && output.pos < output.size) {
-        written = ZSTD_compressStream(stream, &output, &input);
+                RtlZeroMemory(parts[i3].buf + parts[i3].outlen, newlen - parts[i3].outlen);
 
-        if (ZSTD_isError(written)) {
-            ERR("ZSTD_compressStream failed: %s\n", ZSTD_getErrorName(written));
-            ZSTD_freeCStream(stream);
-            ExFreePool(comp_data);
-            return STATUS_INTERNAL_ERROR;
+                parts[i3].outlen = newlen;
+            }
+        } else {
+            parts[i3].compression_type = BTRFS_COMPRESSION_NONE;
+            parts[i3].outlen = (unsigned int)sector_align(parts[i3].inlen, fcb->Vcb->superblock.sector_size);
         }
-    }
 
-    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;
+        buflen += parts[i3].outlen;
+        ExFreePool(parts[i3].cj);
     }
+#endif // __REACTOS__
 
-    ZSTD_freeCStream(stream);
+    // check if first 128 KB of file is incompressible
 
-    out_left = output.size - output.pos;
+    if (start_data == 0 && parts[0].compression_type == BTRFS_COMPRESSION_NONE && !fcb->Vcb->options.compress_force) {
+        TRACE("adding nocompress flag to subvol %I64x, inode %I64x\n", fcb->subvol->id, fcb->inode);
 
-    if (out_left < fcb->Vcb->superblock.sector_size) { // compressed extent would be larger than or same size as uncompressed extent
-        ExFreePool(comp_data);
+        fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS;
+        fcb->inode_item_changed = true;
+        mark_fcb_dirty(fcb);
+    }
 
-        comp_length = (uint32_t)(end_data - start_data);
-        comp_data = data;
-        compression = BTRFS_COMPRESSION_NONE;
+    // join together into continuous buffer
 
-        *compressed = false;
-    } else {
-        uint32_t cl;
+    buf = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG);
+    if (!buf) {
+        ERR("out of memory\n");
+        ExFreePool(parts);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-        compression = BTRFS_COMPRESSION_ZSTD;
-        cl = (uint32_t)(end_data - start_data - out_left);
-        comp_length = (uint32_t)sector_align(cl, fcb->Vcb->superblock.sector_size);
+    {
+        uint8_t* buf2 = buf;
 
-        RtlZeroMemory(comp_data + cl, comp_length - cl);
+        for (i = 0; i < num_parts; i++) {
+            if (parts[i].compression_type == BTRFS_COMPRESSION_NONE)
+                RtlCopyMemory(buf2, (uint8_t*)data + (i * COMPRESSED_EXTENT_SIZE), parts[i].outlen);
+            else
+                RtlCopyMemory(buf2, parts[i].buf, parts[i].outlen);
 
-        *compressed = true;
+            buf2 += parts[i].outlen;
+        }
     }
 
+    // find an address
+
     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);
+        chunk* c2 = CONTAINING_RECORD(le, chunk, list_entry);
 
-            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 (!c2->readonly && !c2->reloc) {
+            acquire_chunk_lock(c2, fcb->Vcb);
 
-                    if (compression != BTRFS_COMPRESSION_NONE)
-                        ExFreePool(comp_data);
-
-                    return STATUS_SUCCESS;
+            if (c2->chunk_item->type == fcb->Vcb->data_flags && (c2->chunk_item->size - c2->used) >= buflen) {
+                if (find_data_address_in_chunk(fcb->Vcb, c2, buflen, &address)) {
+                    c = c2;
+                    c->used += buflen;
+                    space_list_subtract(c, false, address, buflen, rollback);
+                    release_chunk_lock(c2, fcb->Vcb);
+                    break;
                 }
             }
 
-            release_chunk_lock(c, fcb->Vcb);
+            release_chunk_lock(c2, fcb->Vcb);
         }
 
         le = le->Flink;
@@ -1052,126 +1093,164 @@ static NTSTATUS zstd_write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_
 
     ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
 
-    ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, true);
+    if (!c) {
+        chunk* c2;
 
-    Status = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags, &c, false);
+        ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, true);
 
-    ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
+        Status = alloc_chunk(fcb->Vcb, fcb->Vcb->data_flags, &c2, false);
 
-    if (!NT_SUCCESS(Status)) {
-        ERR("alloc_chunk returned %08x\n", Status);
+        ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
 
-        if (compression != BTRFS_COMPRESSION_NONE)
-            ExFreePool(comp_data);
+        if (!NT_SUCCESS(Status)) {
+            ERR("alloc_chunk returned %08lx\n", Status);
+            ExFreePool(buf);
+            ExFreePool(parts);
+            return Status;
+        }
+
+        acquire_chunk_lock(c2, fcb->Vcb);
+
+        if (find_data_address_in_chunk(fcb->Vcb, c2, buflen, &address)) {
+            c = c2;
+            c->used += buflen;
+            space_list_subtract(c, false, address, buflen, rollback);
+        }
+
+        release_chunk_lock(c2, fcb->Vcb);
+    }
 
+    if (!c) {
+        WARN("couldn't find any data chunks with %x bytes free\n", buflen);
+        ExFreePool(buf);
+        ExFreePool(parts);
+        return STATUS_DISK_FULL;
+    }
+
+    // write to disk
+
+    TRACE("writing %x bytes to %I64x\n", buflen, address);
+
+    Status = write_data_complete(fcb->Vcb, address, buf, buflen, Irp, NULL, false, 0,
+                                 fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority);
+    if (!NT_SUCCESS(Status)) {
+        ERR("write_data_complete returned %08lx\n", Status);
+        ExFreePool(buf);
+        ExFreePool(parts);
         return Status;
     }
 
-    if (c) {
-        acquire_chunk_lock(c, fcb->Vcb);
+    // FIXME - do rest of the function while we're waiting for I/O to finish?
 
-        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);
+    // calculate csums if necessary
 
-                return STATUS_SUCCESS;
-            }
+    if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
+        unsigned int sl = buflen / fcb->Vcb->superblock.sector_size;
+
+        csum = ExAllocatePoolWithTag(PagedPool, sl * fcb->Vcb->csum_size, ALLOC_TAG);
+        if (!csum) {
+            ERR("out of memory\n");
+            ExFreePool(buf);
+            ExFreePool(parts);
+            return STATUS_INSUFFICIENT_RESOURCES;
         }
 
-        release_chunk_lock(c, fcb->Vcb);
+        do_calc_job(fcb->Vcb, buf, sl, csum);
     }
 
-    WARN("couldn't find any data chunks with %I64x bytes free\n", comp_length);
+    ExFreePool(buf);
 
-    if (compression != BTRFS_COMPRESSION_NONE)
-        ExFreePool(comp_data);
+    // add extents to fcb
 
-    return STATUS_DISK_FULL;
-}
+    extaddr = address;
 
-NTSTATUS write_compressed_bit(fcb* fcb, uint64_t start_data, uint64_t end_data, void* data, bool* compressed, PIRP Irp, LIST_ENTRY* rollback) {
-    uint8_t type;
+    for (i = 0; i < num_parts; i++) {
+        EXTENT_DATA* ed;
+        EXTENT_DATA2* ed2;
+        void* csum2;
 
-    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_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)
-            type = BTRFS_COMPRESSION_LZO;
-        else
-            type = BTRFS_COMPRESSION_ZLIB;
-    }
+        ed = ExAllocatePoolWithTag(PagedPool, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2), ALLOC_TAG);
+        if (!ed) {
+            ERR("out of memory\n");
+            ExFreePool(parts);
 
-    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);
-}
+            if (csum)
+                ExFreePool(csum);
 
-static void* zstd_malloc(void* opaque, size_t size) {
-    UNUSED(opaque);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
 
-    return ExAllocatePoolWithTag(PagedPool, size, ZSTD_ALLOC_TAG);
-}
+        ed->generation = fcb->Vcb->superblock.generation;
+        ed->decoded_size = parts[i].inlen;
+        ed->compression = parts[i].compression_type;
+        ed->encryption = BTRFS_ENCRYPTION_NONE;
+        ed->encoding = BTRFS_ENCODING_NONE;
+        ed->type = EXTENT_TYPE_REGULAR;
+
+        ed2 = (EXTENT_DATA2*)ed->data;
+        ed2->address = extaddr;
+        ed2->size = parts[i].outlen;
+        ed2->offset = 0;
+        ed2->num_bytes = parts[i].inlen;
+
+        if (csum) {
+            csum2 = ExAllocatePoolWithTag(PagedPool, parts[i].outlen * fcb->Vcb->csum_size / fcb->Vcb->superblock.sector_size, ALLOC_TAG);
+            if (!csum2) {
+                ERR("out of memory\n");
+                ExFreePool(ed);
+                ExFreePool(parts);
+                ExFreePool(csum);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
 
-static void zstd_free(void* opaque, void* address) {
-    UNUSED(opaque);
+            RtlCopyMemory(csum2, (uint8_t*)csum + ((extaddr - address) * fcb->Vcb->csum_size / fcb->Vcb->superblock.sector_size),
+                          parts[i].outlen * fcb->Vcb->csum_size / fcb->Vcb->superblock.sector_size);
+        } else
+            csum2 = NULL;
 
-    ExFreePool(address);
-}
+        Status = add_extent_to_fcb(fcb, start_data + (i * COMPRESSED_EXTENT_SIZE), ed, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2),
+                                   true, csum2, rollback);
+        if (!NT_SUCCESS(Status)) {
+            ERR("add_extent_to_fcb returned %08lx\n", Status);
+            ExFreePool(ed);
+            ExFreePool(parts);
 
-NTSTATUS zstd_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen) {
-    NTSTATUS Status;
-    ZSTD_DStream* stream;
-    size_t init_res, read;
-    ZSTD_inBuffer input;
-    ZSTD_outBuffer output;
+            if (csum)
+                ExFreePool(csum);
 
-    stream = ZSTD_createDStream_advanced(zstd_mem);
+            return Status;
+        }
 
-    if (!stream) {
-        ERR("ZSTD_createDStream failed.\n");
-        return STATUS_INTERNAL_ERROR;
-    }
+        ExFreePool(ed);
 
-    init_res = ZSTD_initDStream(stream);
+        fcb->inode_item.st_blocks += parts[i].inlen;
 
-    if (ZSTD_isError(init_res)) {
-        ERR("ZSTD_initDStream failed: %s\n", ZSTD_getErrorName(init_res));
-        Status = STATUS_INTERNAL_ERROR;
-        goto end;
+        extaddr += parts[i].outlen;
     }
 
-    input.src = inbuf;
-    input.size = inlen;
-    input.pos = 0;
+    if (csum)
+        ExFreePool(csum);
 
-    output.dst = outbuf;
-    output.size = outlen;
-    output.pos = 0;
+    // update extent refcounts
 
-    read = ZSTD_decompressStream(stream, &output, &input);
+    ExAcquireResourceExclusiveLite(&c->changed_extents_lock, true);
 
-    if (ZSTD_isError(read)) {
-        ERR("ZSTD_decompressStream failed: %s\n", ZSTD_getErrorName(read));
-        Status = STATUS_INTERNAL_ERROR;
-        goto end;
+    extaddr = address;
+
+    for (i = 0; i < num_parts; i++) {
+        add_changed_extent_ref(c, extaddr, parts[i].outlen, fcb->subvol->id, fcb->inode,
+                               start_data + (i * COMPRESSED_EXTENT_SIZE), 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM);
+
+        extaddr += parts[i].outlen;
     }
 
-    Status = STATUS_SUCCESS;
+    ExReleaseResourceLite(&c->changed_extents_lock);
 
-end:
-    ZSTD_freeDStream(stream);
+    fcb->extents_changed = true;
+    fcb->inode_item_changed = true;
+    mark_fcb_dirty(fcb);
 
-    return Status;
+    ExFreePool(parts);
+
+    return STATUS_SUCCESS;
 }
diff --git a/drivers/filesystems/btrfs/crc32c-amd64.S b/drivers/filesystems/btrfs/crc32c-amd64.S
new file mode 100644 (file)
index 0000000..3a6c910
--- /dev/null
@@ -0,0 +1,112 @@
+/* Copyright (c) Mark Harmstone 2020
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <asm.inc>
+
+EXTERN _crctable:QWORD
+
+.code64
+
+
+/* uint32_t __stdcall calc_crc32c_sw(uint32_t seed, uint8_t* msg, uint32_t msglen); */
+
+PUBLIC _calc_crc32c_sw@12
+_calc_crc32c_sw@12:
+
+/* rax = crc / seed
+ * rdx = buf
+ * r8 = len
+ * rcx = tmp
+ * r10 = tmp2 */
+
+mov rax, rcx
+
+crcloop:
+test r8, r8
+jz crcend
+
+mov rcx, rax
+shr rcx, 8
+mov r10b, byte ptr [rdx]
+xor al, r10b
+and rax, 255
+shl rax, 2
+mov rax, dword ptr [_crctable + rax]
+xor rax, rcx
+
+inc rdx
+dec r8
+
+jmp crcloop
+
+crcend:
+ret
+
+/****************************************************/
+
+/* uint32_t __stdcall calc_crc32c_hw(uint32_t seed, uint8_t* msg, uint32_t msglen); */
+
+PUBLIC _calc_crc32c_hw@12:
+_calc_crc32c_hw@12:
+
+/* rax = crc / seed
+ * rdx = buf
+ * r8 = len */
+
+mov rax, rcx
+
+crchw_loop:
+cmp r8, 8
+jl crchw_stragglers
+
+crc32 rax, qword ptr [rdx]
+
+add rdx, 8
+sub r8, 8
+jmp crchw_loop
+
+crchw_stragglers:
+cmp r8, 4
+jl crchw_stragglers2
+
+crc32 eax, dword ptr [rdx]
+
+add rdx, 4
+sub r8, 4
+
+crchw_stragglers2:
+cmp r8, 2
+jl crchw_stragglers3
+
+crc32 eax, word ptr [rdx]
+
+add rdx, 2
+sub r8, 2
+
+crchw_stragglers3:
+test r8, r8
+jz crchw_end
+
+crc32 eax, byte ptr [rdx]
+inc rdx
+dec r8
+jmp crchw_stragglers3
+
+crchw_end:
+ret
+
+END
diff --git a/drivers/filesystems/btrfs/crc32c-x86.S b/drivers/filesystems/btrfs/crc32c-x86.S
new file mode 100644 (file)
index 0000000..991eec8
--- /dev/null
@@ -0,0 +1,122 @@
+/* Copyright (c) Mark Harmstone 2020
+ *
+ * This file is part of WinBtrfs.
+ *
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ *
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <asm.inc>
+
+EXTERN _crctable:DWORD
+
+.code
+
+/* uint32_t __stdcall calc_crc32c_sw(uint32_t seed, uint8_t* msg, uint32_t msglen); */
+
+PUBLIC _calc_crc32c_sw@12
+_calc_crc32c_sw@12:
+
+push ebp
+mov ebp, esp
+
+push esi
+push ebx
+
+mov eax, [ebp+8]
+mov edx, [ebp+12]
+mov ebx, [ebp+16]
+
+/* eax = crc / seed
+ * ebx = len
+ * esi = tmp
+ * edx = buf
+ * ecx = tmp2 */
+
+crcloop:
+test ebx, ebx
+jz crcend
+
+mov esi, eax
+shr esi, 8
+mov cl, byte ptr [edx]
+xor al, cl
+and eax, 255
+shl eax, 2
+mov eax, [_crctable + eax]
+xor eax, esi
+
+inc edx
+dec ebx
+
+jmp crcloop
+
+crcend:
+pop ebx
+pop esi
+
+pop ebp
+
+ret 12
+
+/****************************************************/
+
+/* uint32_t __stdcall calc_crc32c_hw(uint32_t seed, uint8_t* msg, uint32_t msglen); */
+
+PUBLIC _calc_crc32c_hw@12
+_calc_crc32c_hw@12:
+
+push ebp
+mov ebp, esp
+
+mov eax, [ebp+8]
+mov edx, [ebp+12]
+mov ecx, [ebp+16]
+
+/* eax = crc / seed
+ * ecx = len
+ * edx = buf */
+
+crchw_loop:
+cmp ecx, 4
+jl crchw_stragglers
+
+crc32 eax, dword ptr [edx]
+
+add edx, 4
+sub ecx, 4
+jmp crchw_loop
+
+crchw_stragglers:
+cmp ecx, 2
+jl crchw_stragglers2
+
+crc32 eax, word ptr [edx]
+
+add edx, 2
+sub ecx, 2
+
+crchw_stragglers2:
+test ecx, ecx
+jz crchw_end
+
+crc32 eax, byte ptr [edx]
+inc edx
+dec ecx
+jmp crchw_stragglers2
+
+crchw_end:
+pop ebp
+
+ret 12
+
+END
index 8d09cad..ef4572b 100644 (file)
  * You should have received a copy of the GNU Lesser General Public Licence
  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
 
-#include <windef.h>
-#ifndef __REACTOS__
-#include <smmintrin.h>
-#endif /* __REACTOS__ */
+#include "crc32c.h"
 #include <stdint.h>
 #include <stdbool.h>
+#include <sal.h>
 
-#ifndef __REACTOS__
-extern bool have_sse42;
-#endif /* __REACTOS__ */
+crc_func calc_crc32c = calc_crc32c_sw;
 
-static const uint32_t crctable[] = {
+const uint32_t crctable[] = {
     0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
     0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
     0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
@@ -61,75 +57,15 @@ static const uint32_t crctable[] = {
     0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
 };
 
-#ifndef __REACTOS__
-// HW code taken from https://github.com/rurban/smhasher/blob/master/crc32_hw.c
-#define ALIGN_SIZE      0x08UL
-#define ALIGN_MASK      (ALIGN_SIZE - 1)
-#define CALC_CRC(op, crc, type, buf, len)                               \
-  do {                                                                  \
-    for (; (len) >= sizeof (type); (len) -= sizeof(type), buf += sizeof (type)) { \
-      (crc) = op((crc), *(type *) (buf));                               \
-    }                                                                   \
-  } while(0)
+// x86 and amd64 versions live in asm files
+#if !defined(_X86_) && !defined(_AMD64_)
+uint32_t __stdcall calc_crc32c_sw(_In_ uint32_t seed, _In_reads_bytes_(msglen) uint8_t* msg, _In_ uint32_t msglen) {
+    uint32_t rem = seed;
 
-static uint32_t crc32c_hw(const void *input, ULONG len, uint32_t crc) {
-    const char* buf = (const char*)input;
-
-    // Annoyingly, the CRC32 intrinsics don't work properly in modern versions of MSVC -
-    // it compiles _mm_crc32_u8 as if it was _mm_crc32_u32. And because we're apparently
-    // not allowed to use inline asm on amd64, there's no easy way to fix this!
-
-    for (; (len > 0) && ((size_t)buf & ALIGN_MASK); len--, buf++) {
-#ifdef _MSC_VER
-        crc = crctable[(crc ^ *buf) & 0xff] ^ (crc >> 8);
-#else
-        crc = _mm_crc32_u8(crc, *buf);
-#endif
-    }
-
-#ifdef _AMD64_
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4244) // _mm_crc32_u64 wants to return uint64_t(!)
-#pragma warning(disable:4242)
-#endif
-    CALC_CRC(_mm_crc32_u64, crc, uint64_t, buf, len);
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-#endif
-    CALC_CRC(_mm_crc32_u32, crc, uint32_t, buf, len);
-
-#ifdef _MSC_VER
-    for (; len > 0; len--, buf++) {
-        crc = crctable[(crc ^ *buf) & 0xff] ^ (crc >> 8);
-    }
-#else
-    CALC_CRC(_mm_crc32_u16, crc, uint16_t, buf, len);
-    CALC_CRC(_mm_crc32_u8, crc, uint8_t, buf, len);
-#endif
-
-    return crc;
-}
-#endif
-
-uint32_t calc_crc32c(_In_ uint32_t seed, _In_reads_bytes_(msglen) uint8_t* msg, _In_ ULONG msglen) {
-    uint32_t rem;
-    ULONG i;
-
-#ifndef __REACTOS__
-    if (have_sse42) {
-        return crc32c_hw(msg, msglen, seed);
-    } else {
-#endif
-        rem = seed;
-
-        for (i = 0; i < msglen; i++) {
-            rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8);
-        }
-#ifndef __REACTOS__
+    for (uint32_t i = 0; i < msglen; i++) {
+        rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8);
     }
-#endif
 
     return rem;
 }
+#endif
diff --git a/drivers/filesystems/btrfs/crc32c.h b/drivers/filesystems/btrfs/crc32c.h
new file mode 100644 (file)
index 0000000..73bd969
--- /dev/null
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <stdint.h>
+
+#if defined(_X86_) || defined(_AMD64_)
+uint32_t __stdcall calc_crc32c_hw(uint32_t seed, uint8_t* msg, uint32_t msglen);
+#endif
+
+uint32_t __stdcall calc_crc32c_sw(uint32_t seed, uint8_t* msg, uint32_t msglen);
+
+typedef uint32_t (__stdcall *crc_func)(uint32_t seed, uint8_t* msg, uint32_t msglen);
+
+extern crc_func calc_crc32c;
index 4eebef9..0821590 100644 (file)
@@ -19,6 +19,7 @@
 #include <sys/stat.h>
 #endif /* __REACTOS__ */
 #include "btrfs_drv.h"
+#include "crc32c.h"
 #include <ntddstor.h>
 
 extern PDEVICE_OBJECT master_devobj;
@@ -33,8 +34,21 @@ static const WCHAR root_dir_utf16[] = L"$Root";
 
 // Windows 10
 #define ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED   0x0002
+#define ATOMIC_CREATE_ECP_IN_FLAG_OP_FLAGS_SPECIFIED        0x0080
 #define ATOMIC_CREATE_ECP_IN_FLAG_BEST_EFFORT               0x0100
+
 #define ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET        0x0002
+#define ATOMIC_CREATE_ECP_OUT_FLAG_OP_FLAGS_HONORED         0x0080
+
+#define ATOMIC_CREATE_ECP_IN_OP_FLAG_CASE_SENSITIVE_FLAGS_SPECIFIED       1
+#define ATOMIC_CREATE_ECP_OUT_OP_FLAG_CASE_SENSITIVE_FLAGS_SET            1
+
+typedef struct _FILE_TIMESTAMPS {
+    LARGE_INTEGER CreationTime;
+    LARGE_INTEGER LastAccessTime;
+    LARGE_INTEGER LastWriteTime;
+    LARGE_INTEGER ChangeTime;
+} FILE_TIMESTAMPS, *PFILE_TIMESTAMPS;
 
 typedef struct _ATOMIC_CREATE_ECP_CONTEXT {
     USHORT Size;
@@ -44,9 +58,23 @@ typedef struct _ATOMIC_CREATE_ECP_CONTEXT {
     PREPARSE_DATA_BUFFER ReparseBuffer;
     LONGLONG FileSize;
     LONGLONG ValidDataLength;
+    PFILE_TIMESTAMPS FileTimestamps;
+    ULONG FileAttributes;
+    ULONG UsnSourceInfo;
+    USN Usn;
+    ULONG SuppressFileAttributeInheritanceMask;
+    ULONG InOpFlags;
+    ULONG OutOpFlags;
+    ULONG InGenFlags;
+    ULONG OutGenFlags;
+    ULONG CaseSensitiveFlagsMask;
+    ULONG InCaseSensitiveFlags;
+    ULONG OutCaseSensitiveFlags;
 } 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 } };
+static const GUID GUID_ECP_QUERY_ON_CREATE = { 0x1aca62e9, 0xabb4, 0x4ff2, { 0xbb, 0x5c, 0x1c, 0x79, 0x02, 0x5e, 0x41, 0x7f } };
+static const GUID GUID_ECP_CREATE_REDIRECTION = { 0x188d6bd6, 0xa126, 0x4fa8, { 0xbd, 0xf2, 0x1c, 0xcd, 0xf8, 0x96, 0xf3, 0xe0 } };
 
 fcb* create_fcb(device_extension* Vcb, POOL_TYPE pool_type) {
     fcb* fcb;
@@ -160,7 +188,7 @@ NTSTATUS find_file_in_dir(PUNICODE_STRING filename, fcb* fcb, root** subvol, uin
         Status = RtlUpcaseUnicodeString(&fnus, filename, true);
 
         if (!NT_SUCCESS(Status)) {
-            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+            ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
             return Status;
         }
     } else
@@ -415,12 +443,13 @@ cleanup:
     return Status;
 }
 
-NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, uint32_t* csum, uint64_t start, uint64_t length, PIRP Irp) {
+NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb, void* csum, uint64_t start, uint64_t length, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp, next_tp;
     uint64_t i, j;
     bool b;
+    void* ptr = csum;
 
     searchkey.obj_id = EXTENT_CSUM_ID;
     searchkey.obj_type = TYPE_EXTENT_CSUM;
@@ -428,7 +457,7 @@ NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb
 
     Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -442,13 +471,15 @@ NTSTATUS load_csum(_Requires_lock_held_(_Curr_->tree_lock) device_extension* Vcb
             else
                 j = ((start - tp.item->key.offset) / Vcb->superblock.sector_size) + i;
 
-            if (j * sizeof(uint32_t) > tp.item->size || tp.item->key.offset > start + (i * Vcb->superblock.sector_size)) {
+            if (j * Vcb->csum_size > tp.item->size || tp.item->key.offset > start + (i * Vcb->superblock.sector_size)) {
                 ERR("checksum not found for %I64x\n", start + (i * Vcb->superblock.sector_size));
                 return STATUS_INTERNAL_ERROR;
             }
 
-            readlen = (ULONG)min((tp.item->size / sizeof(uint32_t)) - j, length - i);
-            RtlCopyMemory(&csum[i], tp.item->data + (j * sizeof(uint32_t)), readlen * sizeof(uint32_t));
+            readlen = (ULONG)min((tp.item->size / Vcb->csum_size) - j, length - i);
+            RtlCopyMemory(ptr, tp.item->data + (j * Vcb->csum_size), readlen * Vcb->csum_size);
+
+            ptr = (uint8_t*)ptr + (readlen * Vcb->csum_size);
             i += readlen;
 
             if (i == length)
@@ -501,7 +532,7 @@ NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extens
 
     Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         return Status;
     }
 
@@ -518,7 +549,7 @@ NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extens
         ULONG utf16len;
 
         if (tp.item->size < sizeof(DIR_ITEM)) {
-            WARN("(%I64x,%x,%I64x) 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, sizeof(DIR_ITEM));
+            WARN("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(DIR_ITEM));
             goto cont;
         }
 
@@ -529,7 +560,7 @@ NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extens
 
         Status = utf8_to_utf16(NULL, 0, &utf16len, di->name, di->n);
         if (!NT_SUCCESS(Status)) {
-            ERR("utf8_to_utf16 1 returned %08x\n", Status);
+            ERR("utf8_to_utf16 1 returned %08lx\n", Status);
             goto cont;
         }
 
@@ -568,7 +599,7 @@ NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extens
 
         Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, di->name, di->n);
         if (!NT_SUCCESS(Status)) {
-            ERR("utf8_to_utf16 2 returned %08x\n", Status);
+            ERR("utf8_to_utf16 2 returned %08lx\n", Status);
             ExFreePool(dc->utf8.Buffer);
             ExFreePool(dc->name.Buffer);
             ExFreePool(dc);
@@ -577,7 +608,7 @@ NTSTATUS load_dir_children(_Requires_lock_held_(_Curr_->tree_lock) device_extens
 
         Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
         if (!NT_SUCCESS(Status)) {
-            ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+            ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
             ExFreePool(dc->utf8.Buffer);
             ExFreePool(dc->name.Buffer);
             ExFreePool(dc);
@@ -646,7 +677,7 @@ cont:
 
             Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
             if (!NT_SUCCESS(Status)) {
-                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
                 ExFreePool(dc->utf8.Buffer);
                 ExFreePool(dc->name.Buffer);
                 ExFreePool(dc);
@@ -750,7 +781,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
     Status = find_item(Vcb, subvol, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("error - find_item returned %08x\n", Status);
+        ERR("error - find_item returned %08lx\n", Status);
         reap_fcb(fcb);
         return Status;
     }
@@ -822,7 +853,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
                 Status = utf8_to_utf16(NULL, 0, &stringlen, ir->name, ir->n);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("utf8_to_utf16 1 returned %08x\n", Status);
+                    ERR("utf8_to_utf16 1 returned %08lx\n", Status);
                     ExFreePool(hl);
                     reap_fcb(fcb);
                     return Status;
@@ -844,7 +875,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
                     Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ir->name, ir->n);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("utf8_to_utf16 2 returned %08x\n", Status);
+                        ERR("utf8_to_utf16 2 returned %08lx\n", Status);
                         ExFreePool(hl->name.Buffer);
                         ExFreePool(hl);
                         reap_fcb(fcb);
@@ -887,7 +918,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
                 Status = utf8_to_utf16(NULL, 0, &stringlen, ier->name, ier->n);
                 if (!NT_SUCCESS(Status)) {
-                    ERR("utf8_to_utf16 1 returned %08x\n", Status);
+                    ERR("utf8_to_utf16 1 returned %08lx\n", Status);
                     ExFreePool(hl);
                     reap_fcb(fcb);
                     return Status;
@@ -909,7 +940,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
                     Status = utf8_to_utf16(hl->name.Buffer, stringlen, &stringlen, ier->name, ier->n);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("utf8_to_utf16 2 returned %08x\n", Status);
+                        ERR("utf8_to_utf16 2 returned %08lx\n", Status);
                         ExFreePool(hl->name.Buffer);
                         ExFreePool(hl);
                         reap_fcb(fcb);
@@ -929,7 +960,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
             static const char xapref[] = "user.";
 
             if (tp.item->size < offsetof(DIR_ITEM, name[0])) {
-                ERR("(%I64x,%x,%I64x) 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]));
+                ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, offsetof(DIR_ITEM, name[0]));
                 continue;
             }
 
@@ -961,7 +992,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                         Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)&di->name[di->n], di->m, &offset);
 
                         if (!NT_SUCCESS(Status))
-                            WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset);
+                            WARN("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset);
                         else {
                             FILE_FULL_EA_INFORMATION* eainfo;
 
@@ -1055,7 +1086,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
                     Status = utf8_to_utf16(NULL, 0, &utf16len, &di->name[sizeof(xapref) - 1], di->n + 1 - sizeof(xapref));
                     if (!NT_SUCCESS(Status)) {
-                        ERR("utf8_to_utf16 1 returned %08x\n", Status);
+                        ERR("utf8_to_utf16 1 returned %08lx\n", Status);
                         reap_fcb(fcb);
                         return Status;
                     }
@@ -1092,7 +1123,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
                     Status = utf8_to_utf16(dc->name.Buffer, utf16len, &utf16len, dc->utf8.Buffer, dc->utf8.Length);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("utf8_to_utf16 2 returned %08x\n", Status);
+                        ERR("utf8_to_utf16 2 returned %08lx\n", Status);
                         ExFreePool(dc->utf8.Buffer);
                         ExFreePool(dc->name.Buffer);
                         ExFreePool(dc);
@@ -1102,7 +1133,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
 
                     Status = RtlUpcaseUnicodeString(&dc->name_uc, &dc->name, true);
                     if (!NT_SUCCESS(Status)) {
-                        ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                        ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
                         ExFreePool(dc->utf8.Buffer);
                         ExFreePool(dc->name.Buffer);
                         ExFreePool(dc);
@@ -1145,7 +1176,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
             ed = (EXTENT_DATA*)tp.item->data;
 
             if (tp.item->size < sizeof(EXTENT_DATA)) {
-                ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
                     tp.item->size, sizeof(EXTENT_DATA));
 
                 reap_fcb(fcb);
@@ -1156,7 +1187,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
                 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0];
 
                 if (tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                    ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
+                    ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
                         tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
 
                     reap_fcb(fcb);
@@ -1192,7 +1223,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
     if (fcb->type == BTRFS_TYPE_DIRECTORY) {
         Status = load_dir_children(Vcb, fcb, false, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("load_dir_children returned %08x\n", Status);
+            ERR("load_dir_children returned %08lx\n", Status);
             reap_fcb(fcb);
             return Status;
         }
@@ -1309,7 +1340,7 @@ NTSTATUS open_fcb(_Requires_lock_held_(_Curr_->tree_lock) _Requires_exclusive_lo
         }
     }
 
-    if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE)
+    if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE && fcb->subvol != Vcb->root_fileref->fcb->subvol)
         fcb->atts |= FILE_ATTRIBUTE_HIDDEN;
 
     subvol->fcbs_version++;
@@ -1382,7 +1413,7 @@ static NTSTATUS open_fcb_stream(_Requires_lock_held_(_Curr_->tree_lock) _Require
 
     Status = find_item(Vcb, parent->subvol, &tp, &searchkey, false, Irp);
     if (!NT_SUCCESS(Status)) {
-        ERR("find_item returned %08x\n", Status);
+        ERR("find_item returned %08lx\n", Status);
         reap_fcb(fcb);
         return Status;
     }
@@ -1439,7 +1470,7 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
         if (!case_sensitive) {
             Status = RtlUpcaseUnicodeString(&name_uc, name, true);
             if (!NT_SUCCESS(Status)) {
-                ERR("RtlUpcaseUnicodeString returned %08x\n", Status);
+                ERR("RtlUpcaseUnicodeString returned %08lx\n", Status);
                 return Status;
             }
         }
@@ -1496,7 +1527,7 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
 
         Status = open_fcb_stream(Vcb, dc, sf->fcb, &fcb, Irp);
         if (!NT_SUCCESS(Status)) {
-            ERR("open_fcb_stream returned %08x\n", Status);
+            ERR("open_fcb_stream returned %08lx\n", Status);
             return Status;
         }
 
@@ -1569,11 +1600,11 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
 
         Status = find_file_in_dir(name, sf->fcb, &subvol, &inode, &dc, case_sensitive);
         if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
-            TRACE("could not find %.*S\n", name->Length / sizeof(WCHAR), name->Buffer);
+            TRACE("could not find %.*S\n", (int)(name->Length / sizeof(WCHAR)), name->Buffer);
 
             return lastpart ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_OBJECT_PATH_NOT_FOUND;
         } else if (!NT_SUCCESS(Status)) {
-            ERR("find_file_in_dir returned %08x\n", Status);
+            ERR("find_file_in_dir returned %08lx\n", Status);
             return Status;
         } else {
             fcb* fcb;
@@ -1597,7 +1628,7 @@ NTSTATUS open_fileref_child(_Requires_lock_held_(_Curr_->tree_lock) _Requires_ex
                 Status =&