Index: dll/shellext/shellbtrfs/CMakeLists.txt
===================================================================
--- dll/shellext/shellbtrfs/CMakeLists.txt	(révision 75772)
+++ dll/shellext/shellbtrfs/CMakeLists.txt	(copie de travail)
@@ -1,18 +1,28 @@
 set_cpp(WITH_RUNTIME WITH_EXCEPTIONS WITH_STL)
 
 remove_definitions(-D_WIN32_WINNT=0x502)
-add_definitions(-D_WIN32_WINNT=0x600)
+add_definitions(-D_WIN32_WINNT=0x603)
 
 spec2def(shellbtrfs.dll shellbtrfs.spec)
 
 list(APPEND SOURCE
+    balance.cpp
     contextmenu.cpp
+    devices.cpp
     factory.cpp
     iconoverlay.cpp
     main.cpp
     propsheet.cpp
+    reactos.cpp
+    recv.cpp
+    scrub.cpp
+    send.cpp
+    volpropsheet.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/shellbtrfs.def)
 
+include_directories(
+    ${REACTOS_SOURCE_DIR}/drivers/filesystems/btrfs)
+
 file(GLOB shellbtrfs_rc_deps *.ico)
 add_rc_deps(shellbtrfs.rc ${shellbtrfs_rc_deps})
 
@@ -19,5 +29,5 @@
 add_library(shellbtrfs SHARED ${SOURCE} shellbtrfs.rc)
 set_module_type(shellbtrfs win32dll UNICODE)
 target_link_libraries(shellbtrfs uuid)
-add_importlibs(shellbtrfs advapi32 advapi32_vista ole32 shell32 shlwapi user32 comctl32 uxtheme msvcrt kernel32 ntdll)
+add_importlibs(shellbtrfs advapi32 advapi32_vista ole32 shell32 shlwapi user32 comctl32 uxtheme setupapi comdlg32 gdi32 msvcrt kernel32_vista kernel32 ntdll)
 add_cd_file(TARGET shellbtrfs DESTINATION reactos/system32 FOR all)
Index: dll/shellext/shellbtrfs/balance.cpp
===================================================================
--- dll/shellext/shellbtrfs/balance.cpp	(nonexistent)
+++ dll/shellext/shellbtrfs/balance.cpp	(copie de travail)
@@ -0,0 +1,1293 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ *
+ * 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 "shellext.h"
+#include "balance.h"
+#include "resource.h"
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "btrfsioctl.h"
+#endif
+#include <shlobj.h>
+#include <uxtheme.h>
+#include <stdio.h>
+#ifndef __REACTOS__
+#include <strsafe.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <strsafe.h>
+#include <ndk/iofuncs.h>
+#include <ndk/iotypes.h>
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include <shlwapi.h>
+#include <uxtheme.h>
+
+static UINT64 convtypes2[] = { BLOCK_FLAG_SINGLE, BLOCK_FLAG_DUPLICATE, BLOCK_FLAG_RAID0, BLOCK_FLAG_RAID1, BLOCK_FLAG_RAID5, BLOCK_FLAG_RAID6, BLOCK_FLAG_RAID10 };
+
+static WCHAR hex_digit(UINT8 u) {
+    if (u >= 0xa && u <= 0xf)
+        return u - 0xa + 'a';
+    else
+        return u + '0';
+}
+
+static void serialize(void* data, ULONG len, WCHAR* s) {
+    UINT8* d;
+
+    d = (UINT8*)data;
+
+    while (TRUE) {
+        *s = hex_digit(*d >> 4); s++;
+        *s = hex_digit(*d & 0xf); s++;
+
+        d++;
+        len--;
+
+        if (len == 0) {
+            *s = 0;
+            return;
+        }
+    }
+}
+
+void BtrfsBalance::StartBalance(HWND hwndDlg) {
+    WCHAR t[MAX_PATH + 600], u[600];
+    SHELLEXECUTEINFOW sei;
+    btrfs_start_balance bsb;
+
+    t[0] = '"';
+    GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+    wcscat(t, L"\",StartBalance ");
+    wcscat(t, fn);
+    wcscat(t, L" ");
+
+    RtlCopyMemory(&bsb.opts[0], &data_opts, sizeof(btrfs_balance_opts));
+    RtlCopyMemory(&bsb.opts[1], &metadata_opts, sizeof(btrfs_balance_opts));
+    RtlCopyMemory(&bsb.opts[2], &system_opts, sizeof(btrfs_balance_opts));
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED)
+        bsb.opts[0].flags |= BTRFS_BALANCE_OPTS_ENABLED;
+    else
+        bsb.opts[0].flags &= ~BTRFS_BALANCE_OPTS_ENABLED;
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED)
+        bsb.opts[1].flags |= BTRFS_BALANCE_OPTS_ENABLED;
+    else
+        bsb.opts[1].flags &= ~BTRFS_BALANCE_OPTS_ENABLED;
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED)
+        bsb.opts[2].flags |= BTRFS_BALANCE_OPTS_ENABLED;
+    else
+        bsb.opts[2].flags &= ~BTRFS_BALANCE_OPTS_ENABLED;
+
+    serialize(&bsb, sizeof(btrfs_start_balance), u);
+    wcscat(t, u);
+
+    RtlZeroMemory(&sei, sizeof(sei));
+
+    sei.cbSize = sizeof(sei);
+    sei.hwnd = hwndDlg;
+    sei.lpVerb = L"runas";
+    sei.lpFile = L"rundll32.exe";
+    sei.lpParameters = t;
+    sei.nShow = SW_SHOW;
+    sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+    if (!ShellExecuteExW(&sei)) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    cancelling = FALSE;
+    removing = FALSE;
+    shrinking = FALSE;
+    balance_status = BTRFS_BALANCE_RUNNING;
+
+    EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), TRUE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), TRUE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), TRUE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), FALSE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), FALSE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), FALSE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? TRUE : FALSE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? TRUE : FALSE);
+    EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? TRUE : FALSE);
+
+    EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), FALSE);
+
+    WaitForSingleObject(sei.hProcess, INFINITE);
+    CloseHandle(sei.hProcess);
+}
+
+void BtrfsBalance::PauseBalance(HWND hwndDlg) {
+    WCHAR t[MAX_PATH + 100];
+    SHELLEXECUTEINFOW sei;
+
+    t[0] = '"';
+    GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+    wcscat(t, L"\",PauseBalance ");
+    wcscat(t, fn);
+
+    RtlZeroMemory(&sei, sizeof(sei));
+
+    sei.cbSize = sizeof(sei);
+    sei.hwnd = hwndDlg;
+    sei.lpVerb = L"runas";
+    sei.lpFile = L"rundll32.exe";
+    sei.lpParameters = t;
+    sei.nShow = SW_SHOW;
+    sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+    if (!ShellExecuteExW(&sei)) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    WaitForSingleObject(sei.hProcess, INFINITE);
+    CloseHandle(sei.hProcess);
+}
+
+void BtrfsBalance::StopBalance(HWND hwndDlg) {
+    WCHAR t[MAX_PATH + 100];
+    SHELLEXECUTEINFOW sei;
+
+    t[0] = '"';
+    GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+    wcscat(t, L"\",StopBalance ");
+    wcscat(t, fn);
+
+    RtlZeroMemory(&sei, sizeof(sei));
+
+    sei.cbSize = sizeof(sei);
+    sei.hwnd = hwndDlg;
+    sei.lpVerb = L"runas";
+    sei.lpFile = L"rundll32.exe";
+    sei.lpParameters = t;
+    sei.nShow = SW_SHOW;
+    sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+    if (!ShellExecuteExW(&sei)) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    cancelling = TRUE;
+
+    WaitForSingleObject(sei.hProcess, INFINITE);
+    CloseHandle(sei.hProcess);
+}
+
+void BtrfsBalance::RefreshBalanceDlg(HWND hwndDlg, BOOL first) {
+    HANDLE h;
+    BOOL balancing = FALSE;
+    WCHAR s[255], t[255];
+
+    h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_BALANCE, NULL, 0, &bqb, sizeof(btrfs_query_balance));
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwndDlg, Status);
+            CloseHandle(h);
+            return;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    if (cancelling)
+        bqb.status = BTRFS_BALANCE_STOPPED;
+
+    balancing = bqb.status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED);
+
+    if (!balancing) {
+        if (first || balance_status != BTRFS_BALANCE_STOPPED) {
+            int resid;
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), FALSE);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), TRUE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), TRUE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), TRUE);
+
+            if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED)) {
+                CheckDlgButton(hwndDlg, IDC_DATA, BST_UNCHECKED);
+                CheckDlgButton(hwndDlg, IDC_METADATA, BST_UNCHECKED);
+                CheckDlgButton(hwndDlg, IDC_SYSTEM, BST_UNCHECKED);
+
+                SendMessage(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETPOS, 0, 0);
+            }
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED ? TRUE : FALSE);
+
+            if (bqb.status & BTRFS_BALANCE_ERROR) {
+                if (removing)
+                    resid = IDS_BALANCE_FAILED_REMOVAL;
+                else if (shrinking)
+                    resid = IDS_BALANCE_FAILED_SHRINK;
+                else
+                    resid = IDS_BALANCE_FAILED;
+
+                if (!LoadStringW(module, resid, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return;
+                }
+
+                if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, bqb.error, format_ntstatus(bqb.error).c_str()) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return;
+
+                SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, t);
+            } else {
+                if (cancelling)
+                    resid = removing ? IDS_BALANCE_CANCELLED_REMOVAL : (shrinking ? IDS_BALANCE_CANCELLED_SHRINK : IDS_BALANCE_CANCELLED);
+                else if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))
+                    resid = removing ? IDS_BALANCE_COMPLETE_REMOVAL : (shrinking ? IDS_BALANCE_COMPLETE_SHRINK : IDS_BALANCE_COMPLETE);
+                else
+                    resid = IDS_NO_BALANCE;
+
+                if (!LoadStringW(module, resid, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return;
+                }
+
+                SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, s);
+            }
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
+                         IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? TRUE: FALSE);
+
+            balance_status = bqb.status;
+            cancelling = FALSE;
+        }
+
+        return;
+    }
+
+    if (first || !(balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))) {
+        EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), TRUE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), TRUE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), TRUE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), FALSE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), FALSE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), FALSE);
+
+        CheckDlgButton(hwndDlg, IDC_DATA, bqb.data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED);
+        CheckDlgButton(hwndDlg, IDC_METADATA, bqb.metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED);
+        CheckDlgButton(hwndDlg, IDC_SYSTEM, bqb.system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED);
+
+        EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), bqb.data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? TRUE : FALSE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), bqb.metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? TRUE : FALSE);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), bqb.system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? TRUE : FALSE);
+
+        EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), FALSE);
+    }
+
+    SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)bqb.total_chunks);
+    SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETPOS, (WPARAM)(bqb.total_chunks - bqb.chunks_left), 0);
+
+    if (bqb.status & BTRFS_BALANCE_PAUSED && balance_status != bqb.status)
+        SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_PAUSED, 0);
+    else if (!(bqb.status & BTRFS_BALANCE_PAUSED) && balance_status & BTRFS_BALANCE_PAUSED)
+        SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
+
+    balance_status = bqb.status;
+
+    if (bqb.status & BTRFS_BALANCE_REMOVAL) {
+        if (!LoadStringW(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED_REMOVAL : IDS_BALANCE_RUNNING_REMOVAL, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, bqb.data_opts.devid, bqb.total_chunks - bqb.chunks_left,
+            bqb.total_chunks, (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+
+        removing = TRUE;
+        shrinking = FALSE;
+    } else if (bqb.status & BTRFS_BALANCE_SHRINKING) {
+        if (!LoadStringW(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED_SHRINK : IDS_BALANCE_RUNNING_SHRINK, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, bqb.data_opts.devid, bqb.total_chunks - bqb.chunks_left,
+            bqb.total_chunks, (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+
+        removing = FALSE;
+        shrinking = TRUE;
+    } else {
+        if (!LoadStringW(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED : IDS_BALANCE_RUNNING, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, bqb.total_chunks - bqb.chunks_left,
+            bqb.total_chunks, (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+
+        removing = FALSE;
+        shrinking = FALSE;
+    }
+
+    SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, t);
+}
+
+void BtrfsBalance::SaveBalanceOpts(HWND hwndDlg) {
+    btrfs_balance_opts* opts;
+
+    switch (opts_type) {
+        case 1:
+            opts = &data_opts;
+        break;
+
+        case 2:
+            opts = &metadata_opts;
+        break;
+
+        case 3:
+            opts = &system_opts;
+        break;
+
+        default:
+            return;
+    }
+
+    RtlZeroMemory(opts, sizeof(btrfs_balance_opts));
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES) == BST_CHECKED) {
+        opts->flags |= BTRFS_BALANCE_OPTS_PROFILES;
+
+        if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_SINGLE) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_SINGLE;
+        if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_DUP) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_DUPLICATE;
+        if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID0) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID0;
+        if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1;
+        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_DEVID) == BST_CHECKED) {
+        int sel;
+
+        opts->flags |= BTRFS_BALANCE_OPTS_DEVID;
+
+        sel = SendMessageW(GetDlgItem(hwndDlg, IDC_DEVID_COMBO), CB_GETCURSEL, 0, 0);
+
+        if (sel == CB_ERR)
+            opts->flags &= ~BTRFS_BALANCE_OPTS_DEVID;
+        else {
+            btrfs_device* bd = devices;
+            int i = 0;
+
+            while (TRUE) {
+                if (i == sel) {
+                    opts->devid = bd->dev_id;
+                    break;
+                }
+
+                i++;
+
+                if (bd->next_entry > 0)
+                    bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+                else
+                    break;
+            }
+
+            if (opts->devid == 0)
+                opts->flags &= ~BTRFS_BALANCE_OPTS_DEVID;
+        }
+    }
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_DRANGE) == BST_CHECKED) {
+        WCHAR s[255];
+
+        opts->flags |= BTRFS_BALANCE_OPTS_DRANGE;
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_DRANGE_START), s, sizeof(s) / sizeof(WCHAR));
+        opts->drange_start = _wtoi64(s);
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_DRANGE_END), s, sizeof(s) / sizeof(WCHAR));
+        opts->drange_end = _wtoi64(s);
+
+        if (opts->drange_end < opts->drange_start) {
+            ShowStringError(hwndDlg, IDS_DRANGE_END_BEFORE_START);
+            return;
+        }
+    }
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_VRANGE) == BST_CHECKED) {
+        WCHAR s[255];
+
+        opts->flags |= BTRFS_BALANCE_OPTS_VRANGE;
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_VRANGE_START), s, sizeof(s) / sizeof(WCHAR));
+        opts->vrange_start = _wtoi64(s);
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_VRANGE_END), s, sizeof(s) / sizeof(WCHAR));
+        opts->vrange_end = _wtoi64(s);
+
+        if (opts->vrange_end < opts->vrange_start) {
+            ShowStringError(hwndDlg, IDS_VRANGE_END_BEFORE_START);
+            return;
+        }
+    }
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_LIMIT) == BST_CHECKED) {
+        WCHAR s[255];
+
+        opts->flags |= BTRFS_BALANCE_OPTS_LIMIT;
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_LIMIT_START), s, sizeof(s) / sizeof(WCHAR));
+        opts->limit_start = _wtoi64(s);
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_LIMIT_END), s, sizeof(s) / sizeof(WCHAR));
+        opts->limit_end = _wtoi64(s);
+
+        if (opts->limit_end < opts->limit_start) {
+            ShowStringError(hwndDlg, IDS_LIMIT_END_BEFORE_START);
+            return;
+        }
+    }
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_STRIPES) == BST_CHECKED) {
+        WCHAR s[255];
+
+        opts->flags |= BTRFS_BALANCE_OPTS_STRIPES;
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_STRIPES_START), s, sizeof(s) / sizeof(WCHAR));
+        opts->stripes_start = _wtoi(s);
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_STRIPES_END), s, sizeof(s) / sizeof(WCHAR));
+        opts->stripes_end = _wtoi(s);
+
+        if (opts->stripes_end < opts->stripes_start) {
+            ShowStringError(hwndDlg, IDS_STRIPES_END_BEFORE_START);
+            return;
+        }
+    }
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_USAGE) == BST_CHECKED) {
+        WCHAR s[255];
+
+        opts->flags |= BTRFS_BALANCE_OPTS_USAGE;
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_USAGE_START), s, sizeof(s) / sizeof(WCHAR));
+        opts->usage_start = _wtoi(s);
+
+        GetWindowTextW(GetDlgItem(hwndDlg, IDC_USAGE_END), s, sizeof(s) / sizeof(WCHAR));
+        opts->usage_end = _wtoi(s);
+
+        if (opts->usage_end < opts->usage_start) {
+            ShowStringError(hwndDlg, IDS_USAGE_END_BEFORE_START);
+            return;
+        }
+    }
+
+    if (IsDlgButtonChecked(hwndDlg, IDC_CONVERT) == BST_CHECKED) {
+        int sel;
+
+        opts->flags |= BTRFS_BALANCE_OPTS_CONVERT;
+
+        sel = SendMessageW(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), CB_GETCURSEL, 0, 0);
+
+        if (sel == CB_ERR || (unsigned int)sel >= sizeof(convtypes2) / sizeof(convtypes2[0]))
+            opts->flags &= ~BTRFS_BALANCE_OPTS_CONVERT;
+        else {
+            opts->convert = convtypes2[sel];
+
+            if (IsDlgButtonChecked(hwndDlg, IDC_SOFT) == BST_CHECKED) opts->flags |= BTRFS_BALANCE_OPTS_SOFT;
+        }
+    }
+
+    EndDialog(hwndDlg, 0);
+}
+
+INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            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 };
+            int i, num_devices = 0, num_writeable_devices = 0;
+            WCHAR s[255], u[255];
+            BOOL balance_started = balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED);
+
+            switch (opts_type) {
+                case 1:
+                    opts = balance_started ? &bqb.data_opts : &data_opts;
+                break;
+
+                case 2:
+                    opts = balance_started ? &bqb.metadata_opts : &metadata_opts;
+                break;
+
+                case 3:
+                    opts = balance_started ? &bqb.system_opts : &system_opts;
+                break;
+
+                default:
+                    return TRUE;
+            }
+
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+
+            devcb = GetDlgItem(hwndDlg, IDC_DEVID_COMBO);
+
+            if (!LoadStringW(module, IDS_DEVID_LIST, u, sizeof(u) / sizeof(WCHAR))) {
+                ShowError(hwndDlg, GetLastError());
+                return TRUE;
+            }
+
+            bd = devices;
+            while (TRUE) {
+                WCHAR t[255], v[255];
+
+                if (bd->device_number == 0xffffffff) {
+                    memcpy(s, bd->name, bd->namelen);
+                    s[bd->namelen / sizeof(WCHAR)] = 0;
+                } else if (bd->partition_number == 0) {
+                    if (!LoadStringW(module, IDS_DISK_NUM, v, sizeof(v) / sizeof(WCHAR))) {
+                        ShowError(hwndDlg, GetLastError());
+                        return TRUE;
+                    }
+
+                    if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), v, bd->device_number) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        break;
+                } else {
+                    if (!LoadStringW(module, IDS_DISK_PART_NUM, v, sizeof(v) / sizeof(WCHAR))) {
+                        ShowError(hwndDlg, GetLastError());
+                        return TRUE;
+                    }
+
+                    if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), v, bd->device_number, bd->partition_number) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        break;
+                }
+
+                if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, bd->dev_id, s) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    break;
+
+                SendMessage(devcb, CB_ADDSTRING, NULL, (LPARAM)t);
+
+                if (opts->devid == bd->dev_id)
+                    SendMessage(devcb, CB_SETCURSEL, num_devices, 0);
+
+                num_devices++;
+
+                if (!bd->readonly)
+                    num_writeable_devices++;
+
+                if (bd->next_entry > 0)
+                    bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+                else
+                    break;
+            }
+
+            convcb = GetDlgItem(hwndDlg, IDC_CONVERT_COMBO);
+
+            if (num_writeable_devices == 0)
+                num_writeable_devices = num_devices;
+
+            i = 0;
+            while (convtypes[i] != 0) {
+                if (!LoadStringW(module, convtypes[i], s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    break;
+                }
+
+                SendMessage(convcb, CB_ADDSTRING, NULL, (LPARAM)s);
+
+                if (opts->convert == convtypes2[i])
+                    SendMessage(convcb, CB_SETCURSEL, i, 0);
+
+                i++;
+
+                if (num_writeable_devices < 2 && i == 2)
+                    break;
+                else if (num_writeable_devices < 3 && i == 4)
+                    break;
+                else if (num_writeable_devices < 4 && i == 5)
+                    break;
+            }
+
+            // profiles
+
+            CheckDlgButton(hwndDlg, IDC_PROFILES, opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? BST_CHECKED : BST_UNCHECKED);
+            CheckDlgButton(hwndDlg, IDC_PROFILES_SINGLE, opts->profiles & BLOCK_FLAG_SINGLE ? BST_CHECKED : BST_UNCHECKED);
+            CheckDlgButton(hwndDlg, IDC_PROFILES_DUP, opts->profiles & BLOCK_FLAG_DUPLICATE ? BST_CHECKED : BST_UNCHECKED);
+            CheckDlgButton(hwndDlg, IDC_PROFILES_RAID0, opts->profiles & BLOCK_FLAG_RAID0 ? BST_CHECKED : BST_UNCHECKED);
+            CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1, opts->profiles & BLOCK_FLAG_RAID1 ? BST_CHECKED : BST_UNCHECKED);
+            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);
+
+            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);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID0), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? TRUE : FALSE);
+            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), balance_started ? FALSE : TRUE);
+
+            // usage
+
+            CheckDlgButton(hwndDlg, IDC_USAGE, opts->flags & BTRFS_BALANCE_OPTS_USAGE ? BST_CHECKED : BST_UNCHECKED);
+
+            _itow(opts->usage_start, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_USAGE_START, s);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), UDM_SETRANGE32, 0, 100);
+
+            _itow(opts->usage_end, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_USAGE_END, s);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), UDM_SETRANGE32, 0, 100);
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE), balance_started ? FALSE : TRUE);
+
+            // devid
+
+            if (num_devices < 2 || balance_started)
+                EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID), FALSE);
+
+            CheckDlgButton(hwndDlg, IDC_DEVID, opts->flags & BTRFS_BALANCE_OPTS_DEVID ? BST_CHECKED : BST_UNCHECKED);
+            EnableWindow(devcb, (opts->flags & BTRFS_BALANCE_OPTS_DEVID && num_devices >= 2 && !balance_started) ? TRUE : FALSE);
+
+            // drange
+
+            CheckDlgButton(hwndDlg, IDC_DRANGE, opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? BST_CHECKED : BST_UNCHECKED);
+
+            _itow(opts->drange_start, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_DRANGE_START, s);
+
+            _itow(opts->drange_end, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_DRANGE_END, s);
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE), balance_started ? FALSE : TRUE);
+
+            // vrange
+
+            CheckDlgButton(hwndDlg, IDC_VRANGE, opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? BST_CHECKED : BST_UNCHECKED);
+
+            _itow(opts->vrange_start, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_VRANGE_START, s);
+
+            _itow(opts->vrange_end, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_VRANGE_END, s);
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE), balance_started ? FALSE : TRUE);
+
+            // limit
+
+            CheckDlgButton(hwndDlg, IDC_LIMIT, opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? BST_CHECKED : BST_UNCHECKED);
+
+            _itow(opts->limit_start, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_LIMIT_START, s);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff);
+
+            _itow(opts->limit_end, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_LIMIT_END, s);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff);
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT), balance_started ? FALSE : TRUE);
+
+            // stripes
+
+            CheckDlgButton(hwndDlg, IDC_STRIPES, opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? BST_CHECKED : BST_UNCHECKED);
+
+            _itow(opts->stripes_start, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_STRIPES_START, s);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), UDM_SETRANGE32, 0, 0xffff);
+
+            _itow(opts->stripes_end, s, 10);
+            SetDlgItemTextW(hwndDlg, IDC_STRIPES_END, s);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), UDM_SETRANGE32, 0, 0xffff);
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES), balance_started ? FALSE : TRUE);
+
+            // convert
+
+            CheckDlgButton(hwndDlg, IDC_CONVERT, opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? BST_CHECKED : BST_UNCHECKED);
+            CheckDlgButton(hwndDlg, IDC_SOFT, opts->flags & BTRFS_BALANCE_OPTS_SOFT ? BST_CHECKED : BST_UNCHECKED);
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? TRUE : FALSE);
+            EnableWindow(convcb, !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? TRUE : FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT), balance_started ? FALSE : TRUE);
+
+            break;
+        }
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                            if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))
+                                EndDialog(hwndDlg, 0);
+                            else
+                                SaveBalanceOpts(hwndDlg);
+                        return TRUE;
+
+                        case IDCANCEL:
+                            EndDialog(hwndDlg, 0);
+                        return TRUE;
+
+                        case IDC_PROFILES: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_PROFILES) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_SINGLE), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_DUP), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID0), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), enabled);
+                            break;
+                        }
+
+                        case IDC_USAGE: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_USAGE) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), enabled);
+                            break;
+                        }
+
+                        case IDC_DEVID: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID_COMBO), enabled);
+                            break;
+                        }
+
+                        case IDC_DRANGE: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_DRANGE) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), enabled);
+                            break;
+                        }
+
+                        case IDC_VRANGE: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_VRANGE) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), enabled);
+                            break;
+                        }
+
+                        case IDC_LIMIT: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_LIMIT) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), enabled);
+                            break;
+                        }
+
+                        case IDC_STRIPES: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_STRIPES) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), enabled);
+                            break;
+                        }
+
+                        case IDC_CONVERT: {
+                            BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_CONVERT) == BST_CHECKED ? TRUE : FALSE;
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), enabled);
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), enabled);
+                            break;
+                        }
+                    }
+                break;
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsBalance* bb;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bb = (BtrfsBalance*)lParam;
+    } else {
+        bb = (BtrfsBalance*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (bb)
+        return bb->BalanceOptsDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsBalance::ShowBalanceOptions(HWND hwndDlg, UINT8 type) {
+    opts_type = type;
+    DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE_OPTIONS), hwndDlg, stub_BalanceOptsDlgProc, (LPARAM)this);
+}
+
+INT_PTR CALLBACK BtrfsBalance::BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+
+            RtlZeroMemory(&data_opts, sizeof(btrfs_balance_opts));
+            RtlZeroMemory(&metadata_opts, sizeof(btrfs_balance_opts));
+            RtlZeroMemory(&system_opts, sizeof(btrfs_balance_opts));
+
+            removing = called_from_RemoveDevice;
+            shrinking = called_from_ShrinkDevice;
+            balance_status = (removing || shrinking) ? BTRFS_BALANCE_RUNNING : BTRFS_BALANCE_STOPPED;
+            cancelling = FALSE;
+            RefreshBalanceDlg(hwndDlg, TRUE);
+
+            if (readonly) {
+                EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), FALSE);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), FALSE);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), FALSE);
+            }
+
+            SendMessageW(GetDlgItem(hwndDlg, IDC_START_BALANCE), BCM_SETSHIELD, 0, TRUE);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), BCM_SETSHIELD, 0, TRUE);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), BCM_SETSHIELD, 0, TRUE);
+
+            SetTimer(hwndDlg, 1, 1000, NULL);
+
+            break;
+        }
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                        case IDCANCEL:
+                            KillTimer(hwndDlg, 1);
+                            EndDialog(hwndDlg, 0);
+                        return TRUE;
+
+                        case IDC_DATA:
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ? TRUE : FALSE);
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
+                            IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? TRUE: FALSE);
+                        return TRUE;
+
+                        case IDC_METADATA:
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED ? TRUE : FALSE);
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
+                            IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? TRUE: FALSE);
+                        return TRUE;
+
+                        case IDC_SYSTEM:
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED ? TRUE : FALSE);
+
+                            EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
+                            IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? TRUE: FALSE);
+                        return TRUE;
+
+                        case IDC_DATA_OPTIONS:
+                            ShowBalanceOptions(hwndDlg, 1);
+                        return TRUE;
+
+                        case IDC_METADATA_OPTIONS:
+                            ShowBalanceOptions(hwndDlg, 2);
+                        return TRUE;
+
+                        case IDC_SYSTEM_OPTIONS:
+                            ShowBalanceOptions(hwndDlg, 3);
+                        return TRUE;
+
+                        case IDC_START_BALANCE:
+                            StartBalance(hwndDlg);
+                        return TRUE;
+
+                        case IDC_PAUSE_BALANCE:
+                            PauseBalance(hwndDlg);
+                            RefreshBalanceDlg(hwndDlg, FALSE);
+                        return TRUE;
+
+                        case IDC_CANCEL_BALANCE:
+                            StopBalance(hwndDlg);
+                            RefreshBalanceDlg(hwndDlg, FALSE);
+                        return TRUE;
+                    }
+                break;
+            }
+        break;
+
+        case WM_TIMER:
+            RefreshBalanceDlg(hwndDlg, FALSE);
+            break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsBalance* bb;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bb = (BtrfsBalance*)lParam;
+    } else {
+        bb = (BtrfsBalance*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (bb)
+        return bb->BalanceDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsBalance::ShowBalance(HWND hwndDlg) {
+    HANDLE h;
+    btrfs_device* bd;
+
+    if (devices) {
+        free(devices);
+        devices = NULL;
+    }
+
+    h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+        ULONG devsize, i;
+
+        i = 0;
+        devsize = 1024;
+
+        devices = (btrfs_device*)malloc(devsize);
+
+        while (TRUE) {
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_DEVICES, NULL, 0, devices, devsize);
+            if (Status == STATUS_BUFFER_OVERFLOW) {
+                if (i < 8) {
+                    devsize += 1024;
+
+                    free(devices);
+                    devices = (btrfs_device*)malloc(devsize);
+
+                    i++;
+                } else
+                    return;
+            } else
+                break;
+        }
+
+        if (!NT_SUCCESS(Status)) {
+            CloseHandle(h);
+            ShowNtStatusError(hwndDlg, Status);
+            return;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    readonly = TRUE;
+    bd = devices;
+
+    while (TRUE) {
+        if (!bd->readonly) {
+            readonly = FALSE;
+            break;
+        }
+
+        if (bd->next_entry > 0)
+            bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+        else
+            break;
+    }
+
+    DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE), hwndDlg, stub_BalanceDlgProc, (LPARAM)this);
+}
+
+static UINT8 from_hex_digit(WCHAR c) {
+    if (c >= 'a' && c <= 'f')
+        return c - 'a' + 0xa;
+    else if (c >= 'A' && c <= 'F')
+        return c - 'A' + 0xa;
+    else
+        return c - '0';
+}
+
+static void unserialize(void* data, ULONG len, WCHAR* s) {
+    UINT8* d;
+
+    d = (UINT8*)data;
+
+    while (s[0] != 0 && s[1] != 0 && len > 0) {
+        *d = from_hex_digit(s[0]) << 4 | from_hex_digit(s[1]);
+
+        s += 2;
+        d++;
+        len--;
+    }
+}
+
+void CALLBACK StartBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    WCHAR *s, *vol, *block;
+    HANDLE h, token;
+    btrfs_start_balance bsb;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+
+    s = wcsstr(lpszCmdLine, L" ");
+    if (!s)
+        return;
+
+    s[0] = 0;
+
+    vol = lpszCmdLine;
+    block = &s[1];
+
+    RtlZeroMemory(&bsb, sizeof(btrfs_start_balance));
+    unserialize(&bsb, sizeof(btrfs_start_balance), block);
+
+   if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+   }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    h = CreateFileW(vol, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_START_BALANCE, &bsb, sizeof(btrfs_start_balance), NULL, 0);
+
+        if (Status == STATUS_DEVICE_NOT_READY) {
+            btrfs_query_scrub bqs;
+            NTSTATUS Status2;
+
+            Status2 = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_SCRUB, NULL, 0, &bqs, sizeof(btrfs_query_scrub));
+
+            if ((NT_SUCCESS(Status2) || Status2 == STATUS_BUFFER_OVERFLOW) && bqs.status != BTRFS_SCRUB_STOPPED) {
+                ShowStringError(hwnd, IDS_BALANCE_SCRUB_RUNNING);
+                CloseHandle(h);
+                goto end;
+            }
+        }
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwnd, Status);
+            CloseHandle(h);
+            goto end;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+end:
+    CloseHandle(token);
+}
+
+void CALLBACK PauseBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    HANDLE h, token;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+        btrfs_query_balance bqb2;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_BALANCE, NULL, 0, &bqb2, sizeof(btrfs_query_balance));
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwnd, Status);
+            CloseHandle(h);
+            goto end;
+        }
+
+        if (bqb2.status & BTRFS_BALANCE_PAUSED)
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESUME_BALANCE, NULL, 0, NULL, 0);
+        else if (bqb2.status & BTRFS_BALANCE_RUNNING)
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_PAUSE_BALANCE, NULL, 0, NULL, 0);
+        else {
+            CloseHandle(h);
+            goto end;
+        }
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwnd, Status);
+            CloseHandle(h);
+            goto end;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+end:
+    CloseHandle(token);
+}
+
+void CALLBACK StopBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    HANDLE h, token;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+        btrfs_query_balance bqb2;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_BALANCE, NULL, 0, &bqb2, sizeof(btrfs_query_balance));
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwnd, Status);
+            CloseHandle(h);
+            goto end;
+        }
+
+        if (bqb2.status & BTRFS_BALANCE_PAUSED || bqb2.status & BTRFS_BALANCE_RUNNING)
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_STOP_BALANCE, NULL, 0, NULL, 0);
+        else {
+            CloseHandle(h);
+            goto end;
+        }
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwnd, Status);
+            CloseHandle(h);
+            goto end;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+end:
+    CloseHandle(token);
+}

Modification de propriétés sur dll/shellext/shellbtrfs/balance.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/balance.h
===================================================================
--- dll/shellext/shellbtrfs/balance.h	(nonexistent)
+++ dll/shellext/shellbtrfs/balance.h	(copie de travail)
@@ -0,0 +1,58 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ *
+ * 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 <windows.h>
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "btrfsioctl.h"
+#endif
+
+class BtrfsBalance {
+public:
+    BtrfsBalance(WCHAR* drive, BOOL RemoveDevice = FALSE, BOOL ShrinkDevice = FALSE) {
+        removing = FALSE;
+        devices = NULL;
+        called_from_RemoveDevice = RemoveDevice;
+        called_from_ShrinkDevice = ShrinkDevice;
+        wcscpy(fn, drive);
+    }
+
+    void ShowBalance(HWND hwndDlg);
+    INT_PTR CALLBACK BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+    INT_PTR CALLBACK BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+private:
+    void ShowBalanceOptions(HWND hwndDlg, UINT8 type);
+    void SaveBalanceOpts(HWND hwndDlg);
+    void StartBalance(HWND hwndDlg);
+    void RefreshBalanceDlg(HWND hwndDlg, BOOL first);
+    void PauseBalance(HWND hwndDlg);
+    void StopBalance(HWND hwndDlg);
+
+    UINT32 balance_status;
+    btrfs_balance_opts data_opts, metadata_opts, system_opts;
+    UINT8 opts_type;
+    btrfs_query_balance bqb;
+    BOOL cancelling;
+    BOOL removing;
+    BOOL shrinking;
+    WCHAR fn[MAX_PATH];
+    btrfs_device* devices;
+    BOOL readonly;
+    BOOL called_from_RemoveDevice, called_from_ShrinkDevice;
+};

Modification de propriétés sur dll/shellext/shellbtrfs/balance.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/contextmenu.cpp
===================================================================
--- dll/shellext/shellbtrfs/contextmenu.cpp	(révision 75772)
+++ dll/shellext/shellbtrfs/contextmenu.cpp	(copie de travail)
@@ -1,22 +1,28 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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/>. */
 
 #ifndef __REACTOS__
+#define UNICODE
+#endif
+#include "shellext.h"
+#ifndef __REACTOS__
 #include <windows.h>
+#include <strsafe.h>
+#include <stddef.h>
 #include <winternl.h>
 #else
 #define WIN32_NO_STATUS
@@ -23,8 +29,14 @@
 #include <windef.h>
 #include <winbase.h>
 #include <strsafe.h>
+#include <shellapi.h>
+#include <winioctl.h>
 #include <ndk/iofuncs.h>
+#undef DeleteFile
 #endif
+#include <wincodec.h>
+#include <string>
+#include <sstream>
 
 #define NO_SHLWAPI_STRFCNS
 #include <shlwapi.h>
@@ -34,7 +46,7 @@
 #ifndef __REACTOS__
 #include "../btrfsioctl.h"
 #else
-#include "../../drivers/filesystems/btrfs/btrfsioctl.h"
+#include "btrfsioctl.h"
 #endif
 
 #define NEW_SUBVOL_VERBA "newsubvol"
@@ -41,38 +53,28 @@
 #define NEW_SUBVOL_VERBW L"newsubvol"
 #define SNAPSHOT_VERBA "snapshot"
 #define SNAPSHOT_VERBW L"snapshot"
+#define REFLINK_VERBA "reflink"
+#define REFLINK_VERBW L"reflink"
+#define RECV_VERBA "recvsubvol"
+#define RECV_VERBW L"recvsubvol"
+#define SEND_VERBA "sendsubvol"
+#define SEND_VERBW L"sendsubvol"
 
-#ifndef __REACTOS__
-// FIXME - is there a way to link to the proper header files without breaking everything?
-#ifdef __cplusplus
-extern "C" {
-#endif
-NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
-#ifdef __cplusplus
-}
-#endif
+typedef struct {
+    ULONG  ReparseTag;
+    USHORT ReparseDataLength;
+    USHORT Reserved;
+} reparse_header;
 
-#define STATUS_SUCCESS          (NTSTATUS)0x00000000
-#endif
-
-typedef struct _KEY_NAME_INFORMATION {
-    ULONG NameLength;
-    WCHAR Name[1];
-} KEY_NAME_INFORMATION;
-
-typedef ULONG (WINAPI *_RtlNtStatusToDosError)(NTSTATUS Status);
-
-extern HMODULE module;
-
 // FIXME - don't assume subvol's top inode is 0x100
 
 HRESULT __stdcall BtrfsContextMenu::QueryInterface(REFIID riid, void **ppObj) {
     if (riid == IID_IUnknown || riid == IID_IContextMenu) {
-        *ppObj = static_cast<IContextMenu*>(this); 
+        *ppObj = static_cast<IContextMenu*>(this);
         AddRef();
         return S_OK;
     } else if (riid == IID_IShellExtInit) {
-        *ppObj = static_cast<IShellExtInit*>(this); 
+        *ppObj = static_cast<IShellExtInit*>(this);
         AddRef();
         return S_OK;
     }
@@ -86,160 +88,339 @@
     IO_STATUS_BLOCK iosb;
     btrfs_get_file_ids bgfi;
     NTSTATUS Status;
-    
+
     if (!pidlFolder) {
         FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
         UINT num_files, i;
         WCHAR fn[MAX_PATH];
         HDROP hdrop;
-        
+
         if (!pdtobj)
             return E_FAIL;
-        
+
         stgm.tymed = TYMED_HGLOBAL;
-        
+
         if (FAILED(pdtobj->GetData(&format, &stgm)))
             return E_INVALIDARG;
-        
+
         stgm_set = TRUE;
-        
+
         hdrop = (HDROP)GlobalLock(stgm.hGlobal);
-        
+
         if (!hdrop) {
             ReleaseStgMedium(&stgm);
             stgm_set = FALSE;
             return E_INVALIDARG;
         }
-         
+
         num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
-        
+
         for (i = 0; i < num_files; i++) {
-            if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+            if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
                 h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-    
+
                 if (h != INVALID_HANDLE_VALUE) {
                     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
-                        
-                    if (Status == STATUS_SUCCESS && bgfi.inode == 0x100 && !bgfi.top) {
+
+                    if (NT_SUCCESS(Status) && bgfi.inode == 0x100 && !bgfi.top) {
                         WCHAR parpath[MAX_PATH];
                         HANDLE h2;
-                        
+
                         StringCchCopyW(parpath, sizeof(parpath) / sizeof(WCHAR), fn);
-                        
+
                         PathRemoveFileSpecW(parpath);
-                        
+
                         h2 = CreateFileW(parpath, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-    
-                        if (h2 == INVALID_HANDLE_VALUE) {
-                            CloseHandle(h);
-                            return E_FAIL;
-                        }
-                        
+
+                        if (h2 != INVALID_HANDLE_VALUE)
+                            allow_snapshot = TRUE;
+
+                        CloseHandle(h2);
+
                         ignore = FALSE;
                         bg = FALSE;
-                        
-                        CloseHandle(h2);
+
                         CloseHandle(h);
+                        GlobalUnlock(hdrop);
                         return S_OK;
                     }
-                    
+
                     CloseHandle(h);
                 }
             }
         }
-        
+
+        GlobalUnlock(hdrop);
+
         return S_OK;
     }
 
     if (!SHGetPathFromIDListW(pidlFolder, path))
         return E_FAIL;
-    
+
     // check we have permissions to create new subdirectory
-    
+
     h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-    
+
     if (h == INVALID_HANDLE_VALUE)
         return E_FAIL;
-    
+
     // check is Btrfs volume
-    
+
     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
-    
-    if (Status != STATUS_SUCCESS) {
+
+    if (!NT_SUCCESS(Status)) {
         CloseHandle(h);
         return E_FAIL;
     }
-    
+
     CloseHandle(h);
-    
+
     ignore = FALSE;
     bg = TRUE;
-    
+
     return S_OK;
 }
 
+static BOOL get_volume_path_parent(const WCHAR* fn, WCHAR* volpath, ULONG volpathlen) {
+    WCHAR *f, *p;
+    BOOL b;
+
+    f = PathFindFileNameW(fn);
+
+    if (f == fn)
+        return GetVolumePathNameW(fn, volpath, volpathlen);
+
+    p = (WCHAR*)malloc((f - fn + 1) * sizeof(WCHAR));
+    memcpy(p, fn, (f - fn) * sizeof(WCHAR));
+    p[f - fn] = 0;
+
+    b = GetVolumePathNameW(p, volpath, volpathlen);
+
+    free(p);
+
+    return b;
+}
+
+static BOOL show_reflink_paste(WCHAR* path) {
+    HDROP hdrop;
+    HANDLE lh;
+    ULONG num_files;
+    WCHAR fn[MAX_PATH], volpath1[255], volpath2[255];
+
+    if (!IsClipboardFormatAvailable(CF_HDROP))
+        return FALSE;
+
+    if (!GetVolumePathNameW(path, volpath1, sizeof(volpath1) / sizeof(WCHAR)))
+        return FALSE;
+
+    if (!OpenClipboard(NULL))
+        return FALSE;
+
+    hdrop = (HDROP)GetClipboardData(CF_HDROP);
+
+    if (!hdrop) {
+        CloseClipboard();
+        return FALSE;
+    }
+
+    lh = GlobalLock(hdrop);
+
+    if (!lh) {
+        CloseClipboard();
+        return FALSE;
+    }
+
+    num_files = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
+
+    if (num_files == 0) {
+        GlobalUnlock(lh);
+        CloseClipboard();
+        return FALSE;
+    }
+
+    if (!DragQueryFileW(hdrop, 0, fn, sizeof(fn) / sizeof(WCHAR))) {
+        GlobalUnlock(lh);
+        CloseClipboard();
+        return FALSE;
+    }
+
+    if (!get_volume_path_parent(fn, volpath2, sizeof(volpath2) / sizeof(WCHAR))) {
+        GlobalUnlock(lh);
+        CloseClipboard();
+        return FALSE;
+    }
+
+    GlobalUnlock(lh);
+
+    CloseClipboard();
+
+    return !wcscmp(volpath1, volpath2);
+}
+
+// The code for putting an icon against a menu item comes from:
+// http://web.archive.org/web/20070208005514/http://shellrevealed.com/blogs/shellblog/archive/2007/02/06/Vista-Style-Menus_2C00_-Part-1-_2D00_-Adding-icons-to-standard-menus.aspx
+
+static void InitBitmapInfo(BITMAPINFO* pbmi, ULONG cbInfo, LONG cx, LONG cy, WORD bpp) {
+    ZeroMemory(pbmi, cbInfo);
+    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    pbmi->bmiHeader.biPlanes = 1;
+    pbmi->bmiHeader.biCompression = BI_RGB;
+
+    pbmi->bmiHeader.biWidth = cx;
+    pbmi->bmiHeader.biHeight = cy;
+    pbmi->bmiHeader.biBitCount = bpp;
+}
+
+static HRESULT Create32BitHBITMAP(HDC hdc, const SIZE *psize, void **ppvBits, HBITMAP* phBmp) {
+    BITMAPINFO bmi;
+    HDC hdcUsed;
+
+    *phBmp = NULL;
+
+    InitBitmapInfo(&bmi, sizeof(bmi), psize->cx, psize->cy, 32);
+
+    hdcUsed = hdc ? hdc : GetDC(NULL);
+
+    if (hdcUsed) {
+        *phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
+        if (hdc != hdcUsed)
+            ReleaseDC(NULL, hdcUsed);
+    }
+
+    return !*phBmp ? E_OUTOFMEMORY : S_OK;
+}
+
+void BtrfsContextMenu::get_uac_icon() {
+    IWICImagingFactory* factory = NULL;
+    IWICBitmap* bitmap;
+    HRESULT hr;
+
+#ifdef __REACTOS__
+    hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (void **)&factory);
+#else
+    hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
+#endif
+
+    if (SUCCEEDED(hr)) {
+        HANDLE icon;
+
+        // We can't use IDI_SHIELD, as that will only give us the full-size icon
+        icon = LoadImageW(GetModuleHandleW(L"user32.dll"), MAKEINTRESOURCEW(106)/* UAC shield */, IMAGE_ICON,
+                          GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
+
+        hr = factory->CreateBitmapFromHICON((HICON)icon, &bitmap);
+        if (SUCCEEDED(hr)) {
+            UINT cx, cy;
+
+            hr = bitmap->GetSize(&cx, &cy);
+            if (SUCCEEDED(hr)) {
+                SIZE sz;
+                BYTE* buf;
+
+                sz.cx = (int)cx;
+                sz.cy = -(int)cy;
+
+                hr = Create32BitHBITMAP(NULL, &sz, (void**)&buf, &uacicon);
+                if (SUCCEEDED(hr)) {
+                    UINT stride = cx * sizeof(DWORD);
+                    UINT buflen = cy * stride;
+                    bitmap->CopyPixels(NULL, stride, buflen, buf);
+                }
+            }
+
+            bitmap->Release();
+        }
+
+        factory->Release();
+    }
+}
+
 HRESULT __stdcall BtrfsContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) {
     WCHAR str[256];
-    
+    ULONG entries = 0;
+
     if (ignore)
         return E_INVALIDARG;
-    
+
     if (uFlags & CMF_DEFAULTONLY)
         return S_OK;
-    
+
     if (!bg) {
-        if (LoadStringW(module, IDS_CREATE_SNAPSHOT, str, sizeof(str) / sizeof(WCHAR)) == 0)
+        if (allow_snapshot) {
+            if (LoadStringW(module, IDS_CREATE_SNAPSHOT, str, sizeof(str) / sizeof(WCHAR)) == 0)
+                return E_FAIL;
+
+            if (!InsertMenuW(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, str))
+                return E_FAIL;
+
+            entries = 1;
+        }
+
+        if (idCmdFirst + entries <= idCmdLast) {
+            MENUITEMINFOW mii;
+
+            if (LoadStringW(module, IDS_SEND_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0)
+                return E_FAIL;
+
+            if (!uacicon)
+                get_uac_icon();
+
+            memset(&mii, 0, sizeof(MENUITEMINFOW));
+            mii.cbSize = sizeof(MENUITEMINFOW);
+            mii.fMask = MIIM_STRING | MIIM_ID | MIIM_BITMAP;
+            mii.dwTypeData = str;
+            mii.wID = idCmdFirst + entries;
+            mii.hbmpItem = uacicon;
+
+            if (!InsertMenuItemW(hmenu, indexMenu + entries, TRUE, &mii))
+                return E_FAIL;
+
+            entries++;
+        }
+    } else {
+        if (LoadStringW(module, IDS_NEW_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0)
             return E_FAIL;
 
         if (!InsertMenuW(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, str))
             return E_FAIL;
 
-        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
-    }
-    
-    if (LoadStringW(module, IDS_NEW_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0)
-        return E_FAIL;
+        entries = 1;
 
-    if (!InsertMenuW(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, str))
-        return E_FAIL;
+        if (idCmdFirst + 1 <= idCmdLast) {
+            MENUITEMINFOW mii;
 
-    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
-}
+            if (LoadStringW(module, IDS_RECV_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0)
+                return E_FAIL;
 
-static void ShowError(HWND hwnd, ULONG err) {
-    WCHAR* buf;
-    
-    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
-                       err, 0, (WCHAR*)&buf, 0, NULL) == 0) {
-        MessageBoxW(hwnd, L"FormatMessage failed", L"Error", MB_ICONERROR);
-        return;
+            if (!uacicon)
+                get_uac_icon();
+
+            memset(&mii, 0, sizeof(MENUITEMINFOW));
+            mii.cbSize = sizeof(MENUITEMINFOW);
+            mii.fMask = MIIM_STRING | MIIM_ID | MIIM_BITMAP;
+            mii.dwTypeData = str;
+            mii.wID = idCmdFirst + 1;
+            mii.hbmpItem = uacicon;
+
+            if (!InsertMenuItemW(hmenu, indexMenu + 1, TRUE, &mii))
+                return E_FAIL;
+
+            entries++;
+        }
+
+        if (idCmdFirst + 2 <= idCmdLast && show_reflink_paste(path)) {
+            if (LoadStringW(module, IDS_REFLINK_PASTE, str, sizeof(str) / sizeof(WCHAR)) == 0)
+                return E_FAIL;
+
+            if (!InsertMenuW(hmenu, indexMenu + 2, MF_BYPOSITION, idCmdFirst + 2, str))
+                return E_FAIL;
+
+            entries++;
+        }
     }
-    
-    MessageBoxW(hwnd, buf, L"Error", MB_ICONERROR);
-    
-    LocalFree(buf);
-}
 
-void ShowNtStatusError(HWND hwnd, NTSTATUS Status) {
-    _RtlNtStatusToDosError RtlNtStatusToDosError;
-    HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
-    
-    if (!ntdll) {
-        MessageBoxW(hwnd, L"Error loading ntdll.dll", L"Error", MB_ICONERROR);
-        return;
-    }
-    
-    RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(ntdll, "RtlNtStatusToDosError");
-    
-    if (!ntdll) {
-        MessageBoxW(hwnd, L"Error loading RtlNtStatusToDosError in ntdll.dll", L"Error", MB_ICONERROR);
-        FreeLibrary(ntdll);
-        return;
-    }
-    
-    ShowError(hwnd, RtlNtStatusToDosError(Status));
-    
-    FreeLibrary(ntdll);
+    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, entries);
 }
 
 static void create_snapshot(HWND hwnd, WCHAR* fn) {
@@ -247,13 +428,13 @@
     NTSTATUS Status;
     IO_STATUS_BLOCK iosb;
     btrfs_get_file_ids bgfi;
-    
+
     h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-    
+
     if (h != INVALID_HANDLE_VALUE) {
         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
-            
-        if (Status == STATUS_SUCCESS && bgfi.inode == 0x100 && !bgfi.top) {
+
+        if (NT_SUCCESS(Status) && bgfi.inode == 0x100 && !bgfi.top) {
             WCHAR parpath[MAX_PATH], subvolname[MAX_PATH], templ[MAX_PATH], name[MAX_PATH], searchpath[MAX_PATH];
             HANDLE h2, fff;
             btrfs_create_snapshot* bcs;
@@ -260,13 +441,13 @@
             ULONG namelen, pathend;
             WIN32_FIND_DATAW wfd;
             SYSTEMTIME time;
-            
+
             StringCchCopyW(parpath, sizeof(parpath) / sizeof(WCHAR), fn);
             PathRemoveFileSpecW(parpath);
-            
+
             StringCchCopyW(subvolname, sizeof(subvolname) / sizeof(WCHAR), fn);
             PathStripPathW(subvolname);
-            
+
             h2 = CreateFileW(parpath, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 
             if (h2 == INVALID_HANDLE_VALUE) {
@@ -274,7 +455,7 @@
                 CloseHandle(h);
                 return;
             }
-            
+
             if (!LoadStringW(module, IDS_SNAPSHOT_FILENAME, templ, MAX_PATH)) {
                 ShowError(hwnd, GetLastError());
                 CloseHandle(h);
@@ -281,9 +462,9 @@
                 CloseHandle(h2);
                 return;
             }
-            
+
             GetLocalTime(&time);
-            
+
             if (StringCchPrintfW(name, sizeof(name) / sizeof(WCHAR), templ, subvolname, time.wYear, time.wMonth, time.wDay) == STRSAFE_E_INSUFFICIENT_BUFFER) {
                 MessageBoxW(hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
                 CloseHandle(h);
@@ -290,21 +471,21 @@
                 CloseHandle(h2);
                 return;
             }
-            
+
             StringCchCopyW(searchpath, sizeof(searchpath) / sizeof(WCHAR), parpath);
             StringCchCatW(searchpath, sizeof(searchpath) / sizeof(WCHAR), L"\\");
             pathend = wcslen(searchpath);
-            
+
             StringCchCatW(searchpath, sizeof(searchpath) / sizeof(WCHAR), name);
-            
+
             fff = FindFirstFileW(searchpath, &wfd);
-            
+
             if (fff != INVALID_HANDLE_VALUE) {
                 ULONG i = wcslen(searchpath), num = 2;
-                
+
                 do {
                     FindClose(fff);
-                    
+
                     searchpath[i] = 0;
                     if (StringCchPrintfW(searchpath, sizeof(searchpath) / sizeof(WCHAR), L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) {
                         MessageBoxW(hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
@@ -317,123 +498,739 @@
                     num++;
                 } while (fff != INVALID_HANDLE_VALUE);
             }
-            
+
             namelen = wcslen(&searchpath[pathend]) * sizeof(WCHAR);
-                        
+
             bcs = (btrfs_create_snapshot*)malloc(sizeof(btrfs_create_snapshot) - 1 + namelen);
+            bcs->readonly = FALSE;
+            bcs->posix = FALSE;
             bcs->subvol = h;
             bcs->namelen = namelen;
             memcpy(bcs->name, &searchpath[pathend], namelen);
-            
-            Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, NULL, 0, bcs, sizeof(btrfs_create_snapshot) - 1 + namelen);
-        
-            if (Status != STATUS_SUCCESS)
+
+            Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, sizeof(btrfs_create_snapshot) - 1 + namelen, NULL, 0);
+
+            if (!NT_SUCCESS(Status))
                 ShowNtStatusError(hwnd, Status);
-            
+
             CloseHandle(h2);
         }
-        
+
         CloseHandle(h);
     } else
         ShowError(hwnd, GetLastError());
 }
 
-HRESULT __stdcall BtrfsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) {
+static UINT64 __inline sector_align(UINT64 n, UINT64 a) {
+    if (n & (a - 1))
+        n = (n + a) & ~(a - 1);
+
+    return n;
+}
+
+BOOL BtrfsContextMenu::reflink_copy(HWND hwnd, const WCHAR* fn, const WCHAR* dir) {
+    HANDLE source, dest;
+    WCHAR* name, volpath1[255], volpath2[255];
+    std::wstring dirw, newpath;
+    BOOL ret = FALSE;
+    FILE_BASIC_INFO fbi;
+    FILETIME atime, mtime;
+    btrfs_inode_info bii;
+    btrfs_set_inode_info bsii;
+    ULONG bytesret;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_set_xattr bsxa;
+
+    // Thanks to 0xbadfca11, whose version of reflink for Windows provided a few pointers on what
+    // to do here - https://github.com/0xbadfca11/reflink
+
+    name = PathFindFileNameW(fn);
+
+    dirw = dir;
+
+    if (dir[0] != 0 && dir[wcslen(dir) - 1] != '\\')
+        dirw += L"\\";
+
+    newpath = dirw;
+    newpath += name;
+
+    if (!get_volume_path_parent(fn, volpath1, sizeof(volpath1) / sizeof(WCHAR))) {
+        ShowError(hwnd, GetLastError());
+        return FALSE;
+    }
+
+    if (!GetVolumePathNameW(dir, volpath2, sizeof(volpath2) / sizeof(WCHAR))) {
+        ShowError(hwnd, GetLastError());
+        return FALSE;
+    }
+
+    if (wcscmp(volpath1, volpath2)) // different filesystems
+        return FALSE;
+
+    source = CreateFileW(fn, GENERIC_READ | FILE_TRAVERSE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);
+    if (source == INVALID_HANDLE_VALUE) {
+        ShowError(hwnd, GetLastError());
+        return FALSE;
+    }
+
+    Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii, sizeof(btrfs_inode_info));
+    if (!NT_SUCCESS(Status)) {
+        ShowNtStatusError(hwnd, Status);
+        CloseHandle(source);
+        return FALSE;
+    }
+
+    // if subvol, do snapshot instead
+    if (bii.inode == SUBVOL_ROOT_INODE) {
+        btrfs_create_snapshot* bcs;
+        HANDLE dirh, fff;
+        std::wstring destname, search;
+        WIN32_FIND_DATAW wfd;
+        int num = 2;
+
+        dirh = CreateFileW(dir, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (dirh == INVALID_HANDLE_VALUE) {
+            ShowError(hwnd, GetLastError());
+            CloseHandle(source);
+            return FALSE;
+        }
+
+        search = dirw;
+        search += name;
+        destname = name;
+
+        fff = FindFirstFileW(search.c_str(), &wfd);
+
+        if (fff != INVALID_HANDLE_VALUE) {
+            do {
+                std::wstringstream ss;
+
+                FindClose(fff);
+
+                ss << name;
+                ss << L" (";
+                ss << num;
+                ss << L")";
+                destname = ss.str();
+
+                search = dirw + destname;
+
+                fff = FindFirstFileW(search.c_str(), &wfd);
+                num++;
+            } while (fff != INVALID_HANDLE_VALUE);
+        }
+
+        bcs = (btrfs_create_snapshot*)malloc(sizeof(btrfs_create_snapshot) - sizeof(WCHAR) + (destname.length() * sizeof(WCHAR)));
+        bcs->subvol = source;
+        bcs->namelen = destname.length() * sizeof(WCHAR);
+        memcpy(bcs->name, destname.c_str(), destname.length() * sizeof(WCHAR));
+
+        Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, sizeof(btrfs_create_snapshot) - sizeof(WCHAR) + bcs->namelen, NULL, 0);
+
+        free(bcs);
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwnd, Status);
+            CloseHandle(source);
+            CloseHandle(dirh);
+            return FALSE;
+        }
+
+        CloseHandle(source);
+        CloseHandle(dirh);
+        return TRUE;
+    }
+
+    if (!GetFileInformationByHandleEx(source, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) {
+        ShowError(hwnd, GetLastError());
+        CloseHandle(source);
+        return FALSE;
+    }
+
+    if (bii.type == BTRFS_TYPE_CHARDEV || bii.type == BTRFS_TYPE_BLOCKDEV || bii.type == BTRFS_TYPE_FIFO || bii.type == BTRFS_TYPE_SOCKET) {
+        HANDLE dirh;
+        ULONG bmnsize;
+        btrfs_mknod* bmn;
+
+        dirh = CreateFileW(dir, FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (dirh == INVALID_HANDLE_VALUE) {
+            ShowError(hwnd, GetLastError());
+            CloseHandle(source);
+            return FALSE;
+        }
+
+        bmnsize = offsetof(btrfs_mknod, name[0]) + (wcslen(name) * sizeof(WCHAR));
+        bmn = (btrfs_mknod*)malloc(bmnsize);
+
+        bmn->inode = 0;
+        bmn->type = bii.type;
+        bmn->st_rdev = bii.st_rdev;
+        bmn->namelen = wcslen(name) * sizeof(WCHAR);
+        memcpy(bmn->name, name, bmn->namelen);
+
+        Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_MKNOD, bmn, bmnsize, NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwnd, Status);
+            CloseHandle(dirh);
+            CloseHandle(source);
+            free(bmn);
+            return FALSE;
+        }
+
+        CloseHandle(dirh);
+        free(bmn);
+
+        dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, 0, NULL);
+    } else if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (CreateDirectoryExW(fn, newpath.c_str(), NULL))
+            dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                               NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        else
+            dest = INVALID_HANDLE_VALUE;
+    } else
+        dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, source);
+
+    if (dest == INVALID_HANDLE_VALUE) {
+        int num = 2;
+
+        if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_ALREADY_EXISTS && wcscmp(fn, newpath.c_str())) {
+            ShowError(hwnd, GetLastError());
+            CloseHandle(source);
+            return FALSE;
+        }
+
+        do {
+            WCHAR* ext;
+            std::wstringstream ss;
+
+            ext = PathFindExtensionW(fn);
+
+            ss << dirw;
+
+            if (*ext == 0) {
+                ss << name;
+                ss << L" (";
+                ss << num;
+                ss << L")";
+            } else {
+                std::wstring namew = name;
+
+                ss << namew.substr(0, ext - name);
+                ss << L" (";
+                ss << num;
+                ss << L")";
+                ss << ext;
+            }
+
+            newpath = ss.str();
+            if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+                if (CreateDirectoryExW(fn, newpath.c_str(), NULL))
+                    dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+                else
+                    dest = INVALID_HANDLE_VALUE;
+            } else
+                dest = CreateFileW(newpath.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, source);
+
+            if (dest == INVALID_HANDLE_VALUE) {
+                if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_ALREADY_EXISTS) {
+                    ShowError(hwnd, GetLastError());
+                    CloseHandle(source);
+                    return FALSE;
+                }
+
+                num++;
+            } else
+                break;
+        } while (TRUE);
+    }
+
+    memset(&bsii, 0, sizeof(btrfs_set_inode_info));
+
+    bsii.flags_changed = TRUE;
+    bsii.flags = bii.flags;
+
+    if (bii.flags & BTRFS_INODE_COMPRESS) {
+        bsii.compression_type_changed = TRUE;
+        bsii.compression_type = bii.compression_type;
+    }
+
+    Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowNtStatusError(hwnd, Status);
+        goto end;
+    }
+
+    if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (!(fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+            HANDLE h;
+            WIN32_FIND_DATAW fff;
+            std::wstring qs;
+
+            qs = fn;
+            qs += L"\\*";
+
+            h = FindFirstFileW(qs.c_str(), &fff);
+            if (h != INVALID_HANDLE_VALUE) {
+                do {
+                    std::wstring fn2;
+
+                    if (fff.cFileName[0] == '.' && (fff.cFileName[1] == 0 || (fff.cFileName[1] == '.' && fff.cFileName[2] == 0)))
+                        continue;
+
+                    fn2 = fn;
+                    fn2 += L"\\";
+                    fn2 += fff.cFileName;
+
+                    if (!reflink_copy(hwnd, fn2.c_str(), newpath.c_str()))
+                        goto end;
+                } while (FindNextFileW(h, &fff));
+
+                FindClose(h);
+            }
+        }
+
+        // CreateDirectoryExW also copies streams, no need to do it here
+    } else {
+        HANDLE h;
+        WIN32_FIND_STREAM_DATA fsd;
+
+        if (fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+            reparse_header rh;
+            ULONG rplen;
+            UINT8* rp;
+
+            if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, &rh, sizeof(reparse_header), &bytesret, NULL)) {
+                if (GetLastError() != ERROR_MORE_DATA) {
+                    ShowError(hwnd, GetLastError());
+                    goto end;
+                }
+            }
+
+            rplen = sizeof(reparse_header) + rh.ReparseDataLength;
+            rp = (UINT8*)malloc(rplen);
+
+            if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, rp, rplen, &bytesret, NULL)) {
+                ShowError(hwnd, GetLastError());
+                goto end;
+            }
+
+            if (!DeviceIoControl(dest, FSCTL_SET_REPARSE_POINT, rp, rplen, NULL, 0, &bytesret, NULL)) {
+                ShowError(hwnd, GetLastError());
+                goto end;
+            }
+
+            free(rp);
+        } else {
+            FILE_STANDARD_INFO fsi;
+            FILE_END_OF_FILE_INFO feofi;
+            FSCTL_GET_INTEGRITY_INFORMATION_BUFFER fgiib;
+            FSCTL_SET_INTEGRITY_INFORMATION_BUFFER fsiib;
+            DUPLICATE_EXTENTS_DATA ded;
+            UINT64 offset, alloc_size;
+            ULONG maxdup;
+
+            if (!GetFileInformationByHandleEx(source, FileStandardInfo, &fsi, sizeof(FILE_STANDARD_INFO))) {
+                ShowError(hwnd, GetLastError());
+                goto end;
+            }
+
+            if (!DeviceIoControl(source, FSCTL_GET_INTEGRITY_INFORMATION, NULL, 0, &fgiib, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER), &bytesret, NULL)) {
+                ShowError(hwnd, GetLastError());
+                goto end;
+            }
+
+            if (fbi.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) {
+                if (!DeviceIoControl(dest, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesret, NULL)) {
+                    ShowError(hwnd, GetLastError());
+                    goto end;
+                }
+            }
+
+            fsiib.ChecksumAlgorithm = fgiib.ChecksumAlgorithm;
+            fsiib.Reserved = 0;
+            fsiib.Flags = fgiib.Flags;
+            if (!DeviceIoControl(dest, FSCTL_SET_INTEGRITY_INFORMATION, &fsiib, sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER), NULL, 0, &bytesret, NULL)) {
+                ShowError(hwnd, GetLastError());
+                goto end;
+            }
+
+            feofi.EndOfFile = fsi.EndOfFile;
+            if (!SetFileInformationByHandle(dest, FileEndOfFileInfo, &feofi, sizeof(FILE_END_OF_FILE_INFO))){
+                ShowError(hwnd, GetLastError());
+                goto end;
+            }
+
+            ded.FileHandle = source;
+            maxdup = 0xffffffff - fgiib.ClusterSizeInBytes + 1;
+
+            alloc_size = sector_align(fsi.EndOfFile.QuadPart, fgiib.ClusterSizeInBytes);
+
+            offset = 0;
+            while (offset < alloc_size) {
+                ded.SourceFileOffset.QuadPart = ded.TargetFileOffset.QuadPart = offset;
+                ded.ByteCount.QuadPart = maxdup < (alloc_size - offset) ? maxdup : (alloc_size - offset);
+                if (!DeviceIoControl(dest, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA), NULL, 0, &bytesret, NULL)) {
+                    ShowError(hwnd, GetLastError());
+                    goto end;
+                }
+
+                offset += ded.ByteCount.QuadPart;
+            }
+        }
+
+        h = FindFirstStreamW(fn, FindStreamInfoStandard, &fsd, 0);
+        if (h != INVALID_HANDLE_VALUE) {
+            do {
+                std::wstring sn;
+
+                sn = fsd.cStreamName;
+
+                if (sn != L"::$DATA" && sn.length() > 6 && sn.substr(sn.length() - 6, 6) == L":$DATA") {
+                    HANDLE stream;
+                    UINT8* data = NULL;
+
+                    if (fsd.StreamSize.QuadPart > 0) {
+                        std::wstring fn2;
+
+                        fn2 = fn;
+                        fn2 += sn;
+
+                        stream = CreateFileW(fn2.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+                        if (stream == INVALID_HANDLE_VALUE) {
+                            ShowError(hwnd, GetLastError());
+                            goto end;
+                        }
+
+                        // We can get away with this because our streams are guaranteed to be below 64 KB -
+                        // don't do this on NTFS!
+                        data = (UINT8*)malloc(fsd.StreamSize.QuadPart);
+
+                        if (!ReadFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
+                            ShowError(hwnd, GetLastError());
+                            free(data);
+                            CloseHandle(stream);
+                            goto end;
+                        }
+
+                        CloseHandle(stream);
+                    }
+
+                    stream = CreateFileW((newpath + sn).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, NULL);
+
+                    if (stream == INVALID_HANDLE_VALUE) {
+                        ShowError(hwnd, GetLastError());
+
+                        if (data) free(data);
+
+                        goto end;
+                    }
+
+                    if (data) {
+                        if (!WriteFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
+                            ShowError(hwnd, GetLastError());
+                            free(data);
+                            CloseHandle(stream);
+                            goto end;
+                        }
+
+                        free(data);
+                    }
+
+                    CloseHandle(stream);
+                }
+            } while (FindNextStreamW(h, &fsd));
+
+            FindClose(h);
+        }
+    }
+
+    atime.dwLowDateTime = fbi.LastAccessTime.LowPart;
+    atime.dwHighDateTime = fbi.LastAccessTime.HighPart;
+    mtime.dwLowDateTime = fbi.LastWriteTime.LowPart;
+    mtime.dwHighDateTime = fbi.LastWriteTime.HighPart;
+    SetFileTime(dest, NULL, &atime, &mtime);
+
+    Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, &bsxa, sizeof(btrfs_set_xattr));
+
+    if (Status == STATUS_BUFFER_OVERFLOW || (NT_SUCCESS(Status) && bsxa.valuelen > 0)) {
+        ULONG xalen = 0;
+        btrfs_set_xattr *xa = NULL, *xa2;
+
+        do {
+            xalen += 1024;
+
+            if (xa) free(xa);
+            xa = (btrfs_set_xattr*)malloc(xalen);
+
+            Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, xa, xalen);
+        } while (Status == STATUS_BUFFER_OVERFLOW);
+
+        if (!NT_SUCCESS(Status)) {
+            free(xa);
+            ShowNtStatusError(hwnd, Status);
+            goto end;
+        }
+
+        xa2 = xa;
+        while (xa2->valuelen > 0) {
+            Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, xa2,
+                                     offsetof(btrfs_set_xattr, data[0]) + xa2->namelen + xa2->valuelen, NULL, 0);
+            if (!NT_SUCCESS(Status)) {
+                free(xa);
+                ShowNtStatusError(hwnd, Status);
+                goto end;
+            }
+            xa2 = (btrfs_set_xattr*)&xa2->data[xa2->namelen + xa2->valuelen];
+        }
+
+        free(xa);
+    } else if (!NT_SUCCESS(Status)) {
+        ShowNtStatusError(hwnd, Status);
+        goto end;
+    }
+
+    ret = TRUE;
+
+end:
+    if (!ret) {
+        FILE_DISPOSITION_INFO fdi;
+
+        fdi.DeleteFile = TRUE;
+        if (!SetFileInformationByHandle(dest, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO)))
+            ShowError(hwnd, GetLastError());
+    }
+
+    CloseHandle(dest);
+    CloseHandle(source);
+
+    return ret;
+}
+
+HRESULT __stdcall BtrfsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO picia) {
+    LPCMINVOKECOMMANDINFOEX pici = (LPCMINVOKECOMMANDINFOEX)picia;
+
     if (ignore)
         return E_INVALIDARG;
-    
+
     if (!bg) {
-        if ((IS_INTRESOURCE(pici->lpVerb) && pici->lpVerb == 0) || !strcmp(pici->lpVerb, SNAPSHOT_VERBA)) {
+        if ((IS_INTRESOURCE(pici->lpVerb) && allow_snapshot && pici->lpVerb == 0) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, SNAPSHOT_VERBA))) {
             UINT num_files, i;
             WCHAR fn[MAX_PATH];
-            
+
             if (!stgm_set)
                 return E_FAIL;
-            
+
             num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
-            
+
             if (num_files == 0)
                 return E_FAIL;
-            
+
             for (i = 0; i < num_files; i++) {
-                if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+                if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
                     create_snapshot(pici->hwnd, fn);
                 }
             }
 
             return S_OK;
-        }
-        
-        return E_FAIL;
-    }
-    
-    if ((IS_INTRESOURCE(pici->lpVerb) && pici->lpVerb == 0) || !strcmp(pici->lpVerb, NEW_SUBVOL_VERBA)) {
-        HANDLE h;
-        IO_STATUS_BLOCK iosb;
-        NTSTATUS Status;
-        ULONG pathlen, searchpathlen, pathend;
-        WCHAR name[MAX_PATH], *searchpath;
-        HANDLE fff;
-        WIN32_FIND_DATAW wfd;
-        
-        if (!LoadStringW(module, IDS_NEW_SUBVOL_FILENAME, name, MAX_PATH)) {
-            ShowError(pici->hwnd, GetLastError());
-            return E_FAIL;
-        }
-        
-        h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-    
-        if (h == INVALID_HANDLE_VALUE) {
-            ShowError(pici->hwnd, GetLastError());
-            return E_FAIL;
-        }
-        
-        pathlen = wcslen(path);
-        
-        searchpathlen = pathlen + wcslen(name) + 10;
-        searchpath = (WCHAR*)malloc(searchpathlen * sizeof(WCHAR));
-        
-        StringCchCopyW(searchpath, searchpathlen, path);
-        StringCchCatW(searchpath, searchpathlen, L"\\");
-        pathend = wcslen(searchpath);
-        
-        StringCchCatW(searchpath, searchpathlen, name);
-        
-        fff = FindFirstFileW(searchpath, &wfd);
-        
-        if (fff != INVALID_HANDLE_VALUE) {
-            ULONG i = wcslen(searchpath), num = 2;
-            
-            do {
-                FindClose(fff);
-                
-                searchpath[i] = 0;
-                if (StringCchPrintfW(searchpath, searchpathlen, L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) {
-                    MessageBoxW(pici->hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
-                    CloseHandle(h);
-                    return E_FAIL;
+        } else if ((IS_INTRESOURCE(pici->lpVerb) && ((allow_snapshot && (ULONG_PTR)pici->lpVerb == 1) || (!allow_snapshot && (ULONG_PTR)pici->lpVerb == 0))) ||
+                   (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, SEND_VERBA))) {
+            UINT num_files, i;
+            WCHAR dll[MAX_PATH], fn[MAX_PATH];
+            std::wstring t;
+            SHELLEXECUTEINFOW sei;
+
+            GetModuleFileNameW(module, dll, sizeof(dll) / sizeof(WCHAR));
+
+            if (!stgm_set)
+                return E_FAIL;
+
+            num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+
+            if (num_files == 0)
+                return E_FAIL;
+
+            for (i = 0; i < num_files; i++) {
+                if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(WCHAR))) {
+                    t = L"\"";
+                    t += dll;
+                    t += L"\",SendSubvolGUI ";
+                    t += fn;
+
+                    RtlZeroMemory(&sei, sizeof(sei));
+
+                    sei.cbSize = sizeof(sei);
+                    sei.hwnd = pici->hwnd;
+                    sei.lpVerb = L"runas";
+                    sei.lpFile = L"rundll32.exe";
+                    sei.lpParameters = t.c_str();
+                    sei.nShow = SW_SHOW;
+                    sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+                    if (!ShellExecuteExW(&sei)) {
+                        ShowError(pici->hwnd, GetLastError());
+                        return E_FAIL;
+                    }
+
+                    WaitForSingleObject(sei.hProcess, INFINITE);
+                    CloseHandle(sei.hProcess);
                 }
+            }
 
-                fff = FindFirstFileW(searchpath, &wfd);
-                num++;
-            } while (fff != INVALID_HANDLE_VALUE);
+            return S_OK;
         }
-        
-        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, NULL, 0, &searchpath[pathend], wcslen(&searchpath[pathend]) * sizeof(WCHAR));
-        
-        free(searchpath);
-        
-        if (Status != STATUS_SUCCESS) {
+    } else {
+        if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 0) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, NEW_SUBVOL_VERBA))) {
+            HANDLE h;
+            IO_STATUS_BLOCK iosb;
+            NTSTATUS Status;
+            ULONG pathlen, searchpathlen, pathend, bcslen;
+            WCHAR name[MAX_PATH], *searchpath;
+            btrfs_create_subvol* bcs;
+            HANDLE fff;
+            WIN32_FIND_DATAW wfd;
+
+            if (!LoadStringW(module, IDS_NEW_SUBVOL_FILENAME, name, MAX_PATH)) {
+                ShowError(pici->hwnd, GetLastError());
+                return E_FAIL;
+            }
+
+            h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+            if (h == INVALID_HANDLE_VALUE) {
+                ShowError(pici->hwnd, GetLastError());
+                return E_FAIL;
+            }
+
+            pathlen = wcslen(path);
+
+            searchpathlen = pathlen + wcslen(name) + 10;
+            searchpath = (WCHAR*)malloc(searchpathlen * sizeof(WCHAR));
+
+            StringCchCopyW(searchpath, searchpathlen, path);
+            StringCchCatW(searchpath, searchpathlen, L"\\");
+            pathend = wcslen(searchpath);
+
+            StringCchCatW(searchpath, searchpathlen, name);
+
+            fff = FindFirstFileW(searchpath, &wfd);
+
+            if (fff != INVALID_HANDLE_VALUE) {
+                ULONG i = wcslen(searchpath), num = 2;
+
+                do {
+                    FindClose(fff);
+
+                    searchpath[i] = 0;
+                    if (StringCchPrintfW(searchpath, searchpathlen, L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+                        MessageBoxW(pici->hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
+                        CloseHandle(h);
+                        return E_FAIL;
+                    }
+
+                    fff = FindFirstFileW(searchpath, &wfd);
+                    num++;
+                } while (fff != INVALID_HANDLE_VALUE);
+            }
+
+            bcslen = offsetof(btrfs_create_subvol, name[0]) + (wcslen(&searchpath[pathend]) * sizeof(WCHAR));
+            bcs = (btrfs_create_subvol*)malloc(bcslen);
+
+            bcs->readonly = FALSE;
+            bcs->posix = FALSE;
+            bcs->namelen = wcslen(&searchpath[pathend]) * sizeof(WCHAR);
+            memcpy(bcs->name, &searchpath[pathend], bcs->namelen);
+
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, NULL, 0);
+
+            free(searchpath);
+            free(bcs);
+
+            if (!NT_SUCCESS(Status)) {
+                CloseHandle(h);
+                ShowNtStatusError(pici->hwnd, Status);
+                return E_FAIL;
+            }
+
             CloseHandle(h);
-            ShowNtStatusError(pici->hwnd, Status);
-            return E_FAIL;
+
+            return S_OK;
+        } else if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 1) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, RECV_VERBA))) {
+            WCHAR dll[MAX_PATH];
+            std::wstring t;
+            SHELLEXECUTEINFOW sei;
+
+            GetModuleFileNameW(module, dll, sizeof(dll) / sizeof(WCHAR));
+
+            t = L"\"";
+            t += dll;
+            t += L"\",RecvSubvolGUI ";
+            t += path;
+
+            RtlZeroMemory(&sei, sizeof(sei));
+
+            sei.cbSize = sizeof(sei);
+            sei.hwnd = pici->hwnd;
+            sei.lpVerb = L"runas";
+            sei.lpFile = L"rundll32.exe";
+            sei.lpParameters = t.c_str();
+            sei.nShow = SW_SHOW;
+            sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+            if (!ShellExecuteExW(&sei)) {
+                ShowError(pici->hwnd, GetLastError());
+                return E_FAIL;
+            }
+
+            WaitForSingleObject(sei.hProcess, INFINITE);
+            CloseHandle(sei.hProcess);
+
+            return S_OK;
+        } else if ((IS_INTRESOURCE(pici->lpVerb) && (ULONG_PTR)pici->lpVerb == 2) || (!IS_INTRESOURCE(pici->lpVerb) && !strcmp(pici->lpVerb, REFLINK_VERBA))) {
+            HDROP hdrop;
+
+            if (!IsClipboardFormatAvailable(CF_HDROP))
+                return S_OK;
+
+            if (!OpenClipboard(pici->hwnd)) {
+                ShowError(pici->hwnd, GetLastError());
+                return E_FAIL;
+            }
+
+            hdrop = (HDROP)GetClipboardData(CF_HDROP);
+
+            if (hdrop) {
+                HANDLE lh;
+
+                lh = GlobalLock(hdrop);
+
+                if (lh) {
+                    ULONG num_files, i;
+                    WCHAR fn[MAX_PATH];
+
+                    num_files = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
+
+                    for (i = 0; i < num_files; i++) {
+                        if (DragQueryFileW(hdrop, i, fn, sizeof(fn) / sizeof(WCHAR))) {
+                            if (!reflink_copy(pici->hwnd, fn, pici->lpDirectoryW)) {
+                                GlobalUnlock(lh);
+                                CloseClipboard();
+                                return E_FAIL;
+                            }
+                        }
+                    }
+
+                    GlobalUnlock(lh);
+                }
+            }
+
+            CloseClipboard();
+
+            return S_OK;
         }
-        
-        CloseHandle(h);
-        
-        return S_OK;
     }
-    
+
     return E_FAIL;
 }
 
@@ -440,34 +1237,558 @@
 HRESULT __stdcall BtrfsContextMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax) {
     if (ignore)
         return E_INVALIDARG;
-    
+
     if (idCmd != 0)
         return E_INVALIDARG;
-    
-    switch (uFlags) {
-        case GCS_HELPTEXTA:
-            if (LoadStringA(module, bg ? IDS_NEW_SUBVOL_HELP_TEXT : IDS_CREATE_SNAPSHOT_HELP_TEXT, pszName, cchMax))
-                return S_OK;
-            else
-                return E_FAIL;
-            
-        case GCS_HELPTEXTW:
-            if (LoadStringW(module, bg ? IDS_NEW_SUBVOL_HELP_TEXT : IDS_CREATE_SNAPSHOT_HELP_TEXT, (LPWSTR)pszName, cchMax))
-                return S_OK;
-            else
-                return E_FAIL;
-            
-        case GCS_VALIDATEA:
-        case GCS_VALIDATEW:
-            return S_OK;
-            
-        case GCS_VERBA:
-            return StringCchCopyA(pszName, cchMax, bg ? NEW_SUBVOL_VERBA : SNAPSHOT_VERBA);
-            
-        case GCS_VERBW:
-            return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, bg ? NEW_SUBVOL_VERBW : SNAPSHOT_VERBW);
-            
-        default:
+
+    if (!bg) {
+        if (idCmd == 0) {
+            switch (uFlags) {
+                case GCS_HELPTEXTA:
+                    if (LoadStringA(module, IDS_CREATE_SNAPSHOT_HELP_TEXT, pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_HELPTEXTW:
+                    if (LoadStringW(module, IDS_CREATE_SNAPSHOT_HELP_TEXT, (LPWSTR)pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_VALIDATEA:
+                case GCS_VALIDATEW:
+                    return S_OK;
+
+                case GCS_VERBA:
+                    return StringCchCopyA(pszName, cchMax, SNAPSHOT_VERBA);
+
+                case GCS_VERBW:
+                    return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, SNAPSHOT_VERBW);
+
+                default:
+                    return E_INVALIDARG;
+            }
+        } else if (idCmd == 1) {
+            switch (uFlags) {
+                case GCS_HELPTEXTA:
+                    if (LoadStringA(module, IDS_SEND_SUBVOL_HELP, pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_HELPTEXTW:
+                    if (LoadStringW(module, IDS_SEND_SUBVOL_HELP, (LPWSTR)pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_VALIDATEA:
+                case GCS_VALIDATEW:
+                    return S_OK;
+
+                case GCS_VERBA:
+                    return StringCchCopyA(pszName, cchMax, SEND_VERBA);
+
+                case GCS_VERBW:
+                    return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, SEND_VERBW);
+
+                default:
+                    return E_INVALIDARG;
+                }
+        } else
             return E_INVALIDARG;
+    } else {
+        if (idCmd == 0) {
+            switch (uFlags) {
+                case GCS_HELPTEXTA:
+                    if (LoadStringA(module, IDS_NEW_SUBVOL_HELP_TEXT, pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_HELPTEXTW:
+                    if (LoadStringW(module, IDS_NEW_SUBVOL_HELP_TEXT, (LPWSTR)pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_VALIDATEA:
+                case GCS_VALIDATEW:
+                    return S_OK;
+
+                case GCS_VERBA:
+                    return StringCchCopyA(pszName, cchMax, NEW_SUBVOL_VERBA);
+
+                case GCS_VERBW:
+                    return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, NEW_SUBVOL_VERBW);
+
+                default:
+                    return E_INVALIDARG;
+            }
+        } else if (idCmd == 1) {
+            switch (uFlags) {
+                case GCS_HELPTEXTA:
+                    if (LoadStringA(module, IDS_RECV_SUBVOL_HELP, pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_HELPTEXTW:
+                    if (LoadStringW(module, IDS_RECV_SUBVOL_HELP, (LPWSTR)pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_VALIDATEA:
+                case GCS_VALIDATEW:
+                    return S_OK;
+
+                case GCS_VERBA:
+                    return StringCchCopyA(pszName, cchMax, RECV_VERBA);
+
+                case GCS_VERBW:
+                    return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, RECV_VERBW);
+
+                default:
+                    return E_INVALIDARG;
+            }
+        } else if (idCmd == 2) {
+            switch (uFlags) {
+                case GCS_HELPTEXTA:
+                    if (LoadStringA(module, IDS_REFLINK_PASTE_HELP, pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_HELPTEXTW:
+                    if (LoadStringW(module, IDS_REFLINK_PASTE_HELP, (LPWSTR)pszName, cchMax))
+                        return S_OK;
+                    else
+                        return E_FAIL;
+
+                case GCS_VALIDATEA:
+                case GCS_VALIDATEW:
+                    return S_OK;
+
+                case GCS_VERBA:
+                    return StringCchCopyA(pszName, cchMax, REFLINK_VERBA);
+
+                case GCS_VERBW:
+                    return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, REFLINK_VERBW);
+
+                default:
+                    return E_INVALIDARG;
+            }
+        } else
+            return E_INVALIDARG;
     }
 }
+
+static void reflink_copy2(std::wstring srcfn, std::wstring destdir, std::wstring destname) {
+    HANDLE source, dest;
+    BOOL ret = FALSE;
+    FILE_BASIC_INFO fbi;
+    FILETIME atime, mtime;
+    btrfs_inode_info bii;
+    btrfs_set_inode_info bsii;
+    ULONG bytesret;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_set_xattr bsxa;
+
+    source = CreateFileW(srcfn.c_str(), GENERIC_READ | FILE_TRAVERSE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);
+    if (source == INVALID_HANDLE_VALUE)
+        return;
+
+    Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii, sizeof(btrfs_inode_info));
+    if (!NT_SUCCESS(Status)) {
+        CloseHandle(source);
+        return;
+    }
+
+    // if subvol, do snapshot instead
+    if (bii.inode == SUBVOL_ROOT_INODE) {
+        ULONG bcslen;
+        btrfs_create_snapshot* bcs;
+        HANDLE dirh;
+
+        dirh = CreateFileW(destdir.c_str(), FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (dirh == INVALID_HANDLE_VALUE) {
+            CloseHandle(source);
+            return;
+        }
+
+        bcslen = offsetof(btrfs_create_snapshot, name[0]) + (destname.length() * sizeof(WCHAR));
+        bcs = (btrfs_create_snapshot*)malloc(bcslen);
+        bcs->subvol = source;
+        bcs->namelen = destname.length() * sizeof(WCHAR);
+        memcpy(bcs->name, destname.c_str(), destname.length() * sizeof(WCHAR));
+
+        Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, bcslen, NULL, 0);
+
+        free(bcs);
+
+        CloseHandle(source);
+        CloseHandle(dirh);
+
+        return;
+    }
+
+    if (!GetFileInformationByHandleEx(source, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) {
+        CloseHandle(source);
+        return;
+    }
+
+    if (bii.type == BTRFS_TYPE_CHARDEV || bii.type == BTRFS_TYPE_BLOCKDEV || bii.type == BTRFS_TYPE_FIFO || bii.type == BTRFS_TYPE_SOCKET) {
+        HANDLE dirh;
+        ULONG bmnsize;
+        btrfs_mknod* bmn;
+
+        dirh = CreateFileW(destdir.c_str(), FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (dirh == INVALID_HANDLE_VALUE) {
+            CloseHandle(source);
+            return;
+        }
+
+        bmnsize = offsetof(btrfs_mknod, name[0]) + (destname.length() * sizeof(WCHAR));
+        bmn = (btrfs_mknod*)malloc(bmnsize);
+
+        bmn->inode = 0;
+        bmn->type = bii.type;
+        bmn->st_rdev = bii.st_rdev;
+        bmn->namelen = destname.length() * sizeof(WCHAR);
+        memcpy(bmn->name, destname.c_str(), bmn->namelen);
+
+        Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_MKNOD, bmn, bmnsize, NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            CloseHandle(dirh);
+            CloseHandle(source);
+            free(bmn);
+            return;
+        }
+
+        CloseHandle(dirh);
+        free(bmn);
+
+        dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, 0, NULL);
+    } else if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (CreateDirectoryExW(srcfn.c_str(), (destdir + destname).c_str(), NULL))
+            dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                               NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+            else
+                dest = INVALID_HANDLE_VALUE;
+    } else
+        dest = CreateFileW((destdir + destname).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, source);
+
+    if (dest == INVALID_HANDLE_VALUE) {
+        CloseHandle(source);
+        return;
+    }
+
+    memset(&bsii, 0, sizeof(btrfs_set_inode_info));
+
+    bsii.flags_changed = TRUE;
+    bsii.flags = bii.flags;
+
+    if (bii.flags & BTRFS_INODE_COMPRESS) {
+        bsii.compression_type_changed = TRUE;
+        bsii.compression_type = bii.compression_type;
+    }
+
+    Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
+    if (!NT_SUCCESS(Status))
+        goto end;
+
+    if (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (!(fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+            HANDLE h;
+            WIN32_FIND_DATAW fff;
+            std::wstring qs;
+
+            qs = srcfn;
+            qs += L"\\*";
+
+            h = FindFirstFileW(qs.c_str(), &fff);
+            if (h != INVALID_HANDLE_VALUE) {
+                do {
+                    std::wstring fn2;
+
+                    if (fff.cFileName[0] == '.' && (fff.cFileName[1] == 0 || (fff.cFileName[1] == '.' && fff.cFileName[2] == 0)))
+                        continue;
+
+                    fn2 = srcfn;
+                    fn2 += L"\\";
+                    fn2 += fff.cFileName;
+
+                    reflink_copy2(fn2, destdir + destname + L"\\", fff.cFileName);
+                } while (FindNextFileW(h, &fff));
+
+                FindClose(h);
+            }
+        }
+
+        // CreateDirectoryExW also copies streams, no need to do it here
+    } else {
+        HANDLE h;
+        WIN32_FIND_STREAM_DATA fsd;
+
+        if (fbi.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+            reparse_header rh;
+            ULONG rplen;
+            UINT8* rp;
+
+            if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, &rh, sizeof(reparse_header), &bytesret, NULL)) {
+                if (GetLastError() != ERROR_MORE_DATA)
+                    goto end;
+            }
+
+            rplen = sizeof(reparse_header) + rh.ReparseDataLength;
+            rp = (UINT8*)malloc(rplen);
+
+            if (!DeviceIoControl(source, FSCTL_GET_REPARSE_POINT, NULL, 0, rp, rplen, &bytesret, NULL))
+                goto end;
+
+            if (!DeviceIoControl(dest, FSCTL_SET_REPARSE_POINT, rp, rplen, NULL, 0, &bytesret, NULL))
+                goto end;
+
+            free(rp);
+        } else {
+            FILE_STANDARD_INFO fsi;
+            FILE_END_OF_FILE_INFO feofi;
+            FSCTL_GET_INTEGRITY_INFORMATION_BUFFER fgiib;
+            FSCTL_SET_INTEGRITY_INFORMATION_BUFFER fsiib;
+            DUPLICATE_EXTENTS_DATA ded;
+            UINT64 offset, alloc_size;
+            ULONG maxdup;
+
+            if (!GetFileInformationByHandleEx(source, FileStandardInfo, &fsi, sizeof(FILE_STANDARD_INFO)))
+                goto end;
+
+            if (!DeviceIoControl(source, FSCTL_GET_INTEGRITY_INFORMATION, NULL, 0, &fgiib, sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER), &bytesret, NULL))
+                goto end;
+
+            if (fbi.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) {
+                if (!DeviceIoControl(dest, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesret, NULL))
+                    goto end;
+            }
+
+            fsiib.ChecksumAlgorithm = fgiib.ChecksumAlgorithm;
+            fsiib.Reserved = 0;
+            fsiib.Flags = fgiib.Flags;
+            if (!DeviceIoControl(dest, FSCTL_SET_INTEGRITY_INFORMATION, &fsiib, sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER), NULL, 0, &bytesret, NULL))
+                goto end;
+
+            feofi.EndOfFile = fsi.EndOfFile;
+            if (!SetFileInformationByHandle(dest, FileEndOfFileInfo, &feofi, sizeof(FILE_END_OF_FILE_INFO)))
+                goto end;
+
+            ded.FileHandle = source;
+            maxdup = 0xffffffff - fgiib.ClusterSizeInBytes + 1;
+
+            alloc_size = sector_align(fsi.EndOfFile.QuadPart, fgiib.ClusterSizeInBytes);
+
+            offset = 0;
+            while (offset < alloc_size) {
+                ded.SourceFileOffset.QuadPart = ded.TargetFileOffset.QuadPart = offset;
+                ded.ByteCount.QuadPart = maxdup < (alloc_size - offset) ? maxdup : (alloc_size - offset);
+                if (!DeviceIoControl(dest, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA), NULL, 0, &bytesret, NULL))
+                    goto end;
+
+                offset += ded.ByteCount.QuadPart;
+            }
+        }
+
+        h = FindFirstStreamW(srcfn.c_str(), FindStreamInfoStandard, &fsd, 0);
+        if (h != INVALID_HANDLE_VALUE) {
+            do {
+                std::wstring sn;
+
+                sn = fsd.cStreamName;
+
+                if (sn != L"::$DATA" && sn.length() > 6 && sn.substr(sn.length() - 6, 6) == L":$DATA") {
+                    HANDLE stream;
+                    UINT8* data = NULL;
+
+                    if (fsd.StreamSize.QuadPart > 0) {
+                        std::wstring fn2;
+
+                        fn2 = srcfn;
+                        fn2 += sn;
+
+                        stream = CreateFileW(fn2.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+                        if (stream == INVALID_HANDLE_VALUE)
+                            goto end;
+
+                        // We can get away with this because our streams are guaranteed to be below 64 KB -
+                        // don't do this on NTFS!
+                        data = (UINT8*)malloc(fsd.StreamSize.QuadPart);
+
+                        if (!ReadFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
+                            free(data);
+                            CloseHandle(stream);
+                            goto end;
+                        }
+
+                        CloseHandle(stream);
+                    }
+
+                    stream = CreateFileW((destdir + destname + sn).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_NEW, 0, NULL);
+
+                    if (stream == INVALID_HANDLE_VALUE) {
+                        if (data) free(data);
+                        goto end;
+                    }
+
+                    if (data) {
+                        if (!WriteFile(stream, data, fsd.StreamSize.QuadPart, &bytesret, NULL)) {
+                            free(data);
+                            CloseHandle(stream);
+                            goto end;
+                        }
+
+                        free(data);
+                    }
+
+                    CloseHandle(stream);
+                }
+            } while (FindNextStreamW(h, &fsd));
+
+            FindClose(h);
+        }
+    }
+
+    atime.dwLowDateTime = fbi.LastAccessTime.LowPart;
+    atime.dwHighDateTime = fbi.LastAccessTime.HighPart;
+    mtime.dwLowDateTime = fbi.LastWriteTime.LowPart;
+    mtime.dwHighDateTime = fbi.LastWriteTime.HighPart;
+    SetFileTime(dest, NULL, &atime, &mtime);
+
+    Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, &bsxa, sizeof(btrfs_set_xattr));
+
+    if (Status == STATUS_BUFFER_OVERFLOW || (NT_SUCCESS(Status) && bsxa.valuelen > 0)) {
+        ULONG xalen = 0;
+        btrfs_set_xattr *xa = NULL, *xa2;
+
+        do {
+            xalen += 1024;
+
+            if (xa) free(xa);
+            xa = (btrfs_set_xattr*)malloc(xalen);
+
+            Status = NtFsControlFile(source, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_XATTRS, NULL, 0, xa, xalen);
+        } while (Status == STATUS_BUFFER_OVERFLOW);
+
+        if (!NT_SUCCESS(Status)) {
+            free(xa);
+            goto end;
+        }
+
+        xa2 = xa;
+        while (xa2->valuelen > 0) {
+            Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, xa2,
+                                     offsetof(btrfs_set_xattr, data[0]) + xa2->namelen + xa2->valuelen, NULL, 0);
+            if (!NT_SUCCESS(Status)) {
+                free(xa);
+                goto end;
+            }
+            xa2 = (btrfs_set_xattr*)&xa2->data[xa2->namelen + xa2->valuelen];
+        }
+
+        free(xa);
+    } else if (!NT_SUCCESS(Status))
+        goto end;
+
+    ret = TRUE;
+
+end:
+    if (!ret) {
+        FILE_DISPOSITION_INFO fdi;
+
+        fdi.DeleteFile = TRUE;
+        SetFileInformationByHandle(dest, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO));
+    }
+
+    CloseHandle(dest);
+    CloseHandle(source);
+}
+
+void CALLBACK ReflinkCopyW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    LPWSTR* args;
+    int num_args;
+
+    args = CommandLineToArgvW(lpszCmdLine, &num_args);
+
+    if (!args)
+        return;
+
+    if (num_args >= 2) {
+        HANDLE destdirh;
+        BOOL dest_is_dir = FALSE;
+        std::wstring dest = args[num_args - 1], destdir, destname;
+        WCHAR volpath2[MAX_PATH];
+        int i;
+
+        destdirh = CreateFileW(dest.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (destdirh != INVALID_HANDLE_VALUE) {
+            BY_HANDLE_FILE_INFORMATION bhfi;
+
+            if (GetFileInformationByHandle(destdirh, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+                dest_is_dir = TRUE;
+
+                destdir = dest;
+                if (destdir.substr(destdir.length() - 1, 1) != L"\\")
+                    destdir += L"\\";
+            }
+            CloseHandle(destdirh);
+        }
+
+        if (!dest_is_dir) {
+            size_t found = dest.rfind(L"\\");
+
+            if (found == std::wstring::npos) {
+                destdir = L"";
+                destname = dest;
+            } else {
+                destdir = dest.substr(0, found);
+                destname = dest.substr(found + 1);
+            }
+        }
+
+        if (!GetVolumePathNameW(dest.c_str(), volpath2, sizeof(volpath2) / sizeof(WCHAR)))
+            goto end;
+
+        for (i = 0; i < num_args - 1; i++) {
+            WIN32_FIND_DATAW ffd;
+            HANDLE h;
+
+            h = FindFirstFileW(args[i], &ffd);
+            if (h != INVALID_HANDLE_VALUE) {
+                WCHAR volpath1[MAX_PATH];
+                std::wstring path = args[i];
+                size_t found = path.rfind(L"\\");
+
+                if (found == std::wstring::npos)
+                    path = L"";
+                else
+                    path = path.substr(0, found);
+
+                path += L"\\";
+
+                if (get_volume_path_parent(path.c_str(), volpath1, sizeof(volpath1) / sizeof(WCHAR))) {
+                    if (!wcscmp(volpath1, volpath2)) {
+                        do {
+                            reflink_copy2(path + ffd.cFileName, destdir, dest_is_dir ? ffd.cFileName : destname);
+                        } while (FindNextFileW(h, &ffd));
+                    }
+                }
+
+                FindClose(h);
+            }
+        }
+    }
+
+end:
+    LocalFree(args);
+}
Index: dll/shellext/shellbtrfs/contextmenu.h
===================================================================
--- dll/shellext/shellbtrfs/contextmenu.h	(révision 75772)
+++ dll/shellext/shellbtrfs/contextmenu.h	(copie de travail)
@@ -1,17 +1,17 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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/>. */
 
@@ -25,6 +25,8 @@
         refcount = 0;
         ignore = TRUE;
         stgm_set = FALSE;
+        uacicon = NULL;
+        allow_snapshot = FALSE;
         InterlockedIncrement(&objs_loaded);
     }
 
@@ -33,14 +35,17 @@
             GlobalUnlock(stgm.hGlobal);
             ReleaseStgMedium(&stgm);
         }
-        
+
+        if (uacicon)
+            DeleteObject(uacicon);
+
         InterlockedDecrement(&objs_loaded);
     }
 
     // IUnknown
-    
+
     HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
-    
+
     ULONG __stdcall AddRef() {
         return InterlockedIncrement(&refcount);
     }
@@ -47,19 +52,19 @@
 
     ULONG __stdcall Release() {
         LONG rc = InterlockedDecrement(&refcount);
-        
+
         if (rc == 0)
             delete this;
-        
+
         return rc;
     }
-    
+
     // IShellExtInit
-    
+
     virtual HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID);
-    
+
     // IContextMenu
-    
+
     virtual HRESULT __stdcall QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
     virtual HRESULT __stdcall InvokeCommand(LPCMINVOKECOMMANDINFO pici);
     virtual HRESULT __stdcall GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax);
@@ -66,9 +71,13 @@
 
 private:
     LONG refcount;
-    BOOL ignore;
+    BOOL ignore, allow_snapshot;
     BOOL bg;
     WCHAR path[MAX_PATH];
     STGMEDIUM stgm;
     BOOL stgm_set;
+    HBITMAP uacicon;
+
+    BOOL reflink_copy(HWND hwnd, const WCHAR* fn, const WCHAR* dir);
+    void get_uac_icon();
 };
Index: dll/shellext/shellbtrfs/devices.cpp
===================================================================
--- dll/shellext/shellbtrfs/devices.cpp	(nonexistent)
+++ dll/shellext/shellbtrfs/devices.cpp	(copie de travail)
@@ -0,0 +1,1081 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ *
+ * 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/>. */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#include "shellext.h"
+#include "devices.h"
+#include "resource.h"
+#include "balance.h"
+#include <stddef.h>
+#include <uxtheme.h>
+#include <setupapi.h>
+#include <strsafe.h>
+#include <mountmgr.h>
+#include <algorithm>
+#ifndef __REACTOS__
+#include "../btrfs.h"
+#else
+#include <ntddstor.h>
+#include <ndk/rtlfuncs.h>
+#include <ndk/obfuncs.h>
+#include "btrfs.h"
+#endif
+
+DEFINE_GUID(GUID_DEVINTERFACE_HIDDEN_VOLUME, 0x7f108a28L, 0x9833, 0x4b3b, 0xb7, 0x80, 0x2c, 0x6b, 0x5f, 0xa5, 0xc0, 0x62);
+
+static std::wstring get_mountdev_name(HANDLE h) {
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    MOUNTDEV_NAME mdn, *mdn2;
+    ULONG mdnsize;
+    std::wstring name;
+
+    Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
+                                   NULL, 0, &mdn, sizeof(MOUNTDEV_NAME));
+    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+        return L"";
+
+    mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength;
+
+    mdn2 = (MOUNTDEV_NAME*)malloc(mdnsize);
+
+    Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
+                                   NULL, 0, mdn2, mdnsize);
+    if (!NT_SUCCESS(Status)) {
+        free(mdn2);
+        return L"";
+    }
+
+    name = std::wstring(mdn2->Name, mdn2->NameLength / sizeof(WCHAR));
+
+    free(mdn2);
+
+    return name;
+}
+
+static void find_devices(HWND hwnd, const GUID* guid, HANDLE mountmgr, std::vector<device>* device_list) {
+    HDEVINFO h;
+
+    static WCHAR dosdevices[] = L"\\DosDevices\\";
+
+    h = SetupDiGetClassDevs(guid, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        DWORD index = 0;
+        SP_DEVICE_INTERFACE_DATA did;
+
+        did.cbSize = sizeof(did);
+
+        if (!SetupDiEnumDeviceInterfaces(h, NULL, guid, index, &did))
+            return;
+
+        do {
+            SP_DEVINFO_DATA dd;
+            SP_DEVICE_INTERFACE_DETAIL_DATA_W* detail;
+            DWORD size;
+
+            dd.cbSize = sizeof(dd);
+
+            SetupDiGetDeviceInterfaceDetailW(h, &did, NULL, 0, &size, NULL);
+
+            detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(size);
+            memset(detail, 0, size);
+
+            detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
+
+            if (SetupDiGetDeviceInterfaceDetailW(h, &did, detail, size, &size, &dd)) {
+                NTSTATUS Status;
+                HANDLE file;
+                device dev;
+                STORAGE_DEVICE_NUMBER sdn;
+                IO_STATUS_BLOCK iosb;
+                UNICODE_STRING path;
+                OBJECT_ATTRIBUTES attr;
+                GET_LENGTH_INFORMATION gli;
+                ULONG i;
+                UINT8 sb[4096];
+
+                path.Buffer = detail->DevicePath;
+                path.Length = path.MaximumLength = wcslen(detail->DevicePath) * sizeof(WCHAR);
+
+                if (path.Length > 4 * sizeof(WCHAR) && path.Buffer[0] == '\\' && path.Buffer[1] == '\\'  && path.Buffer[2] == '?'  && path.Buffer[3] == '\\')
+                    path.Buffer[1] = '?';
+
+                InitializeObjectAttributes(&attr, &path, 0, NULL, NULL);
+
+                Status = NtOpenFile(&file, FILE_GENERIC_READ, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_ALERT);
+
+                if (!NT_SUCCESS(Status))
+                    goto nextitem2;
+
+                dev.pnp_name = detail->DevicePath;
+
+                Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(GET_LENGTH_INFORMATION));
+                if (!NT_SUCCESS(Status))
+                    goto nextitem;
+
+                dev.size = gli.Length.QuadPart;
+
+                Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(STORAGE_DEVICE_NUMBER));
+                if (!NT_SUCCESS(Status)) {
+                    dev.disk_num = 0xffffffff;
+                    dev.part_num = 0xffffffff;
+                } else {
+                    dev.disk_num = sdn.DeviceNumber;
+                    dev.part_num = sdn.PartitionNumber;
+                }
+
+                dev.friendly_name = L"";
+                dev.drive = L"";
+                dev.fstype = L"";
+                dev.has_parts = FALSE;
+                dev.ignore = FALSE;
+                dev.multi_device = FALSE;
+
+                dev.is_disk = RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID);
+
+                if (dev.is_disk) {
+                    STORAGE_PROPERTY_QUERY spq;
+                    STORAGE_DEVICE_DESCRIPTOR sdd, *sdd2;
+                    ULONG dlisize;
+                    DRIVE_LAYOUT_INFORMATION_EX* dli;
+
+                    spq.PropertyId = StorageDeviceProperty;
+                    spq.QueryType = PropertyStandardQuery;
+                    spq.AdditionalParameters[0] = 0;
+
+                    Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_QUERY_PROPERTY,
+                                                   &spq, sizeof(STORAGE_PROPERTY_QUERY), &sdd, sizeof(STORAGE_DEVICE_DESCRIPTOR));
+
+                    if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW) {
+                        sdd2 = (STORAGE_DEVICE_DESCRIPTOR*)malloc(sdd.Size);
+
+                        Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_QUERY_PROPERTY,
+                                                    &spq, sizeof(STORAGE_PROPERTY_QUERY), sdd2, sdd.Size);
+                        if (NT_SUCCESS(Status)) {
+                            std::string desc2;
+
+                            desc2 = "";
+
+                            if (sdd2->VendorIdOffset != 0) {
+                                desc2 += (char*)((UINT8*)sdd2 + sdd2->VendorIdOffset);
+
+                                while (desc2.length() > 0 && desc2[desc2.length() - 1] == ' ')
+                                    desc2 = desc2.substr(0, desc2.length() - 1);
+                            }
+
+                            if (sdd2->ProductIdOffset != 0) {
+                                if (sdd2->VendorIdOffset != 0 && desc2.length() != 0 && desc2[desc2.length() - 1] != ' ')
+                                    desc2 += " ";
+
+                                desc2 += (char*)((UINT8*)sdd2 + sdd2->ProductIdOffset);
+
+                                while (desc2.length() > 0 && desc2[desc2.length() - 1] == ' ')
+                                    desc2 = desc2.substr(0, desc2.length() - 1);
+                            }
+
+                            if (sdd2->VendorIdOffset != 0 || sdd2->ProductIdOffset != 0) {
+                                ULONG ss;
+
+                                ss = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, desc2.c_str(), -1, NULL, 0);
+
+                                if (ss > 0) {
+                                    WCHAR* desc3 = (WCHAR*)malloc(ss * sizeof(WCHAR));
+
+                                    if (MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, desc2.c_str(), -1, desc3, ss * sizeof(WCHAR)))
+                                        dev.friendly_name = desc3;
+
+                                    free(desc3);
+                                }
+                            }
+                        }
+
+                        free(sdd2);
+                    }
+
+                    dlisize = 0;
+                    dli = NULL;
+
+                    do {
+                        dlisize += 1024;
+
+                        if (dli)
+                            free(dli);
+
+                        dli = (DRIVE_LAYOUT_INFORMATION_EX*)malloc(dlisize);
+
+                        Status = NtDeviceIoControlFile(file, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
+                                                       NULL, 0, dli, dlisize);
+                    } while (Status == STATUS_BUFFER_TOO_SMALL);
+
+                    if (NT_SUCCESS(Status) && dli->PartitionCount > 0)
+                        dev.has_parts = TRUE;
+
+                    free(dli);
+                } else {
+                    ULONG mmpsize;
+                    MOUNTMGR_MOUNT_POINT* mmp;
+                    MOUNTMGR_MOUNT_POINTS mmps;
+
+                    mmpsize = sizeof(MOUNTMGR_MOUNT_POINT) + path.Length;
+
+                    mmp = (MOUNTMGR_MOUNT_POINT*)malloc(mmpsize);
+
+                    RtlZeroMemory(mmp, sizeof(MOUNTMGR_MOUNT_POINT));
+                    mmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
+                    mmp->DeviceNameLength = path.Length;
+                    RtlCopyMemory(&mmp[1], path.Buffer, path.Length);
+
+                    Status = NtDeviceIoControlFile(mountmgr, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_QUERY_POINTS,
+                                                   mmp, mmpsize, &mmps, sizeof(MOUNTMGR_MOUNT_POINTS));
+                    if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW) {
+                        MOUNTMGR_MOUNT_POINTS* mmps2;
+
+                        mmps2 = (MOUNTMGR_MOUNT_POINTS*)malloc(mmps.Size);
+
+                        Status = NtDeviceIoControlFile(mountmgr, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_QUERY_POINTS,
+                                                    mmp, mmpsize, mmps2, mmps.Size);
+
+                        if (NT_SUCCESS(Status)) {
+                            ULONG i;
+
+                            for (i = 0; i < mmps2->NumberOfMountPoints; i++) {
+                                WCHAR* symlink = (WCHAR*)((UINT8*)mmps2 + mmps2->MountPoints[i].SymbolicLinkNameOffset);
+
+                                if (mmps2->MountPoints[i].SymbolicLinkNameLength == 0x1c &&
+                                    RtlCompareMemory(symlink, dosdevices, wcslen(dosdevices) * sizeof(WCHAR)) == wcslen(dosdevices) * sizeof(WCHAR) &&
+                                    symlink[13] == ':'
+                                ) {
+                                    WCHAR dr[3];
+
+                                    dr[0] = symlink[12];
+                                    dr[1] = ':';
+                                    dr[2] = 0;
+
+                                    dev.drive = dr;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+
+                    free(mmp);
+                }
+
+                if (!dev.is_disk || !dev.has_parts) {
+                    i = 0;
+                    while (fs_ident[i].name) {
+                        if (i == 0 || fs_ident[i].kboff != fs_ident[i-1].kboff) {
+                            LARGE_INTEGER off;
+
+                            off.QuadPart = fs_ident[i].kboff * 1024;
+                            Status = NtReadFile(file, NULL, NULL, NULL, &iosb, sb, sizeof(sb), &off, NULL);
+                        }
+
+                        if (NT_SUCCESS(Status)) {
+                            if (RtlCompareMemory(sb + fs_ident[i].sboff, fs_ident[i].magic, fs_ident[i].magiclen) == fs_ident[i].magiclen) {
+                                dev.fstype = fs_ident[i].name;
+
+                                if (dev.fstype == L"Btrfs") {
+                                    superblock* bsb = (superblock*)sb;
+
+                                    RtlCopyMemory(&dev.fs_uuid, &bsb->uuid, sizeof(BTRFS_UUID));
+                                    RtlCopyMemory(&dev.dev_uuid, &bsb->dev_item.device_uuid, sizeof(BTRFS_UUID));
+                                }
+
+                                break;
+                            }
+                        }
+
+                        i++;
+                    }
+
+                    if (dev.fstype == L"Btrfs" && RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) != sizeof(GUID)) {
+                        std::wstring name;
+                        std::wstring pref = L"\\Device\\Btrfs{";
+
+                        name = get_mountdev_name(file);
+
+                        if (name.length() > pref.length() && RtlCompareMemory(name.c_str(), pref.c_str(), pref.length() * sizeof(WCHAR)) == pref.length() * sizeof(WCHAR))
+                            dev.ignore = TRUE;
+                    }
+                }
+
+                device_list->push_back(dev);
+
+nextitem:
+                NtClose(file);
+            }
+
+nextitem2:
+            free(detail);
+
+            index++;
+        } while (SetupDiEnumDeviceInterfaces(h, NULL, guid, index, &did));
+
+        SetupDiDestroyDeviceInfoList(h);
+    } else {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+}
+
+static bool sort_devices(device i, device j) {
+    if (i.disk_num < j.disk_num)
+        return true;
+
+    if (i.disk_num == j.disk_num && i.part_num < j.part_num)
+        return true;
+
+    return false;
+}
+
+void BtrfsDeviceAdd::populate_device_tree(HWND tree) {
+    HWND hwnd = GetParent(tree);
+    unsigned int i;
+    ULONG last_disk_num = 0xffffffff;
+    HTREEITEM diskitem;
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING us;
+    IO_STATUS_BLOCK iosb;
+    HANDLE mountmgr, btrfsh;
+    btrfs_filesystem* bfs = NULL;
+
+    static WCHAR btrfs[] = L"\\Btrfs";
+
+    device_list.clear();
+
+    RtlInitUnicodeString(&us, MOUNTMGR_DEVICE_NAME);
+    InitializeObjectAttributes(&attr, &us, 0, NULL, NULL);
+
+    Status = NtOpenFile(&mountmgr, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb,
+                        FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT);
+    if (!NT_SUCCESS(Status)) {
+        MessageBoxW(hwnd, L"Could not get handle to mount manager.", L"Error", MB_ICONERROR);
+        return;
+    }
+
+    us.Length = us.MaximumLength = wcslen(btrfs) * sizeof(WCHAR);
+    us.Buffer = btrfs;
+
+    InitializeObjectAttributes(&attr, &us, 0, NULL, NULL);
+
+    Status = NtOpenFile(&btrfsh, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &attr, &iosb,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
+    if (NT_SUCCESS(Status)) {
+        ULONG bfssize = 0;
+
+        do {
+            bfssize += 1024;
+
+            if (bfs) free(bfs);
+            bfs = (btrfs_filesystem*)malloc(bfssize);
+
+            Status = NtDeviceIoControlFile(btrfsh, NULL, NULL, NULL, &iosb, IOCTL_BTRFS_QUERY_FILESYSTEMS, NULL, 0, bfs, bfssize);
+            if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+                free(bfs);
+                bfs = NULL;
+                break;
+            }
+        } while (Status == STATUS_BUFFER_OVERFLOW);
+
+        if (bfs && bfs->num_devices == 0) { // no mounted filesystems found
+            free(bfs);
+            bfs = NULL;
+        }
+    }
+    NtClose(btrfsh);
+
+    find_devices(hwnd, &GUID_DEVINTERFACE_DISK, mountmgr, &device_list);
+    find_devices(hwnd, &GUID_DEVINTERFACE_VOLUME, mountmgr, &device_list);
+    find_devices(hwnd, &GUID_DEVINTERFACE_HIDDEN_VOLUME, mountmgr, &device_list);
+
+    NtClose(mountmgr);
+
+    std::sort(device_list.begin(), device_list.end(), sort_devices);
+
+    for (i = 0; i < device_list.size(); i++) {
+        if (!device_list[i].ignore) {
+            TVINSERTSTRUCTW tis;
+            HTREEITEM item;
+            std::wstring name;
+            WCHAR size[255];
+
+            if (device_list[i].disk_num != 0xffffffff && device_list[i].disk_num == last_disk_num)
+                tis.hParent = diskitem;
+            else
+                tis.hParent = TVI_ROOT;
+
+            tis.hInsertAfter = TVI_LAST;
+            tis.itemex.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
+            tis.itemex.state = TVIS_EXPANDED;
+            tis.itemex.stateMask = TVIS_EXPANDED;
+
+            if (device_list[i].disk_num != 0xffffffff) {
+                WCHAR t[255], u[255];
+
+                if (!LoadStringW(module, device_list[i].part_num != 0 ? IDS_PARTITION : IDS_DISK_NUM, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hwnd, GetLastError());
+                    return;
+                }
+
+                if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, device_list[i].part_num != 0 ? device_list[i].part_num : device_list[i].disk_num) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return;
+
+                name = u;
+            } else
+                name = device_list[i].pnp_name;
+
+            // match child Btrfs devices to their parent
+            if (bfs && device_list[i].drive == L"" && device_list[i].fstype == L"Btrfs") {
+                btrfs_filesystem* bfs2 = bfs;
+
+                while (TRUE) {
+                    if (RtlCompareMemory(&bfs2->uuid, &device_list[i].fs_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                        ULONG j, k;
+                        btrfs_filesystem_device* dev;
+
+                        for (j = 0; j < bfs2->num_devices; j++) {
+                            if (j == 0)
+                                dev = &bfs2->device;
+                            else
+                                dev = (btrfs_filesystem_device*)((UINT8*)dev + offsetof(btrfs_filesystem_device, name[0]) + dev->name_length);
+
+                            if (RtlCompareMemory(&device_list[i].dev_uuid, &device_list[i].dev_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                                for (k = 0; k < device_list.size(); k++) {
+                                    if (k != i && device_list[k].fstype == L"Btrfs" && device_list[k].drive != L"" &&
+                                        RtlCompareMemory(&device_list[k].fs_uuid, &device_list[i].fs_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
+                                        device_list[i].drive = device_list[k].drive;
+                                        break;
+                                    }
+                                }
+
+                                device_list[i].multi_device = bfs2->num_devices > 1;
+
+                                break;
+                            }
+                        }
+
+                        break;
+                    }
+
+                    if (bfs2->next_entry != 0)
+                        bfs2 = (btrfs_filesystem*)((UINT8*)bfs2 + bfs2->next_entry);
+                    else
+                        break;
+                }
+            }
+
+            name += L" (";
+
+            if (device_list[i].friendly_name != L"") {
+                name += device_list[i].friendly_name;
+                name += L", ";
+            }
+
+            if (device_list[i].drive != L"") {
+                name += device_list[i].drive;
+                name += L", ";
+            }
+
+            if (device_list[i].fstype != L"") {
+                name += device_list[i].fstype;
+                name += L", ";
+            }
+
+            format_size(device_list[i].size, size, sizeof(size) / sizeof(WCHAR), FALSE);
+            name += size;
+
+            name += L")";
+
+            tis.itemex.pszText = (WCHAR*)name.c_str();
+            tis.itemex.cchTextMax = name.length();
+            tis.itemex.lParam = (LPARAM)&device_list[i];
+
+            item = (HTREEITEM)SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tis);
+            if (!item) {
+                MessageBoxW(hwnd, L"TVM_INSERTITEM failed", L"Error", MB_ICONERROR);
+                return;
+            }
+
+            if (device_list[i].part_num == 0) {
+                diskitem = item;
+                last_disk_num = device_list[i].disk_num;
+            }
+        }
+    }
+}
+
+void BtrfsDeviceAdd::AddDevice(HWND hwndDlg) {
+    WCHAR mess[255], title[255];
+    NTSTATUS Status;
+    UNICODE_STRING vn;
+    OBJECT_ATTRIBUTES attr;
+    IO_STATUS_BLOCK iosb;
+    HANDLE h, h2;
+
+    if (!sel) {
+        EndDialog(hwndDlg, 0);
+        return;
+    }
+
+    if (sel->fstype != L"") {
+        WCHAR s[255];
+
+        if (!LoadStringW(module, IDS_ADD_DEVICE_CONFIRMATION_FS, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(mess, sizeof(mess) / sizeof(WCHAR), s, sel->fstype.c_str()) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+    } else {
+        if (!LoadStringW(module, IDS_ADD_DEVICE_CONFIRMATION, mess, sizeof(mess) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+    }
+
+    if (!LoadStringW(module, IDS_CONFIRMATION_TITLE, title, sizeof(title) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    if (MessageBoxW(hwndDlg, mess, title, MB_YESNO) != IDYES)
+        return;
+
+    h = CreateFileW(cmdline, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    vn.Length = vn.MaximumLength = sel->pnp_name.length() * sizeof(WCHAR);
+    vn.Buffer = (WCHAR*)sel->pnp_name.c_str();
+
+    InitializeObjectAttributes(&attr, &vn, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    Status = NtOpenFile(&h2, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT);
+    if (!NT_SUCCESS(Status)) {
+        ShowNtStatusError(hwndDlg, Status);
+        CloseHandle(h);
+        return;
+    }
+
+    if (!sel->is_disk) {
+        Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            WCHAR t[255], u[255];
+
+            if (!LoadStringW(module, IDS_LOCK_FAILED, t, sizeof(t) / sizeof(WCHAR))) {
+                ShowError(hwnd, GetLastError());
+                return;
+            }
+
+            if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, Status) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                return;
+
+            if (!LoadStringW(module, IDS_ERROR, title, sizeof(title) / sizeof(WCHAR))) {
+                ShowError(hwndDlg, GetLastError());
+                return;
+            }
+
+            MessageBoxW(hwndDlg, u, title, MB_ICONERROR);
+
+            NtClose(h2);
+            CloseHandle(h);
+            return;
+        }
+    }
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_ADD_DEVICE, &h2, sizeof(HANDLE), NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowNtStatusError(hwndDlg, Status);
+        NtClose(h2);
+        CloseHandle(h);
+        return;
+    }
+
+    if (!sel->is_disk) {
+        Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0);
+        if (!NT_SUCCESS(Status))
+            ShowNtStatusError(hwndDlg, Status);
+
+        Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0);
+        if (!NT_SUCCESS(Status))
+            ShowNtStatusError(hwndDlg, Status);
+    }
+
+    NtClose(h2);
+    CloseHandle(h);
+
+    EndDialog(hwndDlg, 0);
+}
+
+INT_PTR CALLBACK BtrfsDeviceAdd::DeviceAddDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+            populate_device_tree(GetDlgItem(hwndDlg, IDC_DEVICE_TREE));
+            EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
+            break;
+        }
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                            AddDevice(hwndDlg);
+                        return TRUE;
+
+                        case IDCANCEL:
+                            EndDialog(hwndDlg, 0);
+                        return TRUE;
+                    }
+                break;
+            }
+        break;
+
+        case WM_NOTIFY:
+            switch (((LPNMHDR)lParam)->code) {
+                case TVN_SELCHANGEDW:
+                {
+                    NMTREEVIEWW* nmtv = (NMTREEVIEWW*)lParam;
+                    TVITEMW tvi;
+                    BOOL enable = FALSE;
+
+                    RtlZeroMemory(&tvi, sizeof(TVITEMW));
+                    tvi.hItem = nmtv->itemNew.hItem;
+                    tvi.mask = TVIF_PARAM | TVIF_HANDLE;
+
+                    if (SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_TREE), TVM_GETITEMW, 0, (LPARAM)&tvi))
+                        sel = tvi.lParam == 0 ? NULL : (device*)tvi.lParam;
+                    else
+                        sel = NULL;
+
+                    if (sel)
+                        enable = (!sel->is_disk || !sel->has_parts) && !sel->multi_device;
+
+                    EnableWindow(GetDlgItem(hwndDlg, IDOK), enable);
+                    break;
+                }
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_DeviceAddDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsDeviceAdd* bda;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bda = (BtrfsDeviceAdd*)lParam;
+    } else {
+        bda = (BtrfsDeviceAdd*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (bda)
+        return bda->DeviceAddDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsDeviceAdd::ShowDialog() {
+    DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DEVICE_ADD), hwnd, stub_DeviceAddDlgProc, (LPARAM)this);
+}
+
+BtrfsDeviceAdd::BtrfsDeviceAdd(HINSTANCE hinst, HWND hwnd, WCHAR* cmdline) {
+    this->hinst = hinst;
+    this->hwnd = hwnd;
+    this->cmdline = cmdline;
+
+    sel = NULL;
+}
+
+void BtrfsDeviceResize::do_resize(HWND hwndDlg) {
+    HANDLE h;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_resize br;
+
+    h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    br.device = dev_id;
+    br.size = new_size;
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESIZE, &br, sizeof(btrfs_resize), NULL, 0);
+
+    if (Status != STATUS_MORE_PROCESSING_REQUIRED && !NT_SUCCESS(Status)) {
+        ShowNtStatusError(hwndDlg, Status);
+        CloseHandle(h);
+        return;
+    }
+
+    CloseHandle(h);
+
+    if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
+        WCHAR s[255], t[255], u[255];
+
+        LoadStringW(module, IDS_RESIZE_SUCCESSFUL, s, sizeof(s) / sizeof(WCHAR));
+        format_size(new_size, u, sizeof(u) / sizeof(WCHAR), TRUE);
+        StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, dev_id, u);
+        MessageBoxW(hwndDlg, t, L"", MB_OK);
+
+        EndDialog(hwndDlg, 0);
+    } else {
+        BtrfsBalance* bb;
+        HWND par;
+
+        par = GetParent(hwndDlg);
+        EndDialog(hwndDlg, 0);
+
+        bb = new BtrfsBalance(fn, FALSE, TRUE);
+
+        bb->ShowBalance(par);
+
+        delete bb;
+    }
+}
+
+INT_PTR CALLBACK BtrfsDeviceResize::DeviceResizeDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            HANDLE h;
+            WCHAR s[255], t[255], u[255];
+
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+
+            GetDlgItemTextW(hwndDlg, IDC_RESIZE_DEVICE_ID, s, sizeof(s) / sizeof(WCHAR));
+            StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, dev_id);
+            SetDlgItemTextW(hwndDlg, IDC_RESIZE_DEVICE_ID, t);
+
+            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+            if (h != INVALID_HANDLE_VALUE) {
+                NTSTATUS Status;
+                IO_STATUS_BLOCK iosb;
+                btrfs_device *devices, *bd;
+                ULONG devsize;
+                BOOL found = FALSE;
+                HWND slider;
+
+                devsize = 1024;
+                devices = (btrfs_device*)malloc(devsize);
+
+                while (TRUE) {
+                    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_DEVICES, NULL, 0, devices, devsize);
+                    if (Status == STATUS_BUFFER_OVERFLOW) {
+                        devsize += 1024;
+
+                        free(devices);
+                        devices = (btrfs_device*)malloc(devsize);
+                    } else
+                        break;
+                }
+
+                if (!NT_SUCCESS(Status)) {
+                    free(devices);
+                    CloseHandle(h);
+                    return FALSE;
+                }
+
+                bd = devices;
+
+                while (TRUE) {
+                    if (bd->dev_id == dev_id) {
+                        memcpy(&dev_info, bd, sizeof(btrfs_device));
+                        found = TRUE;
+                        break;
+                    }
+
+                    if (bd->next_entry > 0)
+                        bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+                    else
+                        break;
+                }
+
+                if (!found) {
+                    free(devices);
+                    CloseHandle(h);
+                    return FALSE;
+                }
+
+                free(devices);
+                CloseHandle(h);
+
+                GetDlgItemTextW(hwndDlg, IDC_RESIZE_CURSIZE, s, sizeof(s) / sizeof(WCHAR));
+                format_size(dev_info.size, u, sizeof(u) / sizeof(WCHAR), TRUE);
+                StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, u);
+                SetDlgItemTextW(hwndDlg, IDC_RESIZE_CURSIZE, t);
+
+                new_size = dev_info.size;
+
+                GetDlgItemTextW(hwndDlg, IDC_RESIZE_NEWSIZE, new_size_text, sizeof(new_size_text) / sizeof(WCHAR));
+                StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), new_size_text, u);
+                SetDlgItemTextW(hwndDlg, IDC_RESIZE_NEWSIZE, t);
+
+                slider = GetDlgItem(hwndDlg, IDC_RESIZE_SLIDER);
+                SendMessageW(slider, TBM_SETRANGEMIN, FALSE, 0);
+                SendMessageW(slider, TBM_SETRANGEMAX, FALSE, dev_info.max_size / 1048576);
+                SendMessageW(slider, TBM_SETPOS, TRUE, new_size / 1048576);
+            } else
+                return FALSE;
+
+            break;
+        }
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                            do_resize(hwndDlg);
+                            return TRUE;
+
+                        case IDCANCEL:
+                            EndDialog(hwndDlg, 0);
+                            return TRUE;
+                    }
+                break;
+            }
+        break;
+
+        case WM_HSCROLL:
+        {
+            WCHAR t[255], u[255];
+
+            new_size = UInt32x32To64(SendMessageW(GetDlgItem(hwndDlg, IDC_RESIZE_SLIDER), TBM_GETPOS, 0, 0), 1048576);
+
+            format_size(new_size, u, sizeof(u) / sizeof(WCHAR), TRUE);
+            StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), new_size_text, u);
+            SetDlgItemTextW(hwndDlg, IDC_RESIZE_NEWSIZE, t);
+
+            EnableWindow(GetDlgItem(hwndDlg, IDOK), new_size > 0 ? TRUE : FALSE);
+
+            break;
+        }
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_DeviceResizeDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsDeviceResize* bdr;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bdr = (BtrfsDeviceResize*)lParam;
+    } else
+        bdr = (BtrfsDeviceResize*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+    if (bdr)
+        return bdr->DeviceResizeDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsDeviceResize::ShowDialog(HWND hwnd, WCHAR* fn, UINT64 dev_id) {
+    this->dev_id = dev_id;
+    wcscpy(this->fn, fn);
+
+    DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RESIZE), hwnd, stub_DeviceResizeDlgProc, (LPARAM)this);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void CALLBACK AddDeviceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    HANDLE token;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+    BtrfsDeviceAdd* bda;
+
+    set_dpi_aware();
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    bda = new BtrfsDeviceAdd(hinst, hwnd, lpszCmdLine);
+    bda->ShowDialog();
+    delete bda;
+
+end:
+    CloseHandle(token);
+}
+
+void CALLBACK RemoveDeviceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    WCHAR *s, *vol, *dev;
+    UINT64 devid;
+    HANDLE h, token;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    BtrfsBalance* bb;
+
+    set_dpi_aware();
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    s = wcsstr(lpszCmdLine, L"|");
+    if (!s)
+        goto end;
+
+    s[0] = 0;
+
+    vol = lpszCmdLine;
+    dev = &s[1];
+
+    devid = _wtoi(dev);
+    if (devid == 0)
+        goto end;
+
+    h = CreateFileW(vol, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_REMOVE_DEVICE, &devid, sizeof(UINT64), NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        if (Status == STATUS_CANNOT_DELETE)
+            ShowStringError(hwnd, IDS_CANNOT_REMOVE_RAID);
+        else
+            ShowNtStatusError(hwnd, Status);
+
+        CloseHandle(h);
+        goto end;
+    }
+
+    CloseHandle(h);
+
+    bb = new BtrfsBalance(vol, TRUE);
+
+    bb->ShowBalance(hwnd);
+
+    delete bb;
+
+end:
+    CloseHandle(token);
+}
+
+void CALLBACK ResizeDeviceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    WCHAR *s, *vol, *dev;
+    UINT64 devid;
+    HANDLE token;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+    BtrfsDeviceResize* bdr;
+
+    set_dpi_aware();
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    s = wcsstr(lpszCmdLine, L"|");
+    if (!s)
+        goto end;
+
+    s[0] = 0;
+
+    vol = lpszCmdLine;
+    dev = &s[1];
+
+    devid = _wtoi(dev);
+    if (devid == 0)
+        goto end;
+
+    bdr = new BtrfsDeviceResize;
+    bdr->ShowDialog(hwnd, vol, devid);
+    delete bdr;
+
+end:
+    CloseHandle(token);
+}
+
+#ifdef __cplusplus
+}
+#endif

Modification de propriétés sur dll/shellext/shellbtrfs/devices.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: dll/shellext/shellbtrfs/devices.h
===================================================================
--- dll/shellext/shellbtrfs/devices.h	(nonexistent)
+++ dll/shellext/shellbtrfs/devices.h	(copie de travail)
@@ -0,0 +1,167 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ *
+ * 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/>. */
+
+#ifndef __REACTOS__
+#include <windows.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <ndk/iofuncs.h>
+#include <ndk/iotypes.h>
+#endif
+#include <shlobj.h>
+#include <vector>
+#include <string>
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "btrfsioctl.h"
+#endif
+
+typedef struct {
+    std::wstring pnp_name;
+    std::wstring friendly_name;
+    std::wstring drive;
+    std::wstring fstype;
+    ULONG disk_num;
+    ULONG part_num;
+    UINT64 size;
+    BOOL has_parts;
+    BTRFS_UUID fs_uuid;
+    BTRFS_UUID dev_uuid;
+    BOOL ignore;
+    BOOL multi_device;
+    BOOL is_disk;
+} device;
+
+typedef struct {
+    const WCHAR* name;
+    const char* magic;
+    ULONG magiclen;
+    UINT32 sboff;
+    UINT32 kboff;
+} fs_identifier;
+
+// This list is compiled from information in libblkid, part of util-linux
+// and likewise under the LGPL. Thanks!
+
+const static fs_identifier fs_ident[] = {
+    { L"BeFS", "BFS1", 4, 0x20, 0 },
+    { L"BeFS", "1SFB", 4, 0x20, 0 },
+    { L"BeFS", "BFS1", 4, 0x220, 0 },
+    { L"BeFS", "1SFB", 4, 0x220, 0 },
+    { L"BFS", "\xce\xfa\xad\x1b", 4, 0, 0 },
+    { L"RomFS", "-rom1fs-", 8, 0, 0 },
+    { L"SquashFS", "hsqs", 4, 0, 0 },
+    { L"SquashFS", "sqsh", 4, 0, 0 },
+    { L"SquashFS", "hsqs", 4, 0, 0 },
+    { L"UBIFS", "\x31\x18\x10\x06", 4, 0, 0 },
+    { L"XFS", "XFSB", 4, 0, 0 },
+    { L"ext2", "\123\357", 2, 0x38, 1 },
+    { L"F2FS", "\x10\x20\xF5\xF2", 4, 0, 1 },
+    { L"HFS", "BD", 2, 0, 1 },
+    { L"HFS", "BD", 2, 0, 1 },
+    { L"HFS", "H+", 2, 0, 1 },
+    { L"HFS", "HX", 2, 0, 1 },
+    { L"Minix", "\177\023", 2, 0x10, 1 },
+    { L"Minix", "\217\023", 2, 0x10, 1 },
+    { L"Minix", "\023\177", 2, 0x10, 1 },
+    { L"Minix", "\023\217", 2, 0x10, 1 },
+    { L"Minix", "\150\044", 2, 0x10, 1 },
+    { L"Minix", "\170\044", 2, 0x10, 1 },
+    { L"Minix", "\044\150", 2, 0x10, 1 },
+    { L"Minix", "\044\170", 2, 0x10, 1 },
+    { L"Minix", "\132\115", 2, 0x10, 1 },
+    { L"Minix", "\115\132", 2, 0x10, 1 },
+    { L"OCFS", "OCFSV2", 6, 0, 1 },
+    { L"SysV", "\x2b\x55\x44", 3, 0x400, 1 },
+    { L"SysV", "\x44\x55\x2b", 3, 0x400, 1 },
+    { L"VXFS", "\365\374\001\245", 4, 0, 1 },
+    { L"OCFS", "OCFSV2", 6, 0, 2 },
+    { L"ExFAT", "EXFAT   ", 8, 3 },
+    { L"NTFS", "NTFS    ", 8, 3 },
+    { L"NetWare", "SPB5", 4, 0, 4 },
+    { L"OCFS", "OCFSV2", 6, 0, 4 },
+    { L"HPFS", "\x49\xe8\x95\xf9", 4, 0, 8 },
+    { L"OCFS", "OracleCFS", 9, 0, 8 },
+    { L"OCFS", "OCFSV2", 6, 0, 8 },
+    { L"ReFS", "\000\000\000ReFS\000", 8 },
+    { L"ReiserFS", "ReIsErFs",  8, 0x34, 8 },
+    { L"ReiserFS", "ReIsErFs",  8, 20,  8 },
+    { L"ISO9660", "CD001", 5, 1, 32, },
+    { L"ISO9660", "CDROM", 5, 9, 32, },
+    { L"JFS", "JFS1", 4, 0, 32 },
+    { L"OCFS", "ORCLDISK", 8, 32 },
+    { L"UDF", "BEA01", 5, 1, 32 },
+    { L"UDF", "BOOT2", 5, 1, 32 },
+    { L"UDF", "CD001", 5, 1, 32 },
+    { L"UDF", "CDW02", 5, 1, 32 },
+    { L"UDF", "NSR02", 5, 1, 32 },
+    { L"UDF", "NSR03", 5, 1, 32 },
+    { L"UDF", "TEA01", 5, 1, 32 },
+    { L"Btrfs", "_BHRfS_M", 8, 0x40, 64 },
+    { L"GFS", "\x01\x16\x19\x70", 4, 0, 64 },
+    { L"GFS", "\x01\x16\x19\x70", 4, 0, 64 },
+    { L"ReiserFS", "ReIsEr2Fs", 9, 0x34, 64 },
+    { L"ReiserFS", "ReIsEr3Fs", 9, 0x34, 64 },
+    { L"ReiserFS", "ReIsErFs",  8, 0x34, 64 },
+    { L"ReiserFS", "ReIsEr4", 7, 0, 64 },
+    { L"VMFS", "\x0d\xd0\x01\xc0", 4, 0, 1024 },
+    { L"VMFS", "\x5e\xf1\xab\x2f", 4, 0, 2048 },
+    { L"FAT", "MSWIN",    5, 0x52, 0 },
+    { L"FAT", "FAT32   ", 8, 0x52, 0 },
+    { L"FAT", "MSDOS",    5, 0x36, 0 },
+    { L"FAT", "FAT16   ", 8, 0x36, 0 },
+    { L"FAT", "FAT12   ", 8, 0x36, 0 },
+    { L"FAT", "FAT     ", 8, 0x36, 0 },
+    { L"FAT", "\353",     1, 0, 0 },
+    { L"FAT", "\351",     1, 0, 0},
+    { L"FAT", "\125\252", 2, 0x1fe, 0 },
+    { NULL, 0, 0, 0 }
+};
+
+class BtrfsDeviceAdd {
+public:
+    INT_PTR CALLBACK DeviceAddDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+    void ShowDialog();
+    void AddDevice(HWND hwndDlg);
+    BtrfsDeviceAdd(HINSTANCE hinst, HWND hwnd, WCHAR* cmdline);
+
+private:
+    void populate_device_tree(HWND tree);
+
+    HINSTANCE hinst;
+    HWND hwnd;
+    WCHAR* cmdline;
+    device* sel;
+    std::vector<device> device_list;
+};
+
+class BtrfsDeviceResize {
+public:
+    INT_PTR CALLBACK DeviceResizeDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+    void ShowDialog(HWND hwnd, WCHAR* fn, UINT64 dev_id);
+
+private:
+    void do_resize(HWND hwndDlg);
+
+    UINT64 dev_id, new_size;
+    WCHAR fn[MAX_PATH], new_size_text[255];
+    btrfs_device dev_info;
+};

Modification de propriétés sur dll/shellext/shellbtrfs/devices.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/factory.cpp
===================================================================
--- dll/shellext/shellbtrfs/factory.cpp	(révision 75772)
+++ dll/shellext/shellbtrfs/factory.cpp	(copie de travail)
@@ -1,27 +1,29 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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 "shellext.h"
 #include <windows.h>
 #include "factory.h"
 #include "iconoverlay.h"
 #include "contextmenu.h"
 #include "propsheet.h"
+#include "volpropsheet.h"
 
-HRESULT __stdcall Factory::QueryInterface(const IID& iid, void** ppv) {    
+HRESULT __stdcall Factory::QueryInterface(const IID& iid, void** ppv) {
     if (iid == IID_IUnknown || iid == IID_IClassFactory) {
         *ppv = static_cast<IClassFactory*>(this);
     } else {
@@ -52,31 +54,41 @@
                 return bio->QueryInterface(iid, ppv);
             }
             break;
-            
+
         case FactoryContextMenu:
             if (iid == IID_IUnknown || iid == IID_IContextMenu || iid == IID_IShellExtInit) {
                 BtrfsContextMenu* bcm = new BtrfsContextMenu;
                 if (!bcm)
                     return E_OUTOFMEMORY;
-                
+
                 return bcm->QueryInterface(iid, ppv);
             }
             break;
-            
+
         case FactoryPropSheet:
             if (iid == IID_IUnknown || iid == IID_IShellPropSheetExt || iid == IID_IShellExtInit) {
                 BtrfsPropSheet* bps = new BtrfsPropSheet;
                 if (!bps)
                     return E_OUTOFMEMORY;
-                
+
                 return bps->QueryInterface(iid, ppv);
             }
             break;
-            
+
+        case FactoryVolPropSheet:
+            if (iid == IID_IUnknown || iid == IID_IShellPropSheetExt || iid == IID_IShellExtInit) {
+                BtrfsVolPropSheet* bps = new BtrfsVolPropSheet;
+                if (!bps)
+                    return E_OUTOFMEMORY;
+
+                return bps->QueryInterface(iid, ppv);
+            }
+            break;
+
         default:
             break;
     }
-    
+
     *ppv = NULL;
     return E_NOINTERFACE;
 }
Index: dll/shellext/shellbtrfs/factory.h
===================================================================
--- dll/shellext/shellbtrfs/factory.h	(révision 75772)
+++ dll/shellext/shellbtrfs/factory.h	(copie de travail)
@@ -1,17 +1,17 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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/>. */
 
@@ -21,7 +21,8 @@
     FactoryUnknown,
     FactoryIconHandler,
     FactoryContextMenu,
-    FactoryPropSheet
+    FactoryPropSheet,
+    FactoryVolPropSheet
 } factory_type;
 
 class Factory : public IClassFactory {
@@ -37,9 +38,9 @@
     }
 
     // IUnknown
-    
+
     HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
-    
+
     ULONG __stdcall AddRef() {
         return InterlockedIncrement(&refcount);
     }
@@ -46,18 +47,18 @@
 
     ULONG __stdcall Release() {
         LONG rc = InterlockedDecrement(&refcount);
-        
+
         if (rc == 0)
             delete this;
-        
+
         return rc;
     }
 
     // IClassFactory
-    
+
     virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv);
     virtual HRESULT __stdcall LockServer(BOOL bLock);
-    
+
     factory_type type;
 
 private:
Index: dll/shellext/shellbtrfs/iconoverlay.cpp
===================================================================
--- dll/shellext/shellbtrfs/iconoverlay.cpp	(révision 75772)
+++ dll/shellext/shellbtrfs/iconoverlay.cpp	(copie de travail)
@@ -1,20 +1,21 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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 "shellext.h"
 #ifndef __REACTOS__
 #include <windows.h>
 #include <winternl.h>
@@ -28,25 +29,12 @@
 #ifndef __REACTOS__
 #include "../btrfsioctl.h"
 #else
-#include "../../drivers/filesystems/btrfs/btrfsioctl.h"
+#include "btrfsioctl.h"
 #endif
 
-#ifndef __REACTOS__
-#ifdef __cplusplus
-extern "C" {
-#endif
-NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
-#ifdef __cplusplus
-}
-#endif
-#define STATUS_SUCCESS 0
-#endif
-
-extern HMODULE module;
-
 HRESULT __stdcall BtrfsIconOverlay::QueryInterface(REFIID riid, void **ppObj) {
     if (riid == IID_IUnknown || riid == IID_IShellIconOverlayIdentifier) {
-        *ppObj = static_cast<IShellIconOverlayIdentifier*>(this); 
+        *ppObj = static_cast<IShellIconOverlayIdentifier*>(this);
         AddRef();
         return S_OK;
     }
@@ -57,22 +45,22 @@
 
 HRESULT __stdcall BtrfsIconOverlay::GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int* pIndex, DWORD* pdwFlags) {
     WCHAR dllpath[MAX_PATH];
-    
+
     GetModuleFileNameW(module, dllpath, sizeof(dllpath));
-    
-    if (cchMax < (int)wcslen(dllpath))
+
+    if ((size_t)cchMax < wcslen(dllpath))
         return E_INVALIDARG;
-    
+
     if (!pIndex)
         return E_INVALIDARG;
-    
+
     if (!pdwFlags)
         return E_INVALIDARG;
-    
+
     wcscpy(pwszIconFile, dllpath);
     *pIndex = 0;
     *pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
-    
+
     return S_OK;
 }
 
@@ -79,9 +67,9 @@
 HRESULT __stdcall BtrfsIconOverlay::GetPriority(int *pPriority) {
     if (!pPriority)
         return E_INVALIDARG;
-    
+
     *pPriority = 0;
-    
+
     return S_OK;
 }
 
@@ -90,20 +78,20 @@
     NTSTATUS Status;
     IO_STATUS_BLOCK iosb;
     btrfs_get_file_ids bgfi;
-    
+
     h = CreateFileW(pwszPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
-    
+
     if (h == INVALID_HANDLE_VALUE)
         return S_FALSE;
-    
+
     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
-    
-    if (Status != STATUS_SUCCESS) {
+
+    if (!NT_SUCCESS(Status)) {
         CloseHandle(h);
         return S_FALSE;
     }
 
     CloseHandle(h);
-    
+
     return (bgfi.inode == 0x100 && !bgfi.top) ? S_OK : S_FALSE;
 }
Index: dll/shellext/shellbtrfs/iconoverlay.h
===================================================================
--- dll/shellext/shellbtrfs/iconoverlay.h	(révision 75772)
+++ dll/shellext/shellbtrfs/iconoverlay.h	(copie de travail)
@@ -1,17 +1,17 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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/>. */
 
@@ -31,9 +31,9 @@
     }
 
     // IUnknown
-    
+
     HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
-    
+
     ULONG __stdcall AddRef() {
         return InterlockedIncrement(&refcount);
     }
@@ -40,15 +40,15 @@
 
     ULONG __stdcall Release() {
         LONG rc = InterlockedDecrement(&refcount);
-        
+
         if (rc == 0)
             delete this;
-        
+
         return rc;
     }
 
     // IShellIconOverlayIdentifier
-    
+
     virtual HRESULT __stdcall GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int* pIndex, DWORD* pdwFlags);
     virtual HRESULT __stdcall GetPriority(int *pPriority);
     virtual HRESULT __stdcall IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib);
Index: dll/shellext/shellbtrfs/main.cpp
===================================================================
--- dll/shellext/shellbtrfs/main.cpp	(révision 75772)
+++ dll/shellext/shellbtrfs/main.cpp	(copie de travail)
@@ -1,40 +1,284 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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/>. */
 
-#ifndef __REACTOS__
-#define _WIN32_WINNT 0x0600
-#endif
-
+#include "shellext.h"
 #include <windows.h>
 #include <commctrl.h>
+#include <strsafe.h>
+#include <stddef.h>
 #include "factory.h"
+#include "resource.h"
 
 static const GUID CLSID_ShellBtrfsIconHandler = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf0 } };
 static const GUID CLSID_ShellBtrfsContextMenu = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf1 } };
 static const GUID CLSID_ShellBtrfsPropSheet = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf2 } };
+static const GUID CLSID_ShellBtrfsVolPropSheet = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf3 } };
 
 #define COM_DESCRIPTION_ICON_HANDLER L"WinBtrfs shell extension (icon handler)"
 #define COM_DESCRIPTION_CONTEXT_MENU L"WinBtrfs shell extension (context menu)"
 #define COM_DESCRIPTION_PROP_SHEET L"WinBtrfs shell extension (property sheet)"
+#define COM_DESCRIPTION_VOL_PROP_SHEET L"WinBtrfs shell extension (volume property sheet)"
 #define ICON_OVERLAY_NAME L"WinBtrfs"
 
+typedef enum _PROCESS_DPI_AWARENESS {
+    PROCESS_DPI_UNAWARE,
+    PROCESS_SYSTEM_DPI_AWARE,
+    PROCESS_PER_MONITOR_DPI_AWARE
+} PROCESS_DPI_AWARENESS;
+
+typedef ULONG (WINAPI *_RtlNtStatusToDosError)(NTSTATUS Status);
+typedef HRESULT (WINAPI *_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS value);
+
 HMODULE module;
 LONG objs_loaded = 0;
 
+void ShowError(HWND hwnd, ULONG err) {
+    WCHAR* buf;
+
+    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+                       err, 0, (WCHAR*)&buf, 0, NULL) == 0) {
+        MessageBoxW(hwnd, L"FormatMessage failed", L"Error", MB_ICONERROR);
+        return;
+    }
+
+    MessageBoxW(hwnd, buf, L"Error", MB_ICONERROR);
+
+    LocalFree(buf);
+}
+
+void ShowStringError(HWND hwndDlg, int num, ...) {
+    WCHAR title[255], s[1024], t[1024];
+    va_list ap;
+
+    if (!LoadStringW(module, IDS_ERROR, title, sizeof(title) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    if (!LoadStringW(module, num, s, sizeof(s) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    va_start(ap, num);
+#ifndef __REACTOS__
+    vswprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
+#else
+    vsnwprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
+#endif
+
+    MessageBoxW(hwndDlg, t, title, MB_ICONERROR);
+
+    va_end(ap);
+}
+
+void ShowNtStatusError(HWND hwnd, NTSTATUS Status) {
+    _RtlNtStatusToDosError RtlNtStatusToDosError;
+    HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
+
+    if (!ntdll) {
+        MessageBoxW(hwnd, L"Error loading ntdll.dll", L"Error", MB_ICONERROR);
+        return;
+    }
+
+    RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(ntdll, "RtlNtStatusToDosError");
+
+    if (!RtlNtStatusToDosError) {
+        MessageBoxW(hwnd, L"Error loading RtlNtStatusToDosError in ntdll.dll", L"Error", MB_ICONERROR);
+        FreeLibrary(ntdll);
+        return;
+    }
+
+    ShowError(hwnd, RtlNtStatusToDosError(Status));
+
+    FreeLibrary(ntdll);
+}
+
+void set_dpi_aware() {
+    _SetProcessDpiAwareness SetProcessDpiAwareness;
+    HMODULE shcore = LoadLibraryW(L"shcore.dll");
+
+    if (!shcore)
+        return;
+
+    SetProcessDpiAwareness = (_SetProcessDpiAwareness)GetProcAddress(shcore, "SetProcessDpiAwareness");
+
+    if (!SetProcessDpiAwareness)
+        return;
+
+    SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
+}
+
+void format_size(UINT64 size, WCHAR* s, ULONG len, BOOL show_bytes) {
+    WCHAR nb[255], nb2[255], t[255], bytes[255];
+    WCHAR kb[255];
+    ULONG sr;
+    float f;
+    NUMBERFMTW fmt;
+    WCHAR thou[4], grouping[64], *c;
+
+    _i64tow(size, nb, 10);
+
+    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thou, sizeof(thou) / sizeof(WCHAR));
+
+    fmt.NumDigits = 0;
+    fmt.LeadingZero = 1;
+    fmt.lpDecimalSep = (LPWSTR)L"."; // not used
+    fmt.lpThousandSep = thou;
+    fmt.NegativeOrder = 0;
+
+    // Grouping code copied from dlls/shlwapi/string.c in Wine - thank you
+
+    fmt.Grouping = 0;
+    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping) / sizeof(WCHAR));
+
+    c = grouping;
+    while (*c) {
+        if (*c >= '0' && *c < '9') {
+            fmt.Grouping *= 10;
+            fmt.Grouping += *c - '0';
+        }
+
+        c++;
+    }
+
+    if (fmt.Grouping % 10 == 0)
+        fmt.Grouping /= 10;
+    else
+        fmt.Grouping *= 10;
+
+    GetNumberFormatW(LOCALE_USER_DEFAULT, 0, nb, &fmt, nb2, sizeof(nb2) / sizeof(WCHAR));
+
+    if (size < 1024) {
+        if (!LoadStringW(module, size == 1 ? IDS_SIZE_BYTE : IDS_SIZE_BYTES, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(NULL, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(s, len, t, nb2) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+            ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+            return;
+        }
+
+        return;
+    }
+
+    if (show_bytes) {
+        if (!LoadStringW(module, IDS_SIZE_BYTES, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(NULL, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(bytes, sizeof(bytes) / sizeof(WCHAR), t, nb2) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+            ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+            return;
+        }
+    }
+
+    if (size >= 1152921504606846976) {
+        sr = IDS_SIZE_EB;
+        f = (float)size / 1152921504606846976.0f;
+    } else if (size >= 1125899906842624) {
+        sr = IDS_SIZE_PB;
+        f = (float)size / 1125899906842624.0f;
+    } else if (size >= 1099511627776) {
+        sr = IDS_SIZE_TB;
+        f = (float)size / 1099511627776.0f;
+    } else if (size >= 1073741824) {
+        sr = IDS_SIZE_GB;
+        f = (float)size / 1073741824.0f;
+    } else if (size >= 1048576) {
+        sr = IDS_SIZE_MB;
+        f = (float)size / 1048576.0f;
+    } else {
+        sr = IDS_SIZE_KB;
+        f = (float)size / 1024.0f;
+    }
+
+    if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
+        ShowError(NULL, GetLastError());
+        return;
+    }
+
+    if (show_bytes) {
+        if (StringCchPrintfW(kb, sizeof(kb) / sizeof(WCHAR), t, f) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+            ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+            return;
+        }
+
+        if (!LoadStringW(module, IDS_SIZE_LARGE, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(NULL, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(s, len, t, kb, bytes) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+            ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+            return;
+        }
+    } else {
+        if (StringCchPrintfW(s, len, t, f) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+            ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+            return;
+        }
+    }
+}
+
+std::wstring format_message(ULONG last_error) {
+    WCHAR* buf;
+    std::wstring s;
+
+    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+        last_error, 0, (WCHAR*)&buf, 0, NULL) == 0) {
+        return L"(error retrieving message)";
+    }
+
+    s = buf;
+
+    LocalFree(buf);
+
+    // remove trailing newline
+    while (s.length() > 0 && (s.substr(s.length() - 1, 1) == L"\r" || s.substr(s.length() - 1, 1) == L"\n"))
+        s = s.substr(0, s.length() - 1);
+
+    return s;
+}
+
+std::wstring format_ntstatus(NTSTATUS Status) {
+    _RtlNtStatusToDosError RtlNtStatusToDosError;
+    std::wstring s;
+    HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
+
+    if (!ntdll)
+        return L"(error loading ntdll.dll)";
+
+    RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(ntdll, "RtlNtStatusToDosError");
+
+    if (!RtlNtStatusToDosError) {
+        FreeLibrary(ntdll);
+        return L"(error loading RtlNtStatusToDosError)";
+    }
+
+    s = format_message(RtlNtStatusToDosError(Status));
+
+    FreeLibrary(ntdll);
+
+    return s;
+}
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -50,7 +294,7 @@
             return E_OUTOFMEMORY;
         else {
             fact->type = FactoryIconHandler;
-            
+
             return fact->QueryInterface(riid, ppv);
         }
     } else if (rclsid == CLSID_ShellBtrfsContextMenu) {
@@ -59,7 +303,7 @@
             return E_OUTOFMEMORY;
         else {
             fact->type = FactoryContextMenu;
-            
+
             return fact->QueryInterface(riid, ppv);
         }
     } else if (rclsid == CLSID_ShellBtrfsPropSheet) {
@@ -68,9 +312,18 @@
             return E_OUTOFMEMORY;
         else {
             fact->type = FactoryPropSheet;
-            
+
             return fact->QueryInterface(riid, ppv);
         }
+    } else if (rclsid == CLSID_ShellBtrfsVolPropSheet) {
+        Factory* fact = new Factory;
+        if (!fact)
+            return E_OUTOFMEMORY;
+        else {
+            fact->type = FactoryVolPropSheet;
+
+            return fact->QueryInterface(riid, ppv);
+        }
     }
 
     return CLASS_E_CLASSNOTAVAILABLE;
@@ -80,34 +333,34 @@
     LONG l;
     HKEY hk;
     DWORD dispos;
-   
-    l = RegCreateKeyExW(root, keyname, NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, &dispos);
+
+    l = RegCreateKeyExW(root, keyname, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, &dispos);
     if (l != ERROR_SUCCESS) {
         WCHAR s[255];
         wsprintfW(s, L"RegCreateKey returned %08x", l);
         MessageBoxW(0, s, NULL, MB_ICONERROR);
-        
+
         return FALSE;
     }
 
-    l = RegSetValueExW(hk, val, NULL, type, data, datasize);
+    l = RegSetValueExW(hk, val, 0, type, data, datasize);
     if (l != ERROR_SUCCESS) {
         WCHAR s[255];
         wsprintfW(s, L"RegSetValueEx returned %08x", l);
         MessageBoxW(0, s, NULL, MB_ICONERROR);
-        
+
         return FALSE;
     }
-    
+
     l = RegCloseKey(hk);
     if (l != ERROR_SUCCESS) {
         WCHAR s[255];
         wsprintfW(s, L"RegCloseKey returned %08x", l);
         MessageBoxW(0, s, NULL, MB_ICONERROR);
-        
+
         return FALSE;
     }
-    
+
     return TRUE;
 }
 
@@ -115,26 +368,26 @@
     WCHAR* clsidstring;
     WCHAR inproc[MAX_PATH], progid[MAX_PATH], clsidkeyname[MAX_PATH], dllpath[MAX_PATH];
     BOOL ret = FALSE;
-    
+
     StringFromCLSID(clsid, &clsidstring);
-    
+
     wsprintfW(inproc, L"CLSID\\%s\\InprocServer32", clsidstring);
     wsprintfW(progid, L"CLSID\\%s\\ProgId", clsidstring);
     wsprintfW(clsidkeyname, L"CLSID\\%s", clsidstring);
-    
+
     if (!write_reg_key(HKEY_CLASSES_ROOT, clsidkeyname, NULL, REG_SZ, (BYTE*)description, (wcslen(description) + 1) * sizeof(WCHAR)))
         goto end;
-    
+
     GetModuleFileNameW(module, dllpath, sizeof(dllpath));
-    
+
     if (!write_reg_key(HKEY_CLASSES_ROOT, inproc, NULL, REG_SZ, (BYTE*)dllpath, (wcslen(dllpath) + 1) * sizeof(WCHAR)))
         goto end;
-    
+
     if (!write_reg_key(HKEY_CLASSES_ROOT, inproc, L"ThreadingModel", REG_SZ, (BYTE*)L"Apartment", (wcslen(L"Apartment") + 1) * sizeof(WCHAR)))
         goto end;
-    
+
     ret = TRUE;
-    
+
 end:
     CoTaskMemFree(clsidstring);
 
@@ -146,21 +399,21 @@
     WCHAR clsidkeyname[MAX_PATH];
     BOOL ret = FALSE;
     LONG l;
-    
+
     StringFromCLSID(clsid, &clsidstring);
     wsprintfW(clsidkeyname, L"CLSID\\%s", clsidstring);
-    
+
     l = RegDeleteTreeW(HKEY_CLASSES_ROOT, clsidkeyname);
-    
+
     if (l != ERROR_SUCCESS) {
         WCHAR s[255];
         wsprintfW(s, L"RegDeleteTree returned %08x", l);
         MessageBoxW(0, s, NULL, MB_ICONERROR);
-        
+
         ret = FALSE;
-    } else    
+    } else
         ret = TRUE;
-    
+
     CoTaskMemFree(clsidstring);
 
     return ret;
@@ -170,17 +423,17 @@
     WCHAR path[MAX_PATH];
     WCHAR* clsidstring;
     BOOL ret = FALSE;
-    
+
     StringFromCLSID(clsid, &clsidstring);
-    
+
     wcscpy(path, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\");
     wcscat(path, name);
-    
+
     if (!write_reg_key(HKEY_LOCAL_MACHINE, path, NULL, REG_SZ, (BYTE*)clsidstring, (wcslen(clsidstring) + 1) * sizeof(WCHAR)))
         goto end;
 
     ret = TRUE;
-    
+
 end:
     CoTaskMemFree(clsidstring);
 
@@ -190,19 +443,19 @@
 static BOOL unreg_icon_overlay(const WCHAR* name) {
     WCHAR path[MAX_PATH];
     LONG l;
-    
+
     wcscpy(path, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\");
     wcscat(path, name);
-    
+
     l = RegDeleteTreeW(HKEY_LOCAL_MACHINE, path);
-    
+
     if (l != ERROR_SUCCESS) {
         WCHAR s[255];
         wsprintfW(s, L"RegDeleteTree returned %08x", l);
         MessageBoxW(0, s, NULL, MB_ICONERROR);
-        
+
         return FALSE;
-    } else    
+    } else
         return TRUE;
 }
 
@@ -210,18 +463,18 @@
     WCHAR path[MAX_PATH];
     WCHAR* clsidstring;
     BOOL ret = FALSE;
-    
+
     StringFromCLSID(clsid, &clsidstring);
-    
+
     wcscpy(path, filetype);
     wcscat(path, L"\\ShellEx\\ContextMenuHandlers\\");
     wcscat(path, name);
-    
+
     if (!write_reg_key(HKEY_CLASSES_ROOT, path, NULL, REG_SZ, (BYTE*)clsidstring, (wcslen(clsidstring) + 1) * sizeof(WCHAR)))
         goto end;
 
     ret = TRUE;
-    
+
 end:
     CoTaskMemFree(clsidstring);
 
@@ -231,20 +484,20 @@
 static BOOL unreg_context_menu_handler(const WCHAR* filetype, const WCHAR* name) {
     WCHAR path[MAX_PATH];
     LONG l;
-    
+
     wcscpy(path, filetype);
     wcscat(path, L"\\ShellEx\\ContextMenuHandlers\\");
     wcscat(path, name);
-    
+
     l = RegDeleteTreeW(HKEY_CLASSES_ROOT, path);
-    
+
     if (l != ERROR_SUCCESS) {
         WCHAR s[255];
         wsprintfW(s, L"RegDeleteTree returned %08x", l);
         MessageBoxW(0, s, NULL, MB_ICONERROR);
-        
+
         return FALSE;
-    } else    
+    } else
         return TRUE;
 }
 
@@ -252,18 +505,18 @@
     WCHAR path[MAX_PATH];
     WCHAR* clsidstring;
     BOOL ret = FALSE;
-    
+
     StringFromCLSID(clsid, &clsidstring);
-    
+
     wcscpy(path, filetype);
     wcscat(path, L"\\ShellEx\\PropertySheetHandlers\\");
     wcscat(path, name);
-    
+
     if (!write_reg_key(HKEY_CLASSES_ROOT, path, NULL, REG_SZ, (BYTE*)clsidstring, (wcslen(clsidstring) + 1) * sizeof(WCHAR)))
         goto end;
 
     ret = TRUE;
-    
+
 end:
     CoTaskMemFree(clsidstring);
 
@@ -273,20 +526,20 @@
 static BOOL unreg_prop_sheet_handler(const WCHAR* filetype, const WCHAR* name) {
     WCHAR path[MAX_PATH];
     LONG l;
-    
+
     wcscpy(path, filetype);
     wcscat(path, L"\\ShellEx\\PropertySheetHandlers\\");
     wcscat(path, name);
-    
+
     l = RegDeleteTreeW(HKEY_CLASSES_ROOT, path);
-    
+
     if (l != ERROR_SUCCESS) {
         WCHAR s[255];
         wsprintfW(s, L"RegDeleteTree returned %08x", l);
         MessageBoxW(0, s, NULL, MB_ICONERROR);
-        
+
         return FALSE;
-    } else    
+    } else
         return TRUE;
 }
 
@@ -293,38 +546,46 @@
 STDAPI DllRegisterServer(void) {
     if (!register_clsid(CLSID_ShellBtrfsIconHandler, COM_DESCRIPTION_ICON_HANDLER))
         return E_FAIL;
-    
+
     if (!register_clsid(CLSID_ShellBtrfsContextMenu, COM_DESCRIPTION_CONTEXT_MENU))
         return E_FAIL;
-    
+
     if (!register_clsid(CLSID_ShellBtrfsPropSheet, COM_DESCRIPTION_PROP_SHEET))
         return E_FAIL;
-    
+
+    if (!register_clsid(CLSID_ShellBtrfsVolPropSheet, COM_DESCRIPTION_VOL_PROP_SHEET))
+        return E_FAIL;
+
     if (!reg_icon_overlay(CLSID_ShellBtrfsIconHandler, ICON_OVERLAY_NAME)) {
         MessageBoxW(0, L"Failed to register icon overlay.", NULL, MB_ICONERROR);
         return E_FAIL;
     }
-    
+
     if (!reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Directory\\Background", ICON_OVERLAY_NAME)) {
         MessageBoxW(0, L"Failed to register context menu handler.", NULL, MB_ICONERROR);
         return E_FAIL;
     }
-    
+
     if (!reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Folder", ICON_OVERLAY_NAME)) {
         MessageBoxW(0, L"Failed to register context menu handler.", NULL, MB_ICONERROR);
         return E_FAIL;
     }
-    
+
     if (!reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"Folder", ICON_OVERLAY_NAME)) {
         MessageBoxW(0, L"Failed to register property sheet handler.", NULL, MB_ICONERROR);
         return E_FAIL;
     }
-    
+
     if (!reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"*", ICON_OVERLAY_NAME)) {
         MessageBoxW(0, L"Failed to register property sheet handler.", NULL, MB_ICONERROR);
         return E_FAIL;
     }
-    
+
+    if (!reg_prop_sheet_handler(CLSID_ShellBtrfsVolPropSheet, L"Drive", ICON_OVERLAY_NAME)) {
+        MessageBoxW(0, L"Failed to register volume property sheet handler.", NULL, MB_ICONERROR);
+        return E_FAIL;
+    }
+
     return S_OK;
 }
 
@@ -331,16 +592,20 @@
 STDAPI DllUnregisterServer(void) {
     unreg_prop_sheet_handler(L"Folder", ICON_OVERLAY_NAME);
     unreg_prop_sheet_handler(L"*", ICON_OVERLAY_NAME);
+    unreg_prop_sheet_handler(L"Drive", ICON_OVERLAY_NAME);
     unreg_context_menu_handler(L"Folder", ICON_OVERLAY_NAME);
     unreg_context_menu_handler(L"Directory\\Background", ICON_OVERLAY_NAME);
     unreg_icon_overlay(ICON_OVERLAY_NAME);
-    
+
+    if (!unregister_clsid(CLSID_ShellBtrfsVolPropSheet))
+        return E_FAIL;
+
     if (!unregister_clsid(CLSID_ShellBtrfsPropSheet))
         return E_FAIL;
-    
+
     if (!unregister_clsid(CLSID_ShellBtrfsContextMenu))
         return E_FAIL;
-    
+
     if (!unregister_clsid(CLSID_ShellBtrfsIconHandler))
         return E_FAIL;
 
@@ -357,11 +622,118 @@
 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) {
     if (dwReason == DLL_PROCESS_ATTACH)
         module = (HMODULE)hModule;
-        
+
     return TRUE;
 }
 
+static void create_subvol(std::wstring fn) {
+    size_t found = fn.rfind(L"\\");
+    std::wstring path, file;
+    HANDLE h;
+    ULONG bcslen;
+    btrfs_create_subvol* bcs;
+    IO_STATUS_BLOCK iosb;
 
+    if (found == std::wstring::npos) {
+        path = L"";
+        file = fn;
+    } else {
+        path = fn.substr(0, found);
+        file = fn.substr(found + 1);
+    }
+    path += L"\\";
+
+    h = CreateFileW(path.c_str(), FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+    if (h == INVALID_HANDLE_VALUE)
+        return;
+
+    bcslen = offsetof(btrfs_create_subvol, name[0]) + (file.length() * sizeof(WCHAR));
+    bcs = (btrfs_create_subvol*)malloc(bcslen);
+
+    bcs->readonly = FALSE;
+    bcs->posix = FALSE;
+    bcs->namelen = file.length() * sizeof(WCHAR);
+    memcpy(bcs->name, file.c_str(), bcs->namelen);
+
+    NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, NULL, 0);
+
+    CloseHandle(h);
+}
+
+void CALLBACK CreateSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    LPWSTR* args;
+    int num_args;
+
+    args = CommandLineToArgvW(lpszCmdLine, &num_args);
+
+    if (!args)
+        return;
+
+    if (num_args >= 1)
+        create_subvol(args[0]);
+
+    LocalFree(args);
+}
+
+static void create_snapshot2(std::wstring source, std::wstring fn) {
+    size_t found = fn.rfind(L"\\");
+    std::wstring path, file;
+    HANDLE h, src;
+    ULONG bcslen;
+    btrfs_create_snapshot* bcs;
+    IO_STATUS_BLOCK iosb;
+
+    if (found == std::wstring::npos) {
+        path = L"";
+        file = fn;
+    } else {
+        path = fn.substr(0, found);
+        file = fn.substr(found + 1);
+    }
+    path += L"\\";
+
+    src = CreateFileW(source.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    if (src == INVALID_HANDLE_VALUE)
+        return;
+
+    h = CreateFileW(path.c_str(), FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        CloseHandle(src);
+        return;
+    }
+
+    bcslen = offsetof(btrfs_create_snapshot, name[0]) + (file.length() * sizeof(WCHAR));
+    bcs = (btrfs_create_snapshot*)malloc(bcslen);
+
+    bcs->readonly = FALSE;
+    bcs->posix = FALSE;
+    bcs->namelen = file.length() * sizeof(WCHAR);
+    memcpy(bcs->name, file.c_str(), bcs->namelen);
+    bcs->subvol = src;
+
+    NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, bcslen, NULL, 0);
+
+    CloseHandle(h);
+    CloseHandle(src);
+}
+
+void CALLBACK CreateSnapshotW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    LPWSTR* args;
+    int num_args;
+
+    args = CommandLineToArgvW(lpszCmdLine, &num_args);
+
+    if (!args)
+        return;
+
+    if (num_args >= 2)
+        create_snapshot2(args[0], args[1]);
+
+    LocalFree(args);
+}
+
 #ifdef __cplusplus
 }
 #endif
Index: dll/shellext/shellbtrfs/propsheet.cpp
===================================================================
--- dll/shellext/shellbtrfs/propsheet.cpp	(révision 75772)
+++ dll/shellext/shellbtrfs/propsheet.cpp	(copie de travail)
@@ -1,17 +1,17 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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/>. */
 
@@ -18,6 +18,7 @@
 #define ISOLATION_AWARE_ENABLED 1
 #define STRSAFE_NO_DEPRECATE
 
+#include "shellext.h"
 #ifndef __REACTOS__
 #include <windows.h>
 #include <strsafe.h>
@@ -28,6 +29,7 @@
 #include <winbase.h>
 #include <strsafe.h>
 #include <ndk/iofuncs.h>
+#include <ndk/iotypes.h>
 #endif
 
 #define NO_SHLWAPI_STRFCNS
@@ -37,40 +39,25 @@
 #include "propsheet.h"
 #include "resource.h"
 
-#ifndef __REACTOS__
-// FIXME - is there a way to link to the proper header files without breaking everything?
-#ifdef __cplusplus
-extern "C" {
-#endif
-NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
-#ifdef __cplusplus
-}
-#endif
+#define SUBVOL_ROOT_INODE 0x100
 
-#define STATUS_SUCCESS          (NTSTATUS)0x00000000
-#endif
+#ifndef __MINGW32__ // in winternl.h in mingw
 
-#define BTRFS_TYPE_FILE      1
-#define BTRFS_TYPE_DIRECTORY 2
-#define BTRFS_TYPE_CHARDEV   3
-#define BTRFS_TYPE_BLOCKDEV  4
-#define BTRFS_TYPE_FIFO      5
-#define BTRFS_TYPE_SOCKET    6
-#define BTRFS_TYPE_SYMLINK   7
+typedef struct _FILE_ACCESS_INFORMATION {
+    ACCESS_MASK AccessFlags;
+} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
 
-extern HMODULE module;
+#define FileAccessInformation (FILE_INFORMATION_CLASS)8
 
-extern void ShowNtStatusError(HWND hwnd, NTSTATUS Status);
-static void format_size(UINT64 size, WCHAR* s, ULONG len);
-static void ShowError(HWND hwnd, ULONG err);
+#endif
 
 HRESULT __stdcall BtrfsPropSheet::QueryInterface(REFIID riid, void **ppObj) {
     if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
-        *ppObj = static_cast<IShellPropSheetExt*>(this); 
+        *ppObj = static_cast<IShellPropSheetExt*>(this);
         AddRef();
         return S_OK;
     } else if (riid == IID_IShellExtInit) {
-        *ppObj = static_cast<IShellExtInit*>(this); 
+        *ppObj = static_cast<IShellExtInit*>(this);
         AddRef();
         return S_OK;
     }
@@ -81,13 +68,13 @@
 
 void BtrfsPropSheet::add_to_search_list(WCHAR* fn) {
     WCHAR* s;
-    
+
     s = (WCHAR*)malloc((wcslen(fn) + 1) * sizeof(WCHAR));
     if (!s)
         return;
-    
+
     memcpy(s, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
-    
+
     search_list.push_back(s);
 }
 
@@ -95,42 +82,42 @@
     HANDLE h;
     WCHAR* ss;
     WIN32_FIND_DATAW ffd;
-    
+
     ss = (WCHAR*)malloc((wcslen(fn) + 3) * sizeof(WCHAR));
     if (!ss)
         return;
-    
+
     memcpy(ss, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
     wcscat(ss, L"\\*");
-    
+
     h = FindFirstFileW(ss, &ffd);
     if (h == INVALID_HANDLE_VALUE)
         return;
-    
+
     do {
         if (ffd.cFileName[0] != '.' || ((ffd.cFileName[1] != 0) && (ffd.cFileName[1] != '.' || ffd.cFileName[2] != 0))) {
             WCHAR* fn2 = (WCHAR*)malloc((wcslen(fn) + 1 + wcslen(ffd.cFileName) + 1) * sizeof(WCHAR));
-                
+
             memcpy(fn2, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
             wcscat(fn2, L"\\");
             wcscat(fn2, ffd.cFileName);
-            
+
             if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                 add_to_search_list(fn2);
             } else {
                 HANDLE fh;
-                
+
                 fh = CreateFileW(fn2, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
                                  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
-                
+
                 if (fh != INVALID_HANDLE_VALUE) {
                     NTSTATUS Status;
                     IO_STATUS_BLOCK iosb;
                     btrfs_inode_info bii2;
-                    
+
                     Status = NtFsControlFile(fh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
-                
-                    if (Status == STATUS_SUCCESS) {
+
+                    if (NT_SUCCESS(Status)) {
                         sizes[0] += bii2.inline_length;
                         sizes[1] += bii2.disk_size[0];
                         sizes[2] += bii2.disk_size[1];
@@ -137,15 +124,15 @@
                         sizes[3] += bii2.disk_size[2];
                         totalsize += bii2.inline_length + bii2.disk_size[0] + bii2.disk_size[1] + bii2.disk_size[2];
                     }
-                    
+
                     CloseHandle(fh);
                 }
-                
+
                 free(fn2);
             }
         }
     } while (FindNextFileW(h, &ffd));
-    
+
     FindClose(h);
 }
 
@@ -152,233 +139,372 @@
 DWORD BtrfsPropSheet::search_list_thread() {
     while (!search_list.empty()) {
         WCHAR* fn = search_list.front();
-        
+
         do_search(fn);
-        
+
         search_list.pop_front();
         free(fn);
     }
-    
+
     thread = NULL;
-    
+
     return 0;
 }
 
 static DWORD WINAPI global_search_list_thread(LPVOID lpParameter) {
     BtrfsPropSheet* bps = (BtrfsPropSheet*)lpParameter;
-    
+
     return bps->search_list_thread();
 }
 
-HRESULT __stdcall BtrfsPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
+HRESULT BtrfsPropSheet::check_file(std::wstring fn, UINT i, UINT num_files, UINT* sv) {
     HANDLE h;
     IO_STATUS_BLOCK iosb;
     NTSTATUS Status;
+    FILE_ACCESS_INFORMATION fai;
+    BY_HANDLE_FILE_INFORMATION bhfi;
+    btrfs_inode_info bii2;
+
+    h = CreateFileW(fn.c_str(), MAXIMUM_ALLOWED, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE)
+        return E_FAIL;
+
+    Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
+    if (!NT_SUCCESS(Status)) {
+        CloseHandle(h);
+        return E_FAIL;
+    }
+
+    if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
+        can_change_perms = fai.AccessFlags & WRITE_DAC;
+
+    readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
+
+    if (!readonly && num_files == 1 && !can_change_perms)
+        show_admin_button = TRUE;
+
+    if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        add_to_search_list((WCHAR*)fn.c_str());
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
+
+    if (NT_SUCCESS(Status) && !bii2.top) {
+        int j;
+
+        LARGE_INTEGER filesize;
+
+        if (i == 0) {
+            subvol = bii2.subvol;
+            inode = bii2.inode;
+            type = bii2.type;
+            uid = bii2.st_uid;
+            gid = bii2.st_gid;
+            rdev = bii2.st_rdev;
+        } else {
+            if (subvol != bii2.subvol)
+                various_subvols = TRUE;
+
+            if (inode != bii2.inode)
+                various_inodes = TRUE;
+
+            if (type != bii2.type)
+                various_types = TRUE;
+
+            if (uid != bii2.st_uid)
+                various_uids = TRUE;
+
+            if (gid != bii2.st_gid)
+                various_gids = TRUE;
+        }
+
+        if (bii2.inline_length > 0) {
+            totalsize += bii2.inline_length;
+            sizes[0] += bii2.inline_length;
+        }
+
+        for (j = 0; j < 3; j++) {
+            if (bii2.disk_size[j] > 0) {
+                totalsize += bii2.disk_size[j];
+                sizes[j + 1] += bii2.disk_size[j];
+            }
+        }
+
+        min_mode |= ~bii2.st_mode;
+        max_mode |= bii2.st_mode;
+        min_flags |= ~bii2.flags;
+        max_flags |= bii2.flags;
+        min_compression_type = bii2.compression_type < min_compression_type ? bii2.compression_type : min_compression_type;
+        max_compression_type = bii2.compression_type > max_compression_type ? bii2.compression_type : max_compression_type;
+
+        if (bii2.inode == SUBVOL_ROOT_INODE) {
+            BOOL ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
+
+            has_subvols = TRUE;
+
+            if (*sv == 0)
+                ro_subvol = ro;
+            else {
+                if (ro_subvol != ro)
+                    various_ro = TRUE;
+            }
+
+            (*sv)++;
+        }
+
+        ignore = FALSE;
+
+        if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
+            if (filesize.QuadPart != 0)
+                can_change_nocow = FALSE;
+        }
+
+        CloseHandle(h);
+    } else {
+        CloseHandle(h);
+        return E_FAIL;
+    }
+
+    return S_OK;
+}
+
+HRESULT BtrfsPropSheet::load_file_list() {
+    UINT num_files, i, sv = 0;
+    WCHAR fn[MAX_PATH];
+
+    num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+
+    min_mode = 0;
+    max_mode = 0;
+    min_flags = 0;
+    max_flags = 0;
+    min_compression_type = 0xff;
+    max_compression_type = 0;
+    various_subvols = various_inodes = various_types = various_uids = various_gids = various_ro = FALSE;
+
+    can_change_perms = TRUE;
+    can_change_nocow = TRUE;
+
+    sizes[0] = sizes[1] = sizes[2] = sizes[3] = 0;
+
+    for (i = 0; i < num_files; i++) {
+        if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+            HRESULT hr;
+
+            hr = check_file(fn, i, num_files, &sv);
+            if (FAILED(hr))
+                return hr;
+        } else
+            return E_FAIL;
+    }
+
+    min_mode = ~min_mode;
+    min_flags = ~min_flags;
+
+    mode = min_mode;
+    mode_set = ~(min_mode ^ max_mode);
+
+    flags = min_flags;
+    flags_set = ~(min_flags ^ max_flags);
+
+    return S_OK;
+}
+
+HRESULT __stdcall BtrfsPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
     FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
-    UINT num_files, i;
-    WCHAR fn[MAX_PATH];
     HDROP hdrop;
-    
+    HRESULT hr;
+
     if (pidlFolder)
         return E_FAIL;
-        
-    
+
     if (!pdtobj)
         return E_FAIL;
-    
+
     stgm.tymed = TYMED_HGLOBAL;
-    
+
     if (FAILED(pdtobj->GetData(&format, &stgm)))
         return E_INVALIDARG;
-    
+
     stgm_set = TRUE;
-    
+
     hdrop = (HDROP)GlobalLock(stgm.hGlobal);
-    
+
     if (!hdrop) {
         ReleaseStgMedium(&stgm);
         stgm_set = FALSE;
         return E_INVALIDARG;
     }
-        
-    num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
-    
+
+    hr = load_file_list();
+    if (FAILED(hr))
+        return hr;
+
+    if (search_list.size() > 0) {
+        thread = CreateThread(NULL, 0, global_search_list_thread, this, 0, NULL);
+
+        if (!thread)
+            ShowError(NULL, GetLastError());
+    }
+
+    GlobalUnlock(hdrop);
+
+    return S_OK;
+}
+
+void BtrfsPropSheet::set_cmdline(std::wstring cmdline) {
+    HANDLE h;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS Status;
+    UINT sv = 0;
+    BY_HANDLE_FILE_INFORMATION bhfi;
+    btrfs_inode_info bii2;
+    FILE_ACCESS_INFORMATION fai;
+
     min_mode = 0;
     max_mode = 0;
     min_flags = 0;
     max_flags = 0;
-    various_subvols = various_inodes = various_types = various_uids = various_gids = FALSE;
-    
+    min_compression_type = 0xff;
+    max_compression_type = 0;
+    various_subvols = various_inodes = various_types = various_uids = various_gids = various_ro = FALSE;
+
     can_change_perms = TRUE;
-    can_change_owner = TRUE;
     can_change_nocow = TRUE;
-    
-    for (i = 0; i < num_files; i++) {
-        if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
-            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
-                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
-            
-            if (h != INVALID_HANDLE_VALUE)
-                CloseHandle(h);
-            else
-                can_change_perms = FALSE;
-            
-            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES | WRITE_OWNER, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
-                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
-            
-            if (h != INVALID_HANDLE_VALUE)
-                CloseHandle(h);
-            else
-                can_change_owner = FALSE;
-            
-            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
-                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
 
-            if (h == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_WRITE_PROTECT)) {
-                h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
-                                FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
-                
-                readonly = TRUE;
+    h = CreateFileW(cmdline.c_str(), MAXIMUM_ALLOWED, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE)
+        return;
+
+    Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
+    if (!NT_SUCCESS(Status)) {
+        CloseHandle(h);
+        return;
+    }
+
+    if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
+        can_change_perms = fai.AccessFlags & WRITE_DAC;
+
+    readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
+
+    if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        add_to_search_list((WCHAR*)cmdline.c_str());
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
+
+    if (NT_SUCCESS(Status) && !bii2.top) {
+        int j;
+
+        LARGE_INTEGER filesize;
+
+        subvol = bii2.subvol;
+        inode = bii2.inode;
+        type = bii2.type;
+        uid = bii2.st_uid;
+        gid = bii2.st_gid;
+        rdev = bii2.st_rdev;
+
+        if (bii2.inline_length > 0) {
+            totalsize += bii2.inline_length;
+            sizes[0] += bii2.inline_length;
+        }
+
+        for (j = 0; j < 3; j++) {
+            if (bii2.disk_size[j] > 0) {
+                totalsize += bii2.disk_size[j];
+                sizes[j + 1] += bii2.disk_size[j];
             }
-            
-            if (h != INVALID_HANDLE_VALUE) {
-                BY_HANDLE_FILE_INFORMATION bhfi;
-                btrfs_inode_info bii2;
-                
-                if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-                    add_to_search_list(fn);
-                
-                Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
-                    
-                if (Status == STATUS_SUCCESS && !bii2.top) {
-                    int j;
-                    
-                    LARGE_INTEGER filesize;
-                    
-                    if (i == 0) {
-                        subvol = bii2.subvol;
-                        inode = bii2.inode;
-                        type = bii2.type;
-                        uid = bii2.st_uid;
-                        gid = bii2.st_gid;
-                        rdev = bii2.st_rdev;
-                    } else {
-                        if (subvol != bii2.subvol)
-                            various_subvols = TRUE;
-                        
-                        if (inode != bii2.inode)
-                            various_inodes = TRUE;
-                        
-                        if (type != bii2.type)
-                            various_types = TRUE;
-                        
-                        if (uid != bii2.st_uid)
-                            various_uids = TRUE;
+        }
 
-                        if (gid != bii2.st_gid)
-                            various_gids = TRUE;
-                    }
+        min_mode |= ~bii2.st_mode;
+        max_mode |= bii2.st_mode;
+        min_flags |= ~bii2.flags;
+        max_flags |= bii2.flags;
+        min_compression_type = bii2.compression_type < min_compression_type ? bii2.compression_type : min_compression_type;
+        max_compression_type = bii2.compression_type > max_compression_type ? bii2.compression_type : max_compression_type;
 
-                    if (bii2.inline_length > 0) {
-                        totalsize += bii2.inline_length;
-                        sizes[0] += bii2.inline_length;
-                    }
-                    
-                    for (j = 0; j < 3; j++) {
-                        if (bii2.disk_size[j] > 0) {
-                            totalsize += bii2.disk_size[j];
-                            sizes[j + 1] += bii2.disk_size[j];
-                        }
-                    }
-                    
-                    min_mode |= ~bii2.st_mode;
-                    max_mode |= bii2.st_mode;
-                    min_flags |= ~bii2.flags;
-                    max_flags |= bii2.flags;
-                    
-                    ignore = FALSE;
-                    
-                    if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
-                        if (filesize.QuadPart != 0)
-                            can_change_nocow = FALSE;
-                    }
-                    
-                    CloseHandle(h);
-                } else {
-                    CloseHandle(h);
-                    return E_FAIL;
-                }
-            } else
-                return E_FAIL;
-        } else
-            return E_FAIL;
+        if (bii2.inode == SUBVOL_ROOT_INODE) {
+            BOOL ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
+
+            has_subvols = TRUE;
+
+            if (sv == 0)
+                ro_subvol = ro;
+            else {
+                if (ro_subvol != ro)
+                    various_ro = TRUE;
+            }
+
+            sv++;
+        }
+
+        ignore = FALSE;
+
+        if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
+            if (filesize.QuadPart != 0)
+                can_change_nocow = FALSE;
+        }
+
+        CloseHandle(h);
+    } else {
+        CloseHandle(h);
+        return;
     }
-    
+
     min_mode = ~min_mode;
     min_flags = ~min_flags;
-    
+
     mode = min_mode;
     mode_set = ~(min_mode ^ max_mode);
-    
+
     flags = min_flags;
     flags_set = ~(min_flags ^ max_flags);
-    
+
     if (search_list.size() > 0) {
         thread = CreateThread(NULL, 0, global_search_list_thread, this, 0, NULL);
-        
+
         if (!thread)
             ShowError(NULL, GetLastError());
     }
 
-    return S_OK;
+    this->filename = cmdline;
 }
 
 static ULONG inode_type_to_string_ref(UINT8 type) {
-    switch (type) {    
+    switch (type) {
         case BTRFS_TYPE_FILE:
             return IDS_INODE_FILE;
-        
+
         case BTRFS_TYPE_DIRECTORY:
             return IDS_INODE_DIR;
-            
+
         case BTRFS_TYPE_CHARDEV:
             return IDS_INODE_CHAR;
-            
+
         case BTRFS_TYPE_BLOCKDEV:
             return IDS_INODE_BLOCK;
-            
+
         case BTRFS_TYPE_FIFO:
             return IDS_INODE_FIFO;
-            
+
         case BTRFS_TYPE_SOCKET:
             return IDS_INODE_SOCKET;
-            
+
         case BTRFS_TYPE_SYMLINK:
             return IDS_INODE_SYMLINK;
-            
+
         default:
             return IDS_INODE_UNKNOWN;
     }
 }
 
-static void ShowError(HWND hwnd, ULONG err) {
-    WCHAR* buf;
-    
-    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
-                       err, 0, (WCHAR*)&buf, 0, NULL) == 0) {
-        MessageBoxW(hwnd, L"FormatMessage failed", L"Error", MB_ICONERROR);
-        return;
-    }
-    
-    MessageBoxW(hwnd, buf, L"Error", MB_ICONERROR);
-    
-    LocalFree(buf);
-}
-
 void BtrfsPropSheet::change_inode_flag(HWND hDlg, UINT64 flag, UINT state) {
     if (flag & BTRFS_INODE_NODATACOW)
         flag |= BTRFS_INODE_NODATASUM;
-    
+
     if (state == BST_CHECKED) {
         flags |= flag;
         flags_set |= flag;
@@ -388,123 +514,153 @@
     } else if (state == BST_INDETERMINATE) {
         flags_set = ~flag;
     }
-    
+
     flags_changed = TRUE;
-    
-    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+
+    SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
 }
 
-void BtrfsPropSheet::apply_changes(HWND hDlg) {
-    UINT num_files, i;
-    WCHAR fn[MAX_PATH]; // FIXME - is this long enough?
+void BtrfsPropSheet::apply_changes_file(HWND hDlg, std::wstring fn) {
     HANDLE h;
     IO_STATUS_BLOCK iosb;
     NTSTATUS Status;
     btrfs_set_inode_info bsii;
-    
+    btrfs_inode_info bii2;
+    ULONG perms = FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
+
+    if (flags_changed || ro_changed)
+        perms |= FILE_WRITE_ATTRIBUTES;
+
+    if (perms_changed || gid_changed || uid_changed)
+        perms |= WRITE_DAC;
+
+    if (mode_set & S_ISUID && (((min_mode & S_ISUID) != (max_mode & S_ISUID)) || ((min_mode & S_ISUID) != (mode & S_ISUID))))
+        perms |= WRITE_OWNER;
+
+    h = CreateFileW(fn.c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowError(hDlg, GetLastError());
+        return;
+    }
+
+    ZeroMemory(&bsii, sizeof(btrfs_set_inode_info));
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
+
+    if (!NT_SUCCESS(Status)) {
+        ShowNtStatusError(hDlg, Status);
+        CloseHandle(h);
+        return;
+    }
+
+    if (bii2.inode == SUBVOL_ROOT_INODE && ro_changed) {
+        BY_HANDLE_FILE_INFORMATION bhfi;
+        FILE_BASIC_INFO fbi;
+
+        if (!GetFileInformationByHandle(h, &bhfi)) {
+            ShowError(hDlg, GetLastError());
+            return;
+        }
+
+        memset(&fbi, 0, sizeof(fbi));
+        fbi.FileAttributes = bhfi.dwFileAttributes;
+
+        if (ro_subvol)
+            fbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+        else
+            fbi.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
+
+        if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(fbi))) {
+            CloseHandle(h);
+            ShowError(hDlg, GetLastError());
+            return;
+        }
+    }
+
+    if (flags_changed || perms_changed || uid_changed || gid_changed || compress_type_changed) {
+        if (flags_changed) {
+            bsii.flags_changed = TRUE;
+            bsii.flags = (bii2.flags & ~flags_set) | (flags & flags_set);
+        }
+
+        if (perms_changed) {
+            bsii.mode_changed = TRUE;
+            bsii.st_mode = (bii2.st_mode & ~mode_set) | (mode & mode_set);
+        }
+
+        if (uid_changed) {
+            bsii.uid_changed = TRUE;
+            bsii.st_uid = uid;
+        }
+
+        if (gid_changed) {
+            bsii.gid_changed = TRUE;
+            bsii.st_gid = gid;
+        }
+
+        if (compress_type_changed) {
+            bsii.compression_type_changed = TRUE;
+            bsii.compression_type = compress_type;
+        }
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hDlg, Status);
+            CloseHandle(h);
+            return;
+        }
+    }
+
+    CloseHandle(h);
+}
+
+void BtrfsPropSheet::apply_changes(HWND hDlg) {
+    UINT num_files, i;
+    WCHAR fn[MAX_PATH]; // FIXME - is this long enough?
+
     if (various_uids)
         uid_changed = FALSE;
-    
+
     if (various_gids)
         gid_changed = FALSE;
-    
-    if (!flags_changed && !perms_changed && !uid_changed && !gid_changed)
+
+    if (!flags_changed && !perms_changed && !uid_changed && !gid_changed && !compress_type_changed && !ro_changed)
         return;
 
-    num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
-    
-    for (i = 0; i < num_files; i++) {
-        if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
-            ULONG perms = FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
-            
-            if (flags_changed)
-                perms |= FILE_WRITE_ATTRIBUTES;
-            
-            if (perms_changed)
-                perms |= WRITE_DAC;
-            
-            if (uid_changed || gid_changed)
-                perms |= WRITE_OWNER;
-            
-            h = CreateFileW(fn, perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
-                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    if (filename[0] != 0)
+        apply_changes_file(hDlg, filename);
+    else {
+        num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
 
-            if (h == INVALID_HANDLE_VALUE) {
-                ShowError(hDlg, GetLastError());
-                return;
+        for (i = 0; i < num_files; i++) {
+            if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+                apply_changes_file(hDlg, fn);
             }
-            
-            ZeroMemory(&bsii, sizeof(btrfs_set_inode_info));
-            
-            btrfs_inode_info bii2;
-                    
-            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
-        
-            if (Status != STATUS_SUCCESS) {
-                ShowNtStatusError(hDlg, Status);
-                CloseHandle(h);
-                return;
-            }
-            
-            if (flags_changed) {
-                bsii.flags_changed = TRUE;
-                bsii.flags = (bii2.flags & ~flags_set) | (flags & flags_set);
-            }
-            
-            if (perms_changed) {
-                bsii.mode_changed = TRUE;
-                bsii.st_mode = (bii2.st_mode & ~mode_set) | (mode & mode_set);
-            }
-            
-            if (uid_changed) {
-                bsii.uid_changed = TRUE;
-                bsii.st_uid = uid;
-            }
-            
-            if (gid_changed) {
-                bsii.gid_changed = TRUE;
-                bsii.st_gid = gid;
-            }
-            
-            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, NULL, 0, &bsii, sizeof(btrfs_set_inode_info));
-            CloseHandle(h);
-
-            if (Status != STATUS_SUCCESS) {
-                WCHAR s[255], t[255];
-                
-                if (!LoadStringW(module, IDS_SET_INODE_INFO_ERROR, t, sizeof(t) / sizeof(WCHAR))) {
-                    ShowError(hDlg, GetLastError());
-                    return;
-                }
-                
-                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, Status) == STRSAFE_E_INSUFFICIENT_BUFFER) {
-                    ShowError(hDlg, ERROR_INSUFFICIENT_BUFFER);
-                    return;
-                }
-                
-                MessageBoxW(hDlg, s, L"Error", MB_ICONERROR);
-            }
         }
     }
-    
+
     flags_changed = FALSE;
     perms_changed = FALSE;
     uid_changed = FALSE;
     gid_changed = FALSE;
+    ro_changed = FALSE;
 }
 
 void BtrfsPropSheet::set_size_on_disk(HWND hwndDlg) {
     WCHAR size_on_disk[1024], s[1024], old_text[1024];
-    
-    format_size(totalsize, size_on_disk, sizeof(size_on_disk) / sizeof(WCHAR));
-    
+
+    format_size(totalsize, size_on_disk, sizeof(size_on_disk) / sizeof(WCHAR), TRUE);
+
     if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), size_format, size_on_disk) == STRSAFE_E_INSUFFICIENT_BUFFER) {
         ShowError(hwndDlg, ERROR_INSUFFICIENT_BUFFER);
         return;
     }
-    
+
     GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, old_text, sizeof(old_text) / sizeof(WCHAR));
-    
+
     if (wcscmp(s, old_text))
         SetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, s);
 }
@@ -519,26 +675,28 @@
     } else if (state == BST_INDETERMINATE) {
         mode_set = ~flag;
     }
-    
+
     perms_changed = TRUE;
-    
-    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+
+    SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
 }
 
 void BtrfsPropSheet::change_uid(HWND hDlg, UINT32 uid) {
-    this->uid = uid;
-    
-    uid_changed = TRUE;
-    
-    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+    if (this->uid != uid) {
+        this->uid = uid;
+        uid_changed = TRUE;
+
+        SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
+    }
 }
 
 void BtrfsPropSheet::change_gid(HWND hDlg, UINT32 gid) {
-    this->gid = gid;
-    
-    gid_changed = TRUE;
-    
-    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+    if (this->gid != gid) {
+        this->gid = gid;
+        gid_changed = TRUE;
+
+        SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
+    }
 }
 
 void BtrfsPropSheet::update_size_details_dialog(HWND hDlg) {
@@ -545,12 +703,12 @@
     WCHAR size[1024], old_text[1024];
     int i;
     ULONG items[] = { IDC_SIZE_INLINE, IDC_SIZE_UNCOMPRESSED, IDC_SIZE_ZLIB, IDC_SIZE_LZO };
-    
+
     for (i = 0; i < 4; i++) {
-        format_size(sizes[i], size, sizeof(size) / sizeof(WCHAR));
-        
+        format_size(sizes[i], size, sizeof(size) / sizeof(WCHAR), TRUE);
+
         GetDlgItemTextW(hDlg, items[i], old_text, sizeof(old_text) / sizeof(WCHAR));
-        
+
         if (wcscmp(size, old_text))
             SetDlgItemTextW(hDlg, items[i], size);
     }
@@ -561,17 +719,17 @@
         case WM_INITDIALOG:
         {
             BtrfsPropSheet* bps = (BtrfsPropSheet*)lParam;
-            
+
             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
-            
+
             bps->update_size_details_dialog(hwndDlg);
-            
+
             if (bps->thread)
                 SetTimer(hwndDlg, 1, 250, NULL);
-            
+
             return TRUE;
         }
-            
+
         case WM_COMMAND:
             if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
                 EndDialog(hwndDlg, 0);
@@ -578,22 +736,22 @@
                 return TRUE;
             }
         break;
-        
+
         case WM_TIMER:
         {
             BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
-            
+
             if (bps) {
                 bps->update_size_details_dialog(hwndDlg);
-                
+
                 if (!bps->thread)
                     KillTimer(hwndDlg, 1);
             }
-            
+
             break;
         }
     }
-    
+
     return FALSE;
 }
 
@@ -604,16 +762,240 @@
         SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_UNCHECKED, 0);
     } else {
         LONG_PTR style;
-        
+
         style = GetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE);
         style &= ~BS_AUTOCHECKBOX;
         style |= BS_AUTO3STATE;
         SetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE, style);
-        
+
         SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_INDETERMINATE, 0);
     }
 }
 
+void BtrfsPropSheet::open_as_admin(HWND hwndDlg) {
+    ULONG num_files, i;
+    WCHAR fn[MAX_PATH];
+
+    num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+
+    for (i = 0; i < num_files; i++) {
+        if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+            WCHAR t[MAX_PATH + 100];
+            SHELLEXECUTEINFOW sei;
+
+            t[0] = '"';
+            GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+            wcscat(t, L"\",ShowPropSheet ");
+            wcscat(t, fn);
+
+            RtlZeroMemory(&sei, sizeof(sei));
+
+            sei.cbSize = sizeof(sei);
+            sei.hwnd = hwndDlg;
+            sei.lpVerb = L"runas";
+            sei.lpFile = L"rundll32.exe";
+            sei.lpParameters = t;
+            sei.nShow = SW_SHOW;
+            sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+            if (!ShellExecuteExW(&sei)) {
+                ShowError(hwndDlg, GetLastError());
+                return;
+            }
+
+            WaitForSingleObject(sei.hProcess, INFINITE);
+            CloseHandle(sei.hProcess);
+
+            load_file_list();
+            init_propsheet(hwndDlg);
+        }
+    }
+}
+
+// based on functions in sys/sysmacros.h
+#define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((UINT32)((rdev) >> 32) & ~0xFFF))
+#define minor(rdev) (((rdev) & 0xFF) | ((UINT32)((rdev) >> 12) & ~0xFF))
+
+void BtrfsPropSheet::init_propsheet(HWND hwndDlg) {
+    WCHAR s[255];
+    ULONG sr;
+    int i;
+    HWND comptype;
+
+    static ULONG perm_controls[] = { IDC_USERR, IDC_USERW, IDC_USERX, IDC_GROUPR, IDC_GROUPW, IDC_GROUPX, IDC_OTHERR, IDC_OTHERW, IDC_OTHERX,
+                                     IDC_SETUID, IDC_SETGID, IDC_STICKY, 0 };
+    static ULONG perms[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH, S_ISUID, S_ISGID, S_ISVTX, 0 };
+    static ULONG comp_types[] = { IDS_COMPRESS_ANY, IDS_COMPRESS_ZLIB, IDS_COMPRESS_LZO, 0 };
+
+    if (various_subvols) {
+        if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+    } else {
+        if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", subvol) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+    }
+
+    SetDlgItemTextW(hwndDlg, IDC_SUBVOL, s);
+
+    if (various_inodes) {
+        if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+    } else {
+        if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", inode) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+    }
+
+    SetDlgItemTextW(hwndDlg, IDC_INODE, s);
+
+    if (various_types)
+        sr = IDS_VARIOUS;
+    else
+        sr = inode_type_to_string_ref(type);
+
+    if (various_inodes) {
+        if (sr == IDS_INODE_CHAR)
+            sr = IDS_INODE_CHAR_SIMPLE;
+        else if (sr == IDS_INODE_BLOCK)
+            sr = IDS_INODE_BLOCK_SIMPLE;
+    }
+
+    if (sr == IDS_INODE_UNKNOWN) {
+        WCHAR t[255];
+
+        if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, type) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+    } else if (sr == IDS_INODE_CHAR || sr == IDS_INODE_BLOCK) {
+        WCHAR t[255];
+
+        if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, major(rdev), minor(rdev)) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+    } else {
+        if (!LoadStringW(module, sr, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+    }
+
+    SetDlgItemTextW(hwndDlg, IDC_TYPE, s);
+
+    GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, size_format, sizeof(size_format) / sizeof(WCHAR));
+    set_size_on_disk(hwndDlg);
+
+    if (thread)
+        SetTimer(hwndDlg, 1, 250, NULL);
+
+    set_check_box(hwndDlg, IDC_NODATACOW, min_flags & BTRFS_INODE_NODATACOW, max_flags & BTRFS_INODE_NODATACOW);
+    set_check_box(hwndDlg, IDC_COMPRESS, min_flags & BTRFS_INODE_COMPRESS, max_flags & BTRFS_INODE_COMPRESS);
+
+    comptype = GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE);
+
+    if (min_compression_type != max_compression_type) {
+        SendMessage(comptype, CB_ADDSTRING, NULL, (LPARAM)L"");
+        SendMessage(comptype, CB_SETCURSEL, 0, 0);
+    }
+
+    i = 0;
+    while (comp_types[i] != 0) {
+        WCHAR t[255];
+
+        if (!LoadStringW(module, comp_types[i], t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        SendMessage(comptype, CB_ADDSTRING, NULL, (LPARAM)t);
+
+        i++;
+    }
+
+    if (min_compression_type == max_compression_type) {
+        SendMessage(comptype, CB_SETCURSEL, min_compression_type, 0);
+        compress_type = min_compression_type;
+    }
+
+    EnableWindow(comptype, max_flags & BTRFS_INODE_COMPRESS);
+
+    i = 0;
+    while (perm_controls[i] != 0) {
+        set_check_box(hwndDlg, perm_controls[i], min_mode & perms[i], max_mode & perms[i]);
+        i++;
+    }
+
+    if (various_uids) {
+        if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
+    } else {
+        if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", uid) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+    }
+
+    SetDlgItemTextW(hwndDlg, IDC_UID, s);
+
+    if (various_gids) {
+        if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
+    } else {
+        if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", gid) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            return;
+    }
+
+    SetDlgItemTextW(hwndDlg, IDC_GID, s);
+
+    ShowWindow(GetDlgItem(hwndDlg, IDC_SUBVOL_RO), has_subvols);
+
+    if (has_subvols)
+        set_check_box(hwndDlg, IDC_SUBVOL_RO, ro_subvol, various_ro ? (!ro_subvol) : ro_subvol);
+
+    if (!can_change_nocow)
+        EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
+
+    if (!can_change_perms) {
+        i = 0;
+        while (perm_controls[i] != 0) {
+            EnableWindow(GetDlgItem(hwndDlg, perm_controls[i]), 0);
+            i++;
+        }
+
+        EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_SETUID), 0);
+    }
+
+    if (readonly) {
+        EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), 0);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), 0);
+    }
+
+    if (show_admin_button) {
+        SendMessageW(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), BCM_SETSHIELD, 0, TRUE);
+        ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_SHOW);
+    } else
+        ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_HIDE);
+}
+
 static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
     switch (uMsg) {
         case WM_INITDIALOG:
@@ -620,234 +1002,165 @@
         {
             PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
             BtrfsPropSheet* bps = (BtrfsPropSheet*)psp->lParam;
-            WCHAR s[255];
-            ULONG sr;
-            int i;
-            
-            static ULONG perm_controls[] = { IDC_USERR, IDC_USERW, IDC_USERX, IDC_GROUPR, IDC_GROUPW, IDC_GROUPX, IDC_OTHERR, IDC_OTHERW, IDC_OTHERX, 0 };
-            static ULONG perms[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH, 0 };
-            
+
             EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
-            
+
             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
-            
-            if (bps->various_subvols) {
-                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
-                    ShowError(hwndDlg, GetLastError());
-                    return FALSE;
-                }
-            } else {
-                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", bps->subvol) == STRSAFE_E_INSUFFICIENT_BUFFER)
-                    return FALSE;
-            }
-            
-            SetDlgItemTextW(hwndDlg, IDC_SUBVOL, s);
-            
-            if (bps->various_inodes) {
-                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
-                    ShowError(hwndDlg, GetLastError());
-                    return FALSE;
-                }
-            } else {
-                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", bps->inode) == STRSAFE_E_INSUFFICIENT_BUFFER)
-                    return FALSE;
-            }
-            
-            SetDlgItemTextW(hwndDlg, IDC_INODE, s);
-            
-            if (bps->various_types)
-                sr = IDS_VARIOUS;
-            else
-                sr = inode_type_to_string_ref(bps->type);
-            
-            if (bps->various_inodes) {
-                if (sr == IDS_INODE_CHAR)
-                    sr = IDS_INODE_CHAR_SIMPLE;
-                else if (sr == IDS_INODE_BLOCK)
-                    sr = IDS_INODE_BLOCK_SIMPLE;
-            }
-            
-            if (sr == IDS_INODE_UNKNOWN) {
-                WCHAR t[255];
-                
-                if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
-                    ShowError(hwndDlg, GetLastError());
-                    return FALSE;
-                }
-                
-                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, bps->type) == STRSAFE_E_INSUFFICIENT_BUFFER)
-                    return FALSE;
-            } else if (sr == IDS_INODE_CHAR || sr == IDS_INODE_BLOCK) {
-                WCHAR t[255];
-                
-                if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
-                    ShowError(hwndDlg, GetLastError());
-                    return FALSE;
-                }
-                
-                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, (UINT64)((bps->rdev & 0xFFFFFFFFFFF) >> 20), (UINT32)(bps->rdev & 0xFFFFF)) == STRSAFE_E_INSUFFICIENT_BUFFER)
-                    return FALSE;
-            } else {
-                if (!LoadStringW(module, sr, s, sizeof(s) / sizeof(WCHAR))) {
-                    ShowError(hwndDlg, GetLastError());
-                    return FALSE;
-                }
-            }
-            
-            SetDlgItemTextW(hwndDlg, IDC_TYPE, s);
-            
-            GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, bps->size_format, sizeof(bps->size_format) / sizeof(WCHAR));
-            bps->set_size_on_disk(hwndDlg);
-            
-            if (bps->thread)
-                SetTimer(hwndDlg, 1, 250, NULL);
-            
-            set_check_box(hwndDlg, IDC_NODATACOW, bps->min_flags & BTRFS_INODE_NODATACOW, bps->max_flags & BTRFS_INODE_NODATACOW);
-            set_check_box(hwndDlg, IDC_COMPRESS, bps->min_flags & BTRFS_INODE_COMPRESS, bps->max_flags & BTRFS_INODE_COMPRESS);
-            
-            i = 0;
-            while (perm_controls[i] != 0) {
-                set_check_box(hwndDlg, perm_controls[i], bps->min_mode & perms[i], bps->max_mode & perms[i]);
-                i++;
-            }
-            
-            if (bps->various_uids) {
-                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
-                    ShowError(hwndDlg, GetLastError());
-                    return FALSE;
-                }
-                
-                EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
-            } else {
-                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", bps->uid) == STRSAFE_E_INSUFFICIENT_BUFFER)
-                    return FALSE;
-            }
-            
-            SetDlgItemTextW(hwndDlg, IDC_UID, s);
-            
-            if (bps->various_gids) {
-                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
-                    ShowError(hwndDlg, GetLastError());
-                    return FALSE;
-                }
-                
-                EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
-            } else {
-                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", bps->gid) == STRSAFE_E_INSUFFICIENT_BUFFER)
-                    return FALSE;
-            }
-            
-            SetDlgItemTextW(hwndDlg, IDC_GID, s);
-            
-            if (!bps->can_change_nocow)
-                EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
-            
-            if (!bps->can_change_owner) {
-                EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
-            }
-            
-            if (!bps->can_change_perms) {
-                EnableWindow(GetDlgItem(hwndDlg, IDC_USERR), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_USERW), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_USERX), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_GROUPR), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_GROUPW), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_GROUPX), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_OTHERR), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_OTHERW), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_OTHERX), 0);
-            }
-            
-            if (bps->readonly) {
-                EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
-                EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), 0);
-            }
-            
+
+            bps->init_propsheet(hwndDlg);
+
             return FALSE;
         }
-        
+
         case WM_COMMAND:
         {
             BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
-            
+
             if (bps && !bps->readonly) {
                 switch (HIWORD(wParam)) {
                     case BN_CLICKED: {
                         switch (LOWORD(wParam)) {
                             case IDC_NODATACOW:
-                                bps->change_inode_flag(hwndDlg, BTRFS_INODE_NODATACOW, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_inode_flag(hwndDlg, BTRFS_INODE_NODATACOW, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_COMPRESS:
-                                bps->change_inode_flag(hwndDlg, BTRFS_INODE_COMPRESS, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_inode_flag(hwndDlg, BTRFS_INODE_COMPRESS, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
+
+                                EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) != BST_UNCHECKED);
                             break;
-                            
+
                             case IDC_USERR:
-                                bps->change_perm_flag(hwndDlg, S_IRUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IRUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_USERW:
-                                bps->change_perm_flag(hwndDlg, S_IWUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IWUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_USERX:
-                                bps->change_perm_flag(hwndDlg, S_IXUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IXUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_GROUPR:
-                                bps->change_perm_flag(hwndDlg, S_IRGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IRGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_GROUPW:
-                                bps->change_perm_flag(hwndDlg, S_IWGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IWGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_GROUPX:
-                                bps->change_perm_flag(hwndDlg, S_IXGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IXGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_OTHERR:
-                                bps->change_perm_flag(hwndDlg, S_IROTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IROTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_OTHERW:
-                                bps->change_perm_flag(hwndDlg, S_IWOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IWOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
-                            
+
                             case IDC_OTHERX:
-                                bps->change_perm_flag(hwndDlg, S_IXOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                                bps->change_perm_flag(hwndDlg, S_IXOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
                             break;
+
+                            case IDC_SETUID:
+                                bps->change_perm_flag(hwndDlg, S_ISUID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
+                            break;
+
+                            case IDC_SETGID:
+                                bps->change_perm_flag(hwndDlg, S_ISGID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
+                            break;
+
+                            case IDC_STICKY:
+                                bps->change_perm_flag(hwndDlg, S_ISVTX, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
+                            break;
+
+                            case IDC_SUBVOL_RO:
+                                switch (IsDlgButtonChecked(hwndDlg, LOWORD(wParam))) {
+                                    case BST_CHECKED:
+                                        bps->ro_subvol = TRUE;
+                                        bps->ro_changed = TRUE;
+                                    break;
+
+                                    case BST_UNCHECKED:
+                                        bps->ro_subvol = FALSE;
+                                        bps->ro_changed = TRUE;
+                                    break;
+
+                                    case BST_INDETERMINATE:
+                                        bps->ro_changed = FALSE;
+                                    break;
+                                }
+
+                                SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
+                            break;
+
+                            case IDC_OPEN_ADMIN:
+                                bps->open_as_admin(hwndDlg);
+                            break;
                         }
+
+                        break;
                     }
-                    
+
                     case EN_CHANGE: {
                         switch (LOWORD(wParam)) {
                             case IDC_UID: {
                                 WCHAR s[255];
-                                
+
                                 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
-                                
+
                                 bps->change_uid(hwndDlg, _wtoi(s));
                                 break;
                             }
-                            
+
                             case IDC_GID: {
                                 WCHAR s[255];
-                                
+
                                 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
-                                
+
                                 bps->change_gid(hwndDlg, _wtoi(s));
                                 break;
                             }
                         }
+
+                        break;
                     }
+
+                    case CBN_SELCHANGE: {
+                        switch (LOWORD(wParam)) {
+                            case IDC_COMPRESS_TYPE: {
+                                int sel = SendMessageW(GetDlgItem(hwndDlg, LOWORD(wParam)), CB_GETCURSEL, 0, 0);
+
+                                if (bps->min_compression_type != bps->max_compression_type) {
+                                    if (sel == 0)
+                                        bps->compress_type_changed = FALSE;
+                                    else {
+                                        bps->compress_type = sel - 1;
+                                        bps->compress_type_changed = TRUE;
+                                    }
+                                } else {
+                                    bps->compress_type = sel;
+                                    bps->compress_type_changed = TRUE;
+                                }
+
+                                SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
+
+                                break;
+                            }
+                        }
+
+                        break;
+                    }
                 }
             }
-            
+
             break;
         }
-        
+
         case WM_NOTIFY:
         {
             switch (((LPNMHDR)lParam)->code) {
@@ -854,20 +1167,20 @@
                 case PSN_KILLACTIVE:
                     SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, FALSE);
                 break;
-                    
+
                 case PSN_APPLY: {
                     BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
-                    
+
                     bps->apply_changes(hwndDlg);
                     SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
                     break;
                 }
-                
+
                 case NM_CLICK:
                 case NM_RETURN: {
                     if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_SIZE_ON_DISK)) {
                         PNMLINK pNMLink = (PNMLINK)lParam;
-                        
+
                         if (pNMLink->item.iLink == 0)
                             DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SIZE_DETAILS), hwndDlg, SizeDetailsDlgProc, GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
                     }
@@ -875,145 +1188,40 @@
                 }
             }
         }
-        
+
         case WM_TIMER:
         {
             BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
-            
+
             if (bps) {
                 bps->set_size_on_disk(hwndDlg);
-                
+
                 if (!bps->thread)
                     KillTimer(hwndDlg, 1);
             }
-            
+
             break;
         }
     }
-    
+
     return FALSE;
 }
 
-static void format_size(UINT64 size, WCHAR* s, ULONG len) {
-    WCHAR nb[255], nb2[255], t[255], bytes[255];
-    WCHAR kb[255];
-    ULONG sr;
-    float f;
-    NUMBERFMTW fmt;
-    WCHAR thou[4], grouping[64], *c;
-    
-    _i64tow(size, nb, 10);
-    
-    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thou, sizeof(thou) / sizeof(WCHAR));
-    
-    fmt.NumDigits = 0;
-    fmt.LeadingZero = 1;
-    fmt.lpDecimalSep = (LPWSTR)L"."; // not used
-    fmt.lpThousandSep = thou;
-    fmt.NegativeOrder = 0;
-    
-    // Grouping code copied from dlls/shlwapi/string.c in Wine - thank you
-    
-    fmt.Grouping = 0;
-    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping) / sizeof(WCHAR));
-    
-    c = grouping;
-    while (*c) {
-        if (*c >= '0' && *c < '9') {
-            fmt.Grouping *= 10;
-            fmt.Grouping += *c - '0';
-        }
-        
-        c++;
-    }
-
-    if (fmt.Grouping % 10 == 0)
-        fmt.Grouping /= 10;
-    else
-        fmt.Grouping *= 10;
-    
-    GetNumberFormatW(LOCALE_USER_DEFAULT, 0, nb, &fmt, nb2, sizeof(nb2) / sizeof(WCHAR));
-    
-    if (size < 1024) {
-        if (!LoadStringW(module, size == 1 ? IDS_SIZE_BYTE : IDS_SIZE_BYTES, t, sizeof(t) / sizeof(WCHAR))) {
-            ShowError(NULL, GetLastError());
-            return;
-        }
-        
-        if (StringCchPrintfW(s, len, t, nb2) == STRSAFE_E_INSUFFICIENT_BUFFER) {
-            ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
-            return;
-        }
-        
-        return;
-    }
-    
-    if (!LoadStringW(module, IDS_SIZE_BYTES, t, sizeof(t) / sizeof(WCHAR))) {
-        ShowError(NULL, GetLastError());
-        return;
-    }
-    
-    if (StringCchPrintfW(bytes, sizeof(bytes) / sizeof(WCHAR), t, nb2) == STRSAFE_E_INSUFFICIENT_BUFFER) {
-        ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
-        return;
-    }
-    
-    if (size >= 1152921504606846976) {
-        sr = IDS_SIZE_EB;
-        f = (float)size / 1152921504606846976.0f;
-    } else if (size >= 1125899906842624) {
-        sr = IDS_SIZE_PB;
-        f = (float)size / 1125899906842624.0f;
-    } else if (size >= 1099511627776) {
-        sr = IDS_SIZE_TB;
-        f = (float)size / 1099511627776.0f;
-    } else if (size >= 1073741824) {
-        sr = IDS_SIZE_GB;
-        f = (float)size / 1073741824.0f;
-    } else if (size >= 1048576) {
-        sr = IDS_SIZE_MB;
-        f = (float)size / 1048576.0f;
-    } else {
-        sr = IDS_SIZE_KB;
-        f = (float)size / 1024.0f;
-    }
-    
-    if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
-        ShowError(NULL, GetLastError());
-        return;
-    }
-    
-    if (StringCchPrintfW(kb, sizeof(kb) / sizeof(WCHAR), t, f) == STRSAFE_E_INSUFFICIENT_BUFFER) {
-        ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
-        return;
-    }
-    
-    if (!LoadStringW(module, IDS_SIZE_LARGE, t, sizeof(t) / sizeof(WCHAR))) {
-        ShowError(NULL, GetLastError());
-        return;
-    }
-    
-    if (StringCchPrintfW(s, len, t, kb, bytes) == STRSAFE_E_INSUFFICIENT_BUFFER) {
-        ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
-        return;
-    }
-}
-
 HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) {
     PROPSHEETPAGE psp;
     HPROPSHEETPAGE hPage;
     INITCOMMONCONTROLSEX icex;
-    
+
     if (ignore)
         return S_OK;
-    
+
     icex.dwSize = sizeof(icex);
     icex.dwICC = ICC_LINK_CLASS;
-    
+
     if (!InitCommonControlsEx(&icex)) {
         MessageBoxW(NULL, L"InitCommonControlsEx failed", L"Error", MB_ICONERROR);
     }
-    
+
     psp.dwSize = sizeof(psp);
     psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
     psp.hInstance = module;
@@ -1026,7 +1234,7 @@
     psp.lParam = (LPARAM)this;
 
     hPage = CreatePropertySheetPage(&psp);
-            
+
     if (hPage) {
         if (pfnAddPage(hPage, lParam)) {
             this->AddRef();
@@ -1035,7 +1243,7 @@
             DestroyPropertySheetPage(hPage);
     } else
         return E_OUTOFMEMORY;
-    
+
     return E_FAIL;
 }
 
@@ -1042,3 +1250,47 @@
 HRESULT __stdcall BtrfsPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) {
     return S_OK;
 }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void CALLBACK ShowPropSheetW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    BtrfsPropSheet* bps;
+    PROPSHEETPAGEW psp;
+    PROPSHEETHEADERW psh;
+    WCHAR title[255];
+
+    set_dpi_aware();
+
+    LoadStringW(module, IDS_STANDALONE_PROPSHEET_TITLE, title, sizeof(title) / sizeof(WCHAR));
+
+    bps = new BtrfsPropSheet;
+    bps->set_cmdline(lpszCmdLine);
+
+    psp.dwSize = sizeof(psp);
+    psp.dwFlags = PSP_USETITLE;
+    psp.hInstance = module;
+    psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROP_SHEET);
+    psp.hIcon = 0;
+    psp.pszTitle = MAKEINTRESOURCEW(IDS_PROP_SHEET_TITLE);
+    psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
+    psp.pfnCallback = NULL;
+    psp.lParam = (LPARAM)bps;
+
+    memset(&psh, 0, sizeof(PROPSHEETHEADERW));
+
+    psh.dwSize = sizeof(PROPSHEETHEADERW);
+    psh.dwFlags = PSH_PROPSHEETPAGE;
+    psh.hwndParent = hwnd;
+    psh.hInstance = psp.hInstance;
+    psh.pszCaption = title;
+    psh.nPages = 1;
+    psh.ppsp = &psp;
+
+    PropertySheetW(&psh);
+}
+
+#ifdef __cplusplus
+}
+#endif
Index: dll/shellext/shellbtrfs/propsheet.h
===================================================================
--- dll/shellext/shellbtrfs/propsheet.h	(révision 75772)
+++ dll/shellext/shellbtrfs/propsheet.h	(copie de travail)
@@ -1,26 +1,27 @@
-/* Copyright (c) Mark Harmstone 2016
- * 
+/* Copyright (c) Mark Harmstone 2016-17
+ *
  * 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 <shlobj.h>
 #include <deque>
+#include <string>
 #ifndef __REACTOS__
 #include "../btrfsioctl.h"
 #else
-#include "../../drivers/filesystems/btrfs/btrfsioctl.h"
+#include "btrfsioctl.h"
 #endif
 
 #ifndef S_IRUSR
@@ -59,6 +60,18 @@
 #define S_IXOTH (S_IXGRP >> 3)
 #endif
 
+#ifndef S_ISUID
+#define S_ISUID 0004000
+#endif
+
+#ifndef S_ISGID
+#define S_ISGID 0002000
+#endif
+
+#ifndef S_ISVTX
+#define S_ISVTX 0001000
+#endif
+
 #define BTRFS_INODE_NODATASUM   0x001
 #define BTRFS_INODE_NODATACOW   0x002
 #define BTRFS_INODE_READONLY    0x004
@@ -85,15 +98,19 @@
         perms_changed = FALSE;
         uid_changed = FALSE;
         gid_changed = FALSE;
+        compress_type_changed = FALSE;
+        ro_changed = FALSE;
         can_change_perms = FALSE;
-        can_change_owner = FALSE;
+        show_admin_button = FALSE;
         thread = NULL;
         mode = mode_set = 0;
         flags = flags_set = 0;
-        
+        has_subvols = FALSE;
+        filename = L"";
+
         sizes[0] = sizes[1] = sizes[2] = sizes[3] = 0;
         totalsize = 0;
-        
+
         InterlockedIncrement(&objs_loaded);
     }
 
@@ -102,14 +119,14 @@
             GlobalUnlock(stgm.hGlobal);
             ReleaseStgMedium(&stgm);
         }
-        
+
         InterlockedDecrement(&objs_loaded);
     }
 
     // IUnknown
-    
+
     HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
-    
+
     ULONG __stdcall AddRef() {
         return InterlockedIncrement(&refcount);
     }
@@ -116,22 +133,23 @@
 
     ULONG __stdcall Release() {
         LONG rc = InterlockedDecrement(&refcount);
-        
+
         if (rc == 0)
             delete this;
-        
+
         return rc;
     }
-    
+
     // IShellExtInit
-    
+
     virtual HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID);
-    
+
     // IShellPropSheetExt
-    
+
     virtual HRESULT __stdcall AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
     virtual HRESULT __stdcall ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam);
-    
+
+    void init_propsheet(HWND hwndDlg);
     void change_inode_flag(HWND hDlg, UINT64 flag, UINT state);
     void change_perm_flag(HWND hDlg, ULONG perm, UINT state);
     void change_uid(HWND hDlg, UINT32 uid);
@@ -142,10 +160,11 @@
     DWORD search_list_thread();
     void do_search(WCHAR* fn);
     void update_size_details_dialog(HWND hDlg);
- 
+    void open_as_admin(HWND hwndDlg);
+    void set_cmdline(std::wstring cmdline);
+
     BOOL readonly;
     BOOL can_change_perms;
-    BOOL can_change_owner;
     BOOL can_change_nocow;
     WCHAR size_format[255];
     HANDLE thread;
@@ -152,10 +171,11 @@
     UINT32 min_mode, max_mode, mode, mode_set;
     UINT64 min_flags, max_flags, flags, flags_set;
     UINT64 subvol, inode, rdev;
-    UINT8 type;
+    UINT8 type, min_compression_type, max_compression_type, compress_type;
     UINT32 uid, gid;
-    BOOL various_subvols, various_inodes, various_types, various_uids, various_gids;
-    
+    BOOL various_subvols, various_inodes, various_types, various_uids, various_gids, compress_type_changed, has_subvols,
+         ro_subvol, various_ro, ro_changed, show_admin_button;
+
 private:
     LONG refcount;
     BOOL ignore;
@@ -164,4 +184,9 @@
     BOOL flags_changed, perms_changed, uid_changed, gid_changed;
     UINT64 sizes[4], totalsize;
     std::deque<WCHAR*> search_list;
+    std::wstring filename;
+
+    void apply_changes_file(HWND hDlg, std::wstring fn);
+    HRESULT check_file(std::wstring fn, UINT i, UINT num_files, UINT* sv);
+    HRESULT load_file_list();
 };
Index: dll/shellext/shellbtrfs/reactos.cpp
===================================================================
--- dll/shellext/shellbtrfs/reactos.cpp	(nonexistent)
+++ dll/shellext/shellbtrfs/reactos.cpp	(copie de travail)
@@ -0,0 +1,207 @@
+#include "shellext.h"
+#include <initguid.h>
+#include <ntddstor.h>
+#include <ndk/rtlfuncs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* So that we can link */
+DEFINE_GUID(CLSID_WICImagingFactory, 0xcacaf262,0x9370,0x4615,0xa1,0x3b,0x9f,0x55,0x39,0xda,0x4c,0x0a);
+
+/* Copied from ntoskrnl_vista */
+NTSTATUS WINAPI RtlUTF8ToUnicodeN(PWSTR uni_dest, ULONG uni_bytes_max,
+                                  PULONG uni_bytes_written,
+                                  PCCH utf8_src, ULONG utf8_bytes)
+{
+    NTSTATUS status;
+    ULONG i, j;
+    ULONG written;
+    ULONG ch;
+    ULONG utf8_trail_bytes;
+    WCHAR utf16_ch[3];
+    ULONG utf16_ch_len;
+
+    if (!utf8_src)
+        return STATUS_INVALID_PARAMETER_4;
+    if (!uni_bytes_written)
+        return STATUS_INVALID_PARAMETER;
+
+    written = 0;
+    status = STATUS_SUCCESS;
+
+    for (i = 0; i < utf8_bytes; i++)
+    {
+        /* read UTF-8 lead byte */
+        ch = (BYTE)utf8_src[i];
+        utf8_trail_bytes = 0;
+        if (ch >= 0xf5)
+        {
+            ch = 0xfffd;
+            status = STATUS_SOME_NOT_MAPPED;
+        }
+        else if (ch >= 0xf0)
+        {
+            ch &= 0x07;
+            utf8_trail_bytes = 3;
+        }
+        else if (ch >= 0xe0)
+        {
+            ch &= 0x0f;
+            utf8_trail_bytes = 2;
+        }
+        else if (ch >= 0xc2)
+        {
+            ch &= 0x1f;
+            utf8_trail_bytes = 1;
+        }
+        else if (ch >= 0x80)
+        {
+            /* overlong or trail byte */
+            ch = 0xfffd;
+            status = STATUS_SOME_NOT_MAPPED;
+        }
+
+        /* read UTF-8 trail bytes */
+        if (i + utf8_trail_bytes < utf8_bytes)
+        {
+            for (j = 0; j < utf8_trail_bytes; j++)
+            {
+                if ((utf8_src[i + 1] & 0xc0) == 0x80)
+                {
+                    ch <<= 6;
+                    ch |= utf8_src[i + 1] & 0x3f;
+                    i++;
+                }
+                else
+                {
+                    ch = 0xfffd;
+                    utf8_trail_bytes = 0;
+                    status = STATUS_SOME_NOT_MAPPED;
+                    break;
+                }
+            }
+        }
+        else
+        {
+            ch = 0xfffd;
+            utf8_trail_bytes = 0;
+            status = STATUS_SOME_NOT_MAPPED;
+            i = utf8_bytes;
+        }
+
+        /* encode ch as UTF-16 */
+        if ((ch > 0x10ffff) ||
+            (ch >= 0xd800 && ch <= 0xdfff) ||
+            (utf8_trail_bytes == 2 && ch < 0x00800) ||
+            (utf8_trail_bytes == 3 && ch < 0x10000))
+        {
+            /* invalid codepoint or overlong encoding */
+            utf16_ch[0] = 0xfffd;
+            utf16_ch[1] = 0xfffd;
+            utf16_ch[2] = 0xfffd;
+            utf16_ch_len = utf8_trail_bytes;
+            status = STATUS_SOME_NOT_MAPPED;
+        }
+        else if (ch >= 0x10000)
+        {
+            /* surrogate pair */
+            ch -= 0x010000;
+            utf16_ch[0] = 0xd800 + (ch >> 10 & 0x3ff);
+            utf16_ch[1] = 0xdc00 + (ch >>  0 & 0x3ff);
+            utf16_ch_len = 2;
+        }
+        else
+        {
+            /* single unit */
+            utf16_ch[0] = ch;
+            utf16_ch_len = 1;
+        }
+
+        if (!uni_dest)
+        {
+            written += utf16_ch_len;
+            continue;
+        }
+
+        for (j = 0; j < utf16_ch_len; j++)
+        {
+            if (uni_bytes_max >= sizeof(WCHAR))
+            {
+                *uni_dest++ = utf16_ch[j];
+                uni_bytes_max -= sizeof(WCHAR);
+                written++;
+            }
+            else
+            {
+                uni_bytes_max = 0;
+                status = STATUS_BUFFER_TOO_SMALL;
+            }
+        }
+    }
+
+    *uni_bytes_written = written * sizeof(WCHAR);
+    return status;
+}
+
+/* Quick and dirty table for conversion */
+FILE_INFORMATION_CLASS ConvertToFileInfo[MaximumFileInfoByHandlesClass] =
+{
+    FileBasicInformation, FileStandardInformation, FileNameInformation, FileRenameInformation,
+    FileDispositionInformation, FileAllocationInformation, FileEndOfFileInformation, FileStreamInformation,
+    FileCompressionInformation, FileAttributeTagInformation, FileIdBothDirectoryInformation, (FILE_INFORMATION_CLASS)-1,
+    FileIoPriorityHintInformation, FileRemoteProtocolInformation
+};
+
+/* Taken from kernel32 */
+DWORD
+BaseSetLastNTError(IN NTSTATUS Status)
+{
+    DWORD dwErrCode;
+    dwErrCode = RtlNtStatusToDosError(Status);
+    SetLastError(dwErrCode);
+    return dwErrCode;
+}
+
+/* Quick implementation, still going farther than Wine implementation */
+BOOL
+WINAPI
+SetFileInformationByHandle(HANDLE hFile,
+                           FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
+                           LPVOID lpFileInformation,
+                           DWORD dwBufferSize)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_INFORMATION_CLASS FileInfoClass;
+
+    FileInfoClass = (FILE_INFORMATION_CLASS)-1;
+
+    /* Attempt to convert the class */
+    if (FileInformationClass < MaximumFileInfoByHandlesClass)
+    {
+        FileInfoClass = ConvertToFileInfo[FileInformationClass];
+    }
+
+    /* If wrong, bail out */
+    if (FileInfoClass == -1)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    /* And set the information */
+    Status = NtSetInformationFile(hFile, &IoStatusBlock, lpFileInformation,
+                                  dwBufferSize, FileInfoClass);
+
+    if (!NT_SUCCESS(Status))
+    {
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+#ifdef __cplusplus
+}
+#endif

Modification de propriétés sur dll/shellext/shellbtrfs/reactos.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/recv.cpp
===================================================================
--- dll/shellext/shellbtrfs/recv.cpp	(nonexistent)
+++ dll/shellext/shellbtrfs/recv.cpp	(copie de travail)
@@ -0,0 +1,2146 @@
+/* Copyright (c) Mark Harmstone 2017
+ *
+ * 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 "shellext.h"
+#include <windows.h>
+#include <strsafe.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <string>
+#include "recv.h"
+#include "resource.h"
+
+#ifndef __REACTOS__
+#ifndef _MSC_VER
+#include <cpuid.h>
+#else
+#include <intrin.h>
+#endif
+
+#include <smmintrin.h>
+#endif
+
+#define EA_NTACL "security.NTACL"
+#define EA_DOSATTRIB "user.DOSATTRIB"
+#define EA_REPARSE "user.reparse"
+#define EA_EA "user.EA"
+#define XATTR_USER "user."
+
+#ifndef __REACTOS__
+BOOL have_sse42 = FALSE;
+#endif
+
+static const UINT32 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,
+    0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
+    0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
+    0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
+    0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
+    0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
+    0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
+    0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
+    0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
+    0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
+    0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
+    0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
+    0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
+    0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
+    0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
+    0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
+    0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
+    0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
+    0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
+    0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
+    0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
+    0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
+    0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
+    0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
+    0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
+    0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
+    0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
+    0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
+    0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
+    0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
+};
+
+// 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)
+
+#ifndef __REACTOS__
+static UINT32 crc32c_hw(const void *input, ULONG len, UINT32 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(!)
+#pragma warning(disable:4242)
+#endif
+    CALC_CRC(_mm_crc32_u64, crc, UINT64, buf, len);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+#endif
+    CALC_CRC(_mm_crc32_u32, crc, UINT32, 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, buf, len);
+    CALC_CRC(_mm_crc32_u8, crc, UINT8, buf, len);
+#endif
+
+    return crc;
+}
+#endif
+
+static UINT32 calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen) {
+#ifndef __REACTOS__
+    if (have_sse42)
+        return crc32c_hw(msg, msglen, seed);
+    else {
+#endif
+        UINT32 rem;
+        ULONG i;
+
+        rem = seed;
+
+        for (i = 0; i < msglen; i++) {
+            rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8);
+        }
+
+        return rem;
+#ifndef __REACTOS__
+    }
+#endif
+}
+
+BOOL BtrfsRecv::find_tlv(UINT8* data, ULONG datalen, UINT16 type, void** value, ULONG* len) {
+    ULONG off = 0;
+
+    while (off < datalen) {
+        btrfs_send_tlv* tlv = (btrfs_send_tlv*)(data + off);
+        UINT8* payload = data + off + sizeof(btrfs_send_tlv);
+
+        if (off + sizeof(btrfs_send_tlv) + tlv->length > datalen) // file is truncated
+            return FALSE;
+
+        if (tlv->type == type) {
+            *value = payload;
+            *len = tlv->length;
+            return TRUE;
+        }
+
+        off += sizeof(btrfs_send_tlv) + tlv->length;
+    }
+
+    return FALSE;
+}
+
+BOOL BtrfsRecv::utf8_to_utf16(HWND hwnd, char* utf8, ULONG utf8len, std::wstring* utf16) {
+    NTSTATUS Status;
+    ULONG utf16len;
+    WCHAR* buf;
+
+    Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, utf8, utf8len);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_RTLUTF8TOUNICODEN_FAILED, Status, format_ntstatus(Status).c_str());
+        return FALSE;
+    }
+
+    buf = (WCHAR*)malloc(utf16len + sizeof(WCHAR));
+
+    if (!buf) {
+        ShowRecvError(IDS_OUT_OF_MEMORY);
+        return FALSE;
+    }
+
+    Status = RtlUTF8ToUnicodeN(buf, utf16len, &utf16len, utf8, utf8len);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_RTLUTF8TOUNICODEN_FAILED, Status, format_ntstatus(Status).c_str());
+        free(buf);
+        return FALSE;
+    }
+
+    buf[utf16len / sizeof(WCHAR)] = 0;
+
+    *utf16 = buf;
+
+    free(buf);
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_subvol(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char* name;
+    BTRFS_UUID* uuid;
+    UINT64* gen;
+    ULONG namelen, uuidlen, genlen, bcslen;
+    btrfs_create_subvol* bcs;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    std::wstring nameu;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
+        return FALSE;
+    }
+
+    if (uuidlen < sizeof(BTRFS_UUID)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"transid");
+        return FALSE;
+    }
+
+    if (genlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    this->subvol_uuid = *uuid;
+    this->stransid = *gen;
+
+    if (!utf8_to_utf16(hwnd, name, namelen, &nameu))
+        return FALSE;
+
+    bcslen = offsetof(btrfs_create_subvol, name[0]) + (nameu.length() * sizeof(WCHAR));
+    bcs = (btrfs_create_subvol*)malloc(bcslen);
+
+    bcs->readonly = TRUE;
+    bcs->posix = TRUE;
+    bcs->namelen = nameu.length() * sizeof(WCHAR);
+    memcpy(bcs->name, nameu.c_str(), bcs->namelen);
+
+    Status = NtFsControlFile(parent, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_CREATE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
+        return FALSE;
+    }
+
+    subvolpath = dirpath;
+    subvolpath += L"\\";
+    subvolpath += nameu;
+
+    if (dir != INVALID_HANDLE_VALUE)
+        CloseHandle(dir);
+
+    if (master != INVALID_HANDLE_VALUE)
+        CloseHandle(master);
+
+    master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (master == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    Status = NtFsControlFile(master, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, bcslen, NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
+        return FALSE;
+    }
+
+    dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                      NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (dir == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    subvolpath += L"\\";
+
+    add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
+
+    num_received++;
+
+    return TRUE;
+}
+
+void BtrfsRecv::add_cache_entry(BTRFS_UUID* uuid, UINT64 transid, std::wstring path) {
+    subvol_cache sc;
+
+    sc.uuid = *uuid;
+    sc.transid = transid;
+    sc.path = path;
+
+    cache.push_back(sc);
+}
+
+BOOL BtrfsRecv::cmd_snapshot(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char* name;
+    BTRFS_UUID *uuid, *parent_uuid;
+    UINT64 *gen, *parent_transid;
+    ULONG namelen, uuidlen, genlen, paruuidlen, partransidlen, bcslen;
+    btrfs_create_snapshot* bcs;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    std::wstring nameu, parpath;
+    btrfs_find_subvol bfs;
+    WCHAR parpathw[MAX_PATH], volpathw[MAX_PATH];
+    HANDLE subvol;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
+        return FALSE;
+    }
+
+    if (uuidlen < sizeof(BTRFS_UUID)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"transid");
+        return FALSE;
+    }
+
+    if (genlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&parent_uuid, &paruuidlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
+        return FALSE;
+    }
+
+    if (paruuidlen < sizeof(BTRFS_UUID)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", paruuidlen, sizeof(BTRFS_UUID));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&parent_transid, &partransidlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
+        return FALSE;
+    }
+
+    if (partransidlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", partransidlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    this->subvol_uuid = *uuid;
+    this->stransid = *gen;
+
+    if (!utf8_to_utf16(hwnd, name, namelen, &nameu))
+        return FALSE;
+
+    bfs.uuid = *parent_uuid;
+    bfs.ctransid = *parent_transid;
+
+    Status = NtFsControlFile(parent, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol),
+                             parpathw, sizeof(parpathw));
+    if (Status == STATUS_NOT_FOUND) {
+        ShowRecvError(IDS_RECV_CANT_FIND_PARENT_SUBVOL);
+        return FALSE;
+    } else if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
+        return FALSE;
+    }
+
+    if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) {
+        ShowRecvError(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    parpath = volpathw;
+    if (parpath.substr(parpath.length() - 1) == L"\\")
+        parpath = parpath.substr(0, parpath.length() - 1);
+
+    parpath += parpathw;
+
+    subvol = CreateFileW(parpath.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    if (subvol == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_PATH, parpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    bcslen = offsetof(btrfs_create_snapshot, name[0]) + (nameu.length() * sizeof(WCHAR));
+    bcs = (btrfs_create_snapshot*)malloc(bcslen);
+
+    bcs->readonly = TRUE;
+    bcs->posix = TRUE;
+    bcs->subvol = subvol;
+    bcs->namelen = nameu.length() * sizeof(WCHAR);
+    memcpy(bcs->name, nameu.c_str(), bcs->namelen);
+
+    Status = NtFsControlFile(parent, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, bcslen, NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_CREATE_SNAPSHOT_FAILED, Status, format_ntstatus(Status).c_str());
+        return FALSE;
+    }
+
+    subvolpath = dirpath;
+    subvolpath += L"\\";
+    subvolpath += nameu;
+
+    if (dir != INVALID_HANDLE_VALUE)
+        CloseHandle(dir);
+
+    if (master != INVALID_HANDLE_VALUE)
+        CloseHandle(master);
+
+    master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (master == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    Status = NtFsControlFile(master, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, bcslen, NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
+        return FALSE;
+    }
+
+    dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                      NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (dir == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    subvolpath += L"\\";
+
+    add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
+
+    num_received++;
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_mkfile(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char *name, *pathlink;
+    UINT64 *inode, *rdev = NULL, *mode = NULL;
+    ULONG namelen, inodelen, bmnsize;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_mknod* bmn;
+    std::wstring nameu, pathlinku;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_INODE, (void**)&inode, &inodelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"inode");
+        return FALSE;
+    }
+
+    if (inodelen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"inode", inodelen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) {
+        ULONG rdevlen, modelen;
+
+        if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_RDEV, (void**)&rdev, &rdevlen)) {
+            ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"rdev");
+            return FALSE;
+        }
+
+        if (rdevlen < sizeof(UINT64)) {
+            ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"rdev", rdev, sizeof(UINT64));
+            return FALSE;
+        }
+
+        if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
+            ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"mode");
+            return FALSE;
+        }
+
+        if (modelen < sizeof(UINT64)) {
+            ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(UINT64));
+            return FALSE;
+        }
+    } else if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) {
+        ULONG pathlinklen;
+
+        if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&pathlink, &pathlinklen)) {
+            ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
+            return FALSE;
+        }
+
+        if (!utf8_to_utf16(hwnd, pathlink, pathlinklen, &pathlinku))
+            return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, name, namelen, &nameu))
+        return FALSE;
+
+    bmnsize = sizeof(btrfs_mknod) - sizeof(WCHAR) + (nameu.length() * sizeof(WCHAR));
+    bmn = (btrfs_mknod*)malloc(bmnsize);
+
+    bmn->inode = *inode;
+
+    if (cmd->cmd == BTRFS_SEND_CMD_MKDIR)
+        bmn->type = BTRFS_TYPE_DIRECTORY;
+    else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD)
+        bmn->type = *mode & S_IFCHR ? BTRFS_TYPE_CHARDEV : BTRFS_TYPE_BLOCKDEV;
+    else if (cmd->cmd == BTRFS_SEND_CMD_MKFIFO)
+        bmn->type = BTRFS_TYPE_FIFO;
+    else if (cmd->cmd == BTRFS_SEND_CMD_MKSOCK)
+        bmn->type = BTRFS_TYPE_SOCKET;
+    else
+        bmn->type = BTRFS_TYPE_FILE;
+
+    bmn->st_rdev = rdev ? *rdev : 0;
+    bmn->namelen = nameu.length() * sizeof(WCHAR);
+    memcpy(bmn->name, nameu.c_str(), bmn->namelen);
+
+    Status = NtFsControlFile(dir, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_MKNOD, bmn, bmnsize, NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_MKNOD_FAILED, Status, format_ntstatus(Status).c_str());
+        free(bmn);
+        return FALSE;
+    }
+
+    free(bmn);
+
+    if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) {
+        HANDLE h;
+        REPARSE_DATA_BUFFER* rdb;
+        ULONG rdblen;
+        btrfs_set_inode_info bsii;
+
+        rdblen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]) + (2 * pathlinku.length() * sizeof(WCHAR));
+
+        rdb = (REPARSE_DATA_BUFFER*)malloc(rdblen);
+
+        rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+        rdb->ReparseDataLength = rdblen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
+        rdb->Reserved = 0;
+        rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+        rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = pathlinku.length() * sizeof(WCHAR);
+        rdb->SymbolicLinkReparseBuffer.PrintNameOffset = pathlinku.length() * sizeof(WCHAR);
+        rdb->SymbolicLinkReparseBuffer.PrintNameLength = pathlinku.length() * sizeof(WCHAR);
+        rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
+
+        memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer, pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
+        memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer + (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)),
+                pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.PrintNameLength);
+
+        h = CreateFileW((subvolpath + nameu).c_str(), GENERIC_WRITE | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+            free(rdb);
+            return FALSE;
+        }
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_SET_REPARSE_POINT, rdb, rdblen, NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            ShowRecvError(IDS_RECV_SET_REPARSE_POINT_FAILED, Status, format_ntstatus(Status).c_str());
+            free(rdb);
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        free(rdb);
+
+        memset(&bsii, 0, sizeof(btrfs_set_inode_info));
+
+        bsii.mode_changed = TRUE;
+        bsii.st_mode = 0777;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        CloseHandle(h);
+    } else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) {
+        UINT64* mode;
+        ULONG modelen;
+
+        if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
+            HANDLE h;
+            btrfs_set_inode_info bsii;
+
+            if (modelen < sizeof(UINT64)) {
+                ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(UINT64));
+                return FALSE;
+            }
+
+            h = CreateFileW((subvolpath + nameu).c_str(), WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+            if (h == INVALID_HANDLE_VALUE) {
+                ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+
+            memset(&bsii, 0, sizeof(btrfs_set_inode_info));
+
+            bsii.mode_changed = TRUE;
+            bsii.st_mode = *mode;
+
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
+            if (!NT_SUCCESS(Status)) {
+                ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
+                CloseHandle(h);
+                return FALSE;
+            }
+
+            CloseHandle(h);
+        }
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_rename(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char *path, *path_to;
+    ULONG path_len, path_to_len;
+    std::wstring pathu, path_tou;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_TO, (void**)&path_to, &path_to_len)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path_to");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, path_len, &pathu))
+        return FALSE;
+
+    if (!utf8_to_utf16(hwnd, path_to, path_to_len, &path_tou))
+        return FALSE;
+
+    if (!MoveFileW((subvolpath + pathu).c_str(), (subvolpath + path_tou).c_str())) {
+        ShowRecvError(IDS_RECV_MOVEFILE_FAILED, pathu.c_str(), path_tou.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_link(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char *path, *path_link;
+    ULONG path_len, path_link_len;
+    std::wstring pathu, path_linku;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&path_link, &path_link_len)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, path_len, &pathu))
+        return FALSE;
+
+    if (!utf8_to_utf16(hwnd, path_link, path_link_len, &path_linku))
+        return FALSE;
+
+    if (!CreateHardLinkW((subvolpath + pathu).c_str(), (subvolpath + path_linku).c_str(), NULL)) {
+        ShowRecvError(IDS_RECV_CREATEHARDLINK_FAILED, pathu.c_str(), path_linku.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_unlink(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char* path;
+    ULONG pathlen;
+    std::wstring pathu;
+    ULONG att;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    att = GetFileAttributesW((subvolpath + pathu).c_str());
+    if (att == INVALID_FILE_ATTRIBUTES) {
+        ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    if (att & FILE_ATTRIBUTE_READONLY) {
+        if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
+            ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+    }
+
+    if (!DeleteFileW((subvolpath + pathu).c_str())) {
+        ShowRecvError(IDS_RECV_DELETEFILE_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_rmdir(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char* path;
+    ULONG pathlen;
+    std::wstring pathu;
+    ULONG att;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    att = GetFileAttributesW((subvolpath + pathu).c_str());
+    if (att == INVALID_FILE_ATTRIBUTES) {
+        ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    if (att & FILE_ATTRIBUTE_READONLY) {
+        if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
+            ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+    }
+
+    if (!RemoveDirectoryW((subvolpath + pathu).c_str())) {
+        ShowRecvError(IDS_RECV_REMOVEDIRECTORY_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_setxattr(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char *path, *xattrname;
+    UINT8* xattrdata;
+    ULONG pathlen, xattrnamelen, xattrdatalen;
+    std::wstring pathu;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrname, &xattrnamelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_DATA, (void**)&xattrdata, &xattrdatalen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"xattr_data");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    if (xattrnamelen > strlen(XATTR_USER) && !memcmp(xattrname, XATTR_USER, strlen(XATTR_USER)) &&
+        (xattrnamelen != strlen(EA_DOSATTRIB) || memcmp(xattrname, EA_DOSATTRIB, xattrnamelen)) &&
+        (xattrnamelen != strlen(EA_EA) || memcmp(xattrname, EA_EA, xattrnamelen)) &&
+        (xattrnamelen != strlen(EA_REPARSE) || memcmp(xattrname, EA_REPARSE, xattrnamelen))) {
+        HANDLE h;
+        std::wstring streamname;
+        ULONG att;
+
+        if (!utf8_to_utf16(hwnd, xattrname, xattrnamelen, &streamname))
+            return FALSE;
+
+        att = GetFileAttributesW((subvolpath + pathu).c_str());
+        if (att == INVALID_FILE_ATTRIBUTES) {
+            ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        if (att & FILE_ATTRIBUTE_READONLY) {
+            if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
+                ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+        }
+
+        streamname = streamname.substr(strlen(XATTR_USER));
+
+        h = CreateFileW((subvolpath + pathu + L":" + streamname).c_str(), GENERIC_WRITE, 0,
+                        NULL, CREATE_ALWAYS, FILE_FLAG_POSIX_SEMANTICS, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ShowRecvError(IDS_RECV_CANT_CREATE_FILE, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        if (xattrdatalen > 0) {
+            if (!WriteFile(h, xattrdata, xattrdatalen, NULL, NULL)) {
+                ShowRecvError(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                CloseHandle(h);
+                return FALSE;
+            }
+        }
+
+        CloseHandle(h);
+
+        if (att & FILE_ATTRIBUTE_READONLY) {
+            if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) {
+                ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+        }
+    } else {
+        HANDLE h;
+        IO_STATUS_BLOCK iosb;
+        NTSTATUS Status;
+        ULONG bsxalen, perms = FILE_WRITE_ATTRIBUTES;
+        btrfs_set_xattr* bsxa;
+
+        if (xattrnamelen == strlen(EA_NTACL) && !memcmp(xattrname, EA_NTACL, xattrnamelen))
+            perms |= WRITE_DAC | WRITE_OWNER;
+        else if (xattrnamelen == strlen(EA_EA) && !memcmp(xattrname, EA_EA, xattrnamelen))
+            perms |= FILE_WRITE_EA;
+
+        h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrnamelen + xattrdatalen;
+        bsxa = (btrfs_set_xattr*)malloc(bsxalen);
+        if (!bsxa) {
+            ShowRecvError(IDS_OUT_OF_MEMORY);
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        bsxa->namelen = xattrnamelen;
+        bsxa->valuelen = xattrdatalen;
+        memcpy(bsxa->data, xattrname, xattrnamelen);
+        memcpy(&bsxa->data[xattrnamelen], xattrdata, xattrdatalen);
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, bsxalen, NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            ShowRecvError(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
+            free(bsxa);
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        free(bsxa);
+        CloseHandle(h);
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_removexattr(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char *path, *xattrname;
+    ULONG pathlen, xattrnamelen;
+    std::wstring pathu;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrname, &xattrnamelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    if (xattrnamelen > strlen(XATTR_USER) && !memcmp(xattrname, XATTR_USER, strlen(XATTR_USER)) &&
+        (xattrnamelen != strlen(EA_DOSATTRIB) || memcmp(xattrname, EA_DOSATTRIB, xattrnamelen)) &&
+        (xattrnamelen != strlen(EA_EA) || memcmp(xattrname, EA_EA, xattrnamelen))) { // deleting stream
+        ULONG att;
+        std::wstring streamname;
+
+        if (!utf8_to_utf16(hwnd, xattrname, xattrnamelen, &streamname))
+            return FALSE;
+
+        streamname = streamname.substr(strlen(XATTR_USER));
+
+        att = GetFileAttributesW((subvolpath + pathu).c_str());
+        if (att == INVALID_FILE_ATTRIBUTES) {
+            ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        if (att & FILE_ATTRIBUTE_READONLY) {
+            if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
+                ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+        }
+
+        if (!DeleteFileW((subvolpath + pathu + L":" + streamname).c_str())) {
+            ShowRecvError(IDS_RECV_DELETEFILE_FAILED, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        if (att & FILE_ATTRIBUTE_READONLY) {
+            if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) {
+                ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+        }
+    } else {
+        HANDLE h;
+        IO_STATUS_BLOCK iosb;
+        NTSTATUS Status;
+        ULONG bsxalen, perms = FILE_WRITE_ATTRIBUTES;
+        btrfs_set_xattr* bsxa;
+
+        if (xattrnamelen == strlen(EA_NTACL) && !memcmp(xattrname, EA_NTACL, xattrnamelen))
+            perms |= WRITE_DAC | WRITE_OWNER;
+        else if (xattrnamelen == strlen(EA_EA) && !memcmp(xattrname, EA_EA, xattrnamelen))
+            perms |= FILE_WRITE_EA;
+
+        h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrnamelen;
+        bsxa = (btrfs_set_xattr*)malloc(bsxalen);
+        if (!bsxa) {
+            ShowRecvError(IDS_OUT_OF_MEMORY);
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        bsxa->namelen = xattrnamelen;
+        bsxa->valuelen = 0;
+        memcpy(bsxa->data, xattrname, xattrnamelen);
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, bsxalen, NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            ShowRecvError(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
+            free(bsxa);
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        free(bsxa);
+        CloseHandle(h);
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_write(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char* path;
+    UINT64* offset;
+    UINT8* writedata;
+    ULONG pathlen, offsetlen, datalen;
+    std::wstring pathu;
+    HANDLE h;
+    LARGE_INTEGER offli;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"offset");
+        return FALSE;
+    }
+
+    if (offsetlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_DATA, (void**)&writedata, &datalen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"data");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    if (lastwritepath != pathu) {
+        FILE_BASIC_INFO fbi;
+
+        if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
+            if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) {
+                ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+        }
+
+        CloseHandle(lastwritefile);
+
+        lastwriteatt = GetFileAttributesW((subvolpath + pathu).c_str());
+        if (lastwriteatt == INVALID_FILE_ATTRIBUTES) {
+            ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
+            if (!SetFileAttributesW((subvolpath + pathu).c_str(), lastwriteatt & ~FILE_ATTRIBUTE_READONLY)) {
+                ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+        }
+
+        h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
+                        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        lastwritepath = pathu;
+        lastwritefile = h;
+
+        memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
+
+        fbi.LastWriteTime.QuadPart = -1;
+
+        if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) {
+            ShowRecvError(IDS_RECV_SETFILEINFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+    } else
+        h = lastwritefile;
+
+    offli.QuadPart = *offset;
+
+    if (SetFilePointer(h, offli.LowPart, &offli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+        ShowRecvError(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    if (!WriteFile(h, writedata, datalen, NULL, NULL)) {
+        ShowRecvError(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_clone(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char *path, *clonepath;
+    UINT64 *offset, *cloneoffset, *clonetransid, *clonelen;
+    BTRFS_UUID* cloneuuid;
+    ULONG i, offsetlen, pathlen, clonepathlen, cloneoffsetlen, cloneuuidlen, clonetransidlen, clonelenlen;
+    std::wstring pathu, clonepathu, clonepar;
+    btrfs_find_subvol bfs;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    WCHAR cloneparw[MAX_PATH];
+    HANDLE src, dest;
+    DUPLICATE_EXTENTS_DATA ded;
+    LARGE_INTEGER filesize;
+    BOOL found = FALSE;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"offset");
+        return FALSE;
+    }
+
+    if (offsetlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_LENGTH, (void**)&clonelen, &clonelenlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_len");
+        return FALSE;
+    }
+
+    if (clonelenlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_len", clonelenlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&cloneuuid, &cloneuuidlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
+        return FALSE;
+    }
+
+    if (cloneuuidlen < sizeof(BTRFS_UUID)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", cloneuuidlen, sizeof(BTRFS_UUID));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&clonetransid, &clonetransidlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
+        return FALSE;
+    }
+
+    if (clonetransidlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", clonetransidlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_PATH, (void**)&clonepath, &clonepathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_path");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, clonepath, clonepathlen, &clonepathu))
+        return FALSE;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_OFFSET, (void**)&cloneoffset, &cloneoffsetlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_offset");
+        return FALSE;
+    }
+
+    if (cloneoffsetlen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_offset", cloneoffsetlen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    for (i = 0; i < cache.size(); i++) {
+        if (!memcmp(cloneuuid, &cache[i].uuid, sizeof(BTRFS_UUID)) && *clonetransid == cache[i].transid) {
+            clonepar = cache[i].path;
+            found = TRUE;
+            break;
+        }
+    }
+
+    if (!found) {
+        WCHAR volpathw[MAX_PATH];
+
+        bfs.uuid = *cloneuuid;
+        bfs.ctransid = *clonetransid;
+
+        Status = NtFsControlFile(dir, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol),
+                                 cloneparw, sizeof(cloneparw));
+        if (Status == STATUS_NOT_FOUND) {
+            ShowRecvError(IDS_RECV_CANT_FIND_CLONE_SUBVOL);
+            return FALSE;
+        } else if (!NT_SUCCESS(Status)) {
+            ShowRecvError(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
+            return FALSE;
+        }
+
+        if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) {
+            ShowRecvError(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+
+        clonepar = volpathw;
+        if (clonepar.substr(clonepar.length() - 1) == L"\\")
+            clonepar = clonepar.substr(0, clonepar.length() - 1);
+
+        clonepar += cloneparw;
+        clonepar += L"\\";
+
+        add_cache_entry(cloneuuid, *clonetransid, clonepar);
+    }
+
+    src = CreateFileW((clonepar + clonepathu).c_str(), FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                      NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (src == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, (clonepar + clonepathu).c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    dest = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (dest == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        CloseHandle(src);
+        return FALSE;
+    }
+
+    if (!GetFileSizeEx(dest, &filesize)) {
+        ShowRecvError(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        CloseHandle(dest);
+        CloseHandle(src);
+        return FALSE;
+    }
+
+    if ((UINT64)filesize.QuadPart < *offset + *clonelen) {
+        LARGE_INTEGER sizeli;
+
+        sizeli.QuadPart = *offset + *clonelen;
+
+        if (SetFilePointer(dest, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+            ShowRecvError(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            CloseHandle(dest);
+            CloseHandle(src);
+            return FALSE;
+        }
+
+        if (!SetEndOfFile(dest)) {
+            ShowRecvError(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            CloseHandle(dest);
+            CloseHandle(src);
+            return FALSE;
+        }
+    }
+
+    ded.FileHandle = src;
+    ded.SourceFileOffset.QuadPart = *cloneoffset;
+    ded.TargetFileOffset.QuadPart = *offset;
+    ded.ByteCount.QuadPart = *clonelen;
+
+    Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA),
+                             NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_DUPLICATE_EXTENTS_FAILED, Status, format_ntstatus(Status).c_str());
+        CloseHandle(dest);
+        CloseHandle(src);
+        return FALSE;
+    }
+
+    CloseHandle(dest);
+    CloseHandle(src);
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_truncate(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char* path;
+    UINT64* size;
+    ULONG pathlen, sizelen;
+    std::wstring pathu;
+    HANDLE h;
+    LARGE_INTEGER sizeli;
+    DWORD att;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_SIZE, (void**)&size, &sizelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"size");
+        return FALSE;
+    }
+
+    if (sizelen < sizeof(UINT64)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"size", sizelen, sizeof(UINT64));
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    att = GetFileAttributesW((subvolpath + pathu).c_str());
+    if (att == INVALID_FILE_ATTRIBUTES) {
+        ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    if (att & FILE_ATTRIBUTE_READONLY) {
+        if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
+            ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+    }
+
+    h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, 0, NULL, OPEN_EXISTING,
+                    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    sizeli.QuadPart = *size;
+
+    if (SetFilePointer(h, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+        ShowRecvError(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        CloseHandle(h);
+        return FALSE;
+    }
+
+    if (!SetEndOfFile(h)) {
+        ShowRecvError(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        CloseHandle(h);
+        return FALSE;
+    }
+
+    CloseHandle(h);
+
+    if (att & FILE_ATTRIBUTE_READONLY) {
+        if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) {
+            ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_chmod(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    HANDLE h;
+    char* path;
+    UINT32* mode;
+    ULONG pathlen, modelen;
+    std::wstring pathu;
+    btrfs_set_inode_info bsii;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"mode");
+        return FALSE;
+    }
+
+    if (modelen < sizeof(UINT32)) {
+        ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(UINT32));
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    h = CreateFileW((subvolpath + pathu).c_str(), WRITE_DAC, 0, NULL, OPEN_EXISTING,
+                    FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    memset(&bsii, 0, sizeof(btrfs_set_inode_info));
+
+    bsii.mode_changed = TRUE;
+    bsii.st_mode = *mode;
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
+        CloseHandle(h);
+        return FALSE;
+    }
+
+    CloseHandle(h);
+
+    return TRUE;
+}
+
+BOOL BtrfsRecv::cmd_chown(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    HANDLE h;
+    char* path;
+    UINT32 *uid, *gid;
+    ULONG pathlen, uidlen, gidlen;
+    std::wstring pathu;
+    btrfs_set_inode_info bsii;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES | WRITE_OWNER | WRITE_DAC, 0, NULL, OPEN_EXISTING,
+                    FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    memset(&bsii, 0, sizeof(btrfs_set_inode_info));
+
+    if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_UID, (void**)&uid, &uidlen)) {
+        if (uidlen < sizeof(UINT32)) {
+            ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"uid", uidlen, sizeof(UINT32));
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        bsii.uid_changed = TRUE;
+        bsii.st_uid = *uid;
+    }
+
+    if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_GID, (void**)&gid, &gidlen)) {
+        if (gidlen < sizeof(UINT32)) {
+            ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"gid", gidlen, sizeof(UINT32));
+            CloseHandle(h);
+            return FALSE;
+        }
+
+        bsii.gid_changed = TRUE;
+        bsii.st_gid = *gid;
+    }
+
+    if (bsii.uid_changed || bsii.gid_changed) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
+            CloseHandle(h);
+            return FALSE;
+        }
+    }
+
+    CloseHandle(h);
+
+    return TRUE;
+}
+
+static __inline UINT64 unix_time_to_win(BTRFS_TIME* t) {
+    return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000;
+}
+
+BOOL BtrfsRecv::cmd_utimes(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
+    char* path;
+    ULONG pathlen;
+    std::wstring pathu;
+    HANDLE h;
+    FILE_BASIC_INFO fbi;
+    BTRFS_TIME* time;
+    ULONG timelen;
+
+    if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
+        ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
+        return FALSE;
+    }
+
+    if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
+        return FALSE;
+
+    h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
+                    FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
+
+    if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_OTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
+        fbi.CreationTime.QuadPart = unix_time_to_win(time);
+
+    if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_ATIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
+        fbi.LastAccessTime.QuadPart = unix_time_to_win(time);
+
+    if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
+        fbi.LastWriteTime.QuadPart = unix_time_to_win(time);
+
+    if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_CTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
+        fbi.ChangeTime.QuadPart = unix_time_to_win(time);
+
+    if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) {
+        ShowRecvError(IDS_RECV_SETFILEINFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        CloseHandle(h);
+        return FALSE;
+    }
+
+    CloseHandle(h);
+
+    return TRUE;
+}
+
+void BtrfsRecv::ShowRecvError(int resid, ...) {
+    WCHAR s[1024], t[1024];
+    va_list ap;
+
+    if (!hwnd)
+        return;
+
+    if (!LoadStringW(module, resid, s, sizeof(s) / sizeof(WCHAR))) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    va_start(ap, resid);
+#ifndef __REACTOS__
+    vswprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
+#else
+    vsnwprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
+#endif
+
+    SetDlgItemTextW(hwnd, IDC_RECV_MSG, t);
+
+    va_end(ap);
+
+    SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0);
+}
+
+static void delete_directory(std::wstring dir) {
+    HANDLE h;
+    WIN32_FIND_DATAW fff;
+
+    h = FindFirstFileW((dir + L"*").c_str(), &fff);
+
+    if (h == INVALID_HANDLE_VALUE)
+        return;
+
+    do {
+        std::wstring file;
+
+        file = fff.cFileName;
+
+        if (file != L"." && file != L"..") {
+            if (fff.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+                SetFileAttributesW((dir + file).c_str(), fff.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
+
+            if (fff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+                if (!(fff.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+                    delete_directory(dir + file + L"\\");
+                else
+                    RemoveDirectoryW((dir + file).c_str());
+            } else
+                DeleteFileW((dir + file).c_str());
+        }
+    } while (FindNextFileW(h, &fff));
+
+    FindClose(h);
+
+    RemoveDirectoryW(dir.c_str());
+}
+
+static BOOL check_csum(btrfs_send_command* cmd, UINT8* data) {
+    UINT32 crc32 = cmd->csum, calc;
+
+    cmd->csum = 0;
+
+    calc = calc_crc32c(0, (UINT8*)cmd, sizeof(btrfs_send_command));
+
+    if (cmd->length > 0)
+        calc = calc_crc32c(calc, data, cmd->length);
+
+    return calc == crc32 ? TRUE : FALSE;
+}
+
+BOOL BtrfsRecv::do_recv(HANDLE f, UINT64* pos, UINT64 size) {
+    btrfs_send_header header;
+    BOOL b = TRUE, ended = FALSE;
+
+    if (!ReadFile(f, &header, sizeof(btrfs_send_header), NULL, NULL)) {
+        ShowRecvError(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return FALSE;
+    }
+
+    *pos += sizeof(btrfs_send_header);
+
+    if (memcmp(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic))) {
+        ShowRecvError(IDS_RECV_NOT_A_SEND_STREAM);
+        return FALSE;
+    }
+
+    if (header.version > 1) {
+        ShowRecvError(IDS_RECV_UNSUPPORTED_VERSION, header.version);
+        return FALSE;
+    }
+
+    SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)65536);
+
+    lastwritefile = INVALID_HANDLE_VALUE;
+    lastwritepath = L"";
+    lastwriteatt = 0;
+
+    while (TRUE) {
+        btrfs_send_command cmd;
+        UINT8* data;
+        ULONG progress;
+
+        if (cancelling) {
+            b = FALSE;
+            break;
+        }
+
+        progress = (ULONG)((float)*pos * 65536.0f / (float)size);
+        SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, progress, 0);
+
+        if (!ReadFile(f, &cmd, sizeof(btrfs_send_command), NULL, NULL)) {
+            if (GetLastError() != ERROR_HANDLE_EOF) {
+                ShowRecvError(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                break;
+            }
+
+            break;
+        }
+
+        *pos += sizeof(btrfs_send_command);
+
+        if (cmd.length > 0) {
+            if (*pos + cmd.length > size) {
+                ShowRecvError(IDS_RECV_FILE_TRUNCATED);
+                b = FALSE;
+                break;
+            }
+
+            data = (UINT8*)malloc(cmd.length);
+            if (!data) {
+                ShowRecvError(IDS_OUT_OF_MEMORY);
+                b = FALSE;
+                break;
+            }
+
+            if (!ReadFile(f, data, cmd.length, NULL, NULL)) {
+                ShowRecvError(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                b = FALSE;
+                break;
+            }
+
+            *pos += cmd.length;
+        } else
+            data = NULL;
+
+        if (!check_csum(&cmd, data)) {
+            ShowRecvError(IDS_RECV_CSUM_ERROR);
+            if (data) free(data);
+            b = FALSE;
+            break;
+        }
+
+        if (cmd.cmd == BTRFS_SEND_CMD_END) {
+            if (data) free(data);
+            ended = TRUE;
+            break;
+        }
+
+        if (lastwritefile != INVALID_HANDLE_VALUE && cmd.cmd != BTRFS_SEND_CMD_WRITE) {
+            if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
+                if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) {
+                    ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                    return FALSE;
+                }
+            }
+
+            CloseHandle(lastwritefile);
+
+            lastwritefile = INVALID_HANDLE_VALUE;
+            lastwritepath = L"";
+            lastwriteatt = 0;
+        }
+
+        switch (cmd.cmd) {
+            case BTRFS_SEND_CMD_SUBVOL:
+                b = cmd_subvol(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_SNAPSHOT:
+                b = cmd_snapshot(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_MKFILE:
+            case BTRFS_SEND_CMD_MKDIR:
+            case BTRFS_SEND_CMD_MKNOD:
+            case BTRFS_SEND_CMD_MKFIFO:
+            case BTRFS_SEND_CMD_MKSOCK:
+            case BTRFS_SEND_CMD_SYMLINK:
+                b = cmd_mkfile(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_RENAME:
+                b = cmd_rename(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_LINK:
+                b = cmd_link(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_UNLINK:
+                b = cmd_unlink(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_RMDIR:
+                b = cmd_rmdir(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_SET_XATTR:
+                b = cmd_setxattr(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_REMOVE_XATTR:
+                b = cmd_removexattr(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_WRITE:
+                b = cmd_write(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_CLONE:
+                b = cmd_clone(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_TRUNCATE:
+                b = cmd_truncate(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_CHMOD:
+                b = cmd_chmod(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_CHOWN:
+                b = cmd_chown(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_UTIMES:
+                b = cmd_utimes(hwnd, &cmd, data);
+            break;
+
+            case BTRFS_SEND_CMD_UPDATE_EXTENT:
+                // does nothing
+            break;
+
+            default:
+                ShowRecvError(IDS_RECV_UNKNOWN_COMMAND, cmd.cmd);
+                b = FALSE;
+            break;
+        }
+
+        if (data) free(data);
+
+        if (!b)
+            break;
+    }
+
+    if (lastwritefile != INVALID_HANDLE_VALUE) {
+        if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
+            if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) {
+                ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                return FALSE;
+            }
+        }
+
+        CloseHandle(lastwritefile);
+    }
+
+    if (b && !ended) {
+        ShowRecvError(IDS_RECV_FILE_TRUNCATED);
+        b = FALSE;
+    }
+
+    if (b) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+        btrfs_received_subvol brs;
+
+        brs.generation = stransid;
+        brs.uuid = subvol_uuid;
+
+        Status = NtFsControlFile(dir, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RECEIVED_SUBVOL, &brs, sizeof(btrfs_received_subvol),
+                                 NULL, 0);
+        if (!NT_SUCCESS(Status)) {
+            ShowRecvError(IDS_RECV_RECEIVED_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
+            b = FALSE;
+        }
+    }
+
+    CloseHandle(dir);
+
+    if (master != INVALID_HANDLE_VALUE)
+        CloseHandle(master);
+
+    if (!b && subvolpath != L"") {
+        ULONG attrib;
+
+        attrib = GetFileAttributesW(subvolpath.c_str());
+        attrib &= ~FILE_ATTRIBUTE_READONLY;
+
+        if (!SetFileAttributesW(subvolpath.c_str(), attrib))
+            ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        else
+            delete_directory(subvolpath);
+    }
+
+    return b;
+}
+
+DWORD BtrfsRecv::recv_thread() {
+    HANDLE f;
+    LARGE_INTEGER size;
+    UINT64 pos = 0;
+    BOOL b;
+
+    running = TRUE;
+
+    f = CreateFileW(streamfile.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+    if (f == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, streamfile.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        goto end;
+    }
+
+    if (!GetFileSizeEx(f, &size)) {
+        ShowRecvError(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        goto end;
+    }
+
+    parent = CreateFileW(dirpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
+    if (parent == INVALID_HANDLE_VALUE) {
+        ShowRecvError(IDS_RECV_CANT_OPEN_PATH, dirpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        CloseHandle(f);
+        goto end;
+    }
+
+    do {
+        b = do_recv(f, &pos, size.QuadPart);
+    } while (b && pos < (UINT64)size.QuadPart);
+
+    CloseHandle(parent);
+    CloseHandle(f);
+
+    if (b && hwnd) {
+        WCHAR s[255];
+
+        SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 65536, 0);
+
+        if (num_received == 1) {
+            if (!LoadStringW(module, IDS_RECV_SUCCESS, s, sizeof(s) / sizeof(WCHAR)))
+                ShowError(hwnd, GetLastError());
+            else
+                SetDlgItemTextW(hwnd, IDC_RECV_MSG, s);
+        } else {
+            if (!LoadStringW(module, IDS_RECV_SUCCESS_PLURAL, s, sizeof(s) / sizeof(WCHAR)))
+                ShowError(hwnd, GetLastError());
+            else {
+                WCHAR t[255];
+
+                if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, num_received) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    ShowError(hwnd, ERROR_INSUFFICIENT_BUFFER);
+                else
+                    SetDlgItemTextW(hwnd, IDC_RECV_MSG, t);
+            }
+        }
+
+        if (!LoadStringW(module, IDS_RECV_BUTTON_OK, s, sizeof(s) / sizeof(WCHAR)))
+            ShowError(hwnd, GetLastError());
+        else
+            SetDlgItemTextW(hwnd, IDCANCEL, s);
+    }
+
+end:
+    thread = NULL;
+    running = FALSE;
+
+    return 0;
+}
+
+static DWORD WINAPI global_recv_thread(LPVOID lpParameter) {
+    BtrfsRecv* br = (BtrfsRecv*)lpParameter;
+
+    return br->recv_thread();
+}
+
+INT_PTR CALLBACK BtrfsRecv::RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+            this->hwnd = hwndDlg;
+            thread = CreateThread(NULL, 0, global_recv_thread, this, 0, NULL);
+
+            if (!thread)
+                ShowRecvError(IDS_RECV_CREATETHREAD_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        break;
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                        case IDCANCEL:
+                            if (running) {
+                                WCHAR s[255];
+
+                                cancelling = TRUE;
+
+                                if (!LoadStringW(module, IDS_RECV_CANCELLED, s, sizeof(s) / sizeof(WCHAR))) {
+                                    ShowError(hwndDlg, GetLastError());
+                                    return FALSE;
+                                }
+
+                                SetDlgItemTextW(hwnd, IDC_RECV_MSG, s);
+                                SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 0, 0);
+
+                                if (!LoadStringW(module, IDS_RECV_BUTTON_OK, s, sizeof(s) / sizeof(WCHAR))) {
+                                    ShowError(hwndDlg, GetLastError());
+                                    return FALSE;
+                                }
+
+                                SetDlgItemTextW(hwnd, IDCANCEL, s);
+                            } else
+                                EndDialog(hwndDlg, 1);
+
+                            return TRUE;
+                    }
+                break;
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsRecv* br;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        br = (BtrfsRecv*)lParam;
+    } else {
+        br = (BtrfsRecv*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (br)
+        return br->RecvProgressDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsRecv::Open(HWND hwnd, WCHAR* file, WCHAR* path, BOOL quiet) {
+#ifndef __REACTOS__
+    UINT32 cpuInfo[4];
+#endif
+
+    streamfile = file;
+    dirpath = path;
+    subvolpath = L"";
+
+#ifndef __REACTOS__
+#ifndef _MSC_VER
+    __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
+    have_sse42 = cpuInfo[2] & bit_SSE4_2;
+#else
+    __cpuid((int*)cpuInfo, 1);
+    have_sse42 = cpuInfo[2] & (1 << 20);
+#endif
+#endif
+
+    if (quiet)
+        recv_thread();
+    else {
+        if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RECV_PROGRESS), hwnd, stub_RecvProgressDlgProc, (LPARAM)this) <= 0)
+            ShowError(hwnd, GetLastError());
+    }
+}
+
+void CALLBACK RecvSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    OPENFILENAMEW ofn;
+    WCHAR file[MAX_PATH];
+    BtrfsRecv* recv;
+    HANDLE token;
+    TOKEN_PRIVILEGES* tp;
+    LUID luid;
+    ULONG tplen;
+
+    set_dpi_aware();
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
+    tp = (TOKEN_PRIVILEGES*)malloc(tplen);
+    if (!tp) {
+        ShowStringError(hwnd, IDS_OUT_OF_MEMORY);
+        CloseHandle(token);
+        return;
+    }
+
+    tp->PrivilegeCount = 3;
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        free(tp);
+        CloseHandle(token);
+        return;
+    }
+
+    tp->Privileges[0].Luid = luid;
+    tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!LookupPrivilegeValueW(NULL, L"SeSecurityPrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        free(tp);
+        CloseHandle(token);
+        return;
+    }
+
+    tp->Privileges[1].Luid = luid;
+    tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!LookupPrivilegeValueW(NULL, L"SeRestorePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        free(tp);
+        CloseHandle(token);
+        return;
+    }
+
+    tp->Privileges[2].Luid = luid;
+    tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, tp, tplen, NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        free(tp);
+        CloseHandle(token);
+        return;
+    }
+
+    file[0] = 0;
+
+    memset(&ofn, 0, sizeof(OPENFILENAMEW));
+    ofn.lStructSize = sizeof(OPENFILENAMEW);
+    ofn.hwndOwner = hwnd;
+    ofn.hInstance = module;
+    ofn.lpstrFile = file;
+    ofn.nMaxFile = sizeof(file) / sizeof(WCHAR);
+    ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+
+    if (GetOpenFileNameW(&ofn)) {
+        recv = new BtrfsRecv;
+
+        recv->Open(hwnd, file, lpszCmdLine, FALSE);
+
+        delete recv;
+    }
+
+    free(tp);
+    CloseHandle(token);
+}
+
+void CALLBACK RecvSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    LPWSTR* args;
+    int num_args;
+
+    args = CommandLineToArgvW(lpszCmdLine, &num_args);
+
+    if (!args)
+        return;
+
+    if (num_args >= 2) {
+        BtrfsRecv* br;
+        HANDLE token;
+        TOKEN_PRIVILEGES* tp;
+        ULONG tplen;
+        LUID luid;
+
+        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
+            goto end;
+
+        tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
+        tp = (TOKEN_PRIVILEGES*)malloc(tplen);
+        if (!tp) {
+            CloseHandle(token);
+            goto end;
+        }
+
+        tp->PrivilegeCount = 3;
+
+        if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+            free(tp);
+            CloseHandle(token);
+            goto end;
+        }
+
+        tp->Privileges[0].Luid = luid;
+        tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+        if (!LookupPrivilegeValueW(NULL, L"SeSecurityPrivilege", &luid)) {
+            free(tp);
+            CloseHandle(token);
+            goto end;
+        }
+
+        tp->Privileges[1].Luid = luid;
+        tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+        if (!LookupPrivilegeValueW(NULL, L"SeRestorePrivilege", &luid)) {
+            free(tp);
+            CloseHandle(token);
+            goto end;
+        }
+
+        tp->Privileges[2].Luid = luid;
+        tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
+
+        if (!AdjustTokenPrivileges(token, FALSE, tp, tplen, NULL, NULL)) {
+            free(tp);
+            CloseHandle(token);
+            goto end;
+        }
+
+        free(tp);
+        CloseHandle(token);
+
+        br = new BtrfsRecv;
+        br->Open(NULL, args[0], args[1], TRUE);
+
+        delete br;
+    }
+
+end:
+    LocalFree(args);
+}

Modification de propriétés sur dll/shellext/shellbtrfs/recv.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/recv.h
===================================================================
--- dll/shellext/shellbtrfs/recv.h	(nonexistent)
+++ dll/shellext/shellbtrfs/recv.h	(copie de travail)
@@ -0,0 +1,88 @@
+/* Copyright (c) Mark Harmstone 2017
+ *
+ * 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 <shlobj.h>
+#include <string>
+#include <vector>
+#ifndef __REACTOS__
+#include "../btrfs.h"
+#else
+#include "btrfs.h"
+#endif
+
+extern LONG objs_loaded;
+
+typedef struct {
+    BTRFS_UUID uuid;
+    UINT64 transid;
+    std::wstring path;
+} subvol_cache;
+
+class BtrfsRecv {
+public:
+    BtrfsRecv() {
+        thread = NULL;
+        master = INVALID_HANDLE_VALUE;
+        dir = INVALID_HANDLE_VALUE;
+        running = FALSE;
+        cancelling = FALSE;
+        stransid = 0;
+        num_received = 0;
+        hwnd = NULL;
+        cache.clear();
+    }
+
+    virtual ~BtrfsRecv() {
+        cache.clear();
+    }
+
+    void Open(HWND hwnd, WCHAR* file, WCHAR* path, BOOL quiet);
+    DWORD recv_thread();
+    INT_PTR CALLBACK RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+private:
+    BOOL cmd_subvol(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_snapshot(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_mkfile(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_rename(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_link(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_unlink(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_rmdir(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_setxattr(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_removexattr(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_write(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_clone(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_truncate(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_chmod(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_chown(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    BOOL cmd_utimes(HWND hwnd, btrfs_send_command* cmd, UINT8* data);
+    void add_cache_entry(BTRFS_UUID* uuid, UINT64 transid, std::wstring path);
+    BOOL utf8_to_utf16(HWND hwnd, char* utf8, ULONG utf8len, std::wstring* utf16);
+    void ShowRecvError(int resid, ...);
+    BOOL find_tlv(UINT8* data, ULONG datalen, UINT16 type, void** value, ULONG* len);
+    BOOL do_recv(HANDLE f, UINT64* pos, UINT64 size);
+
+    HANDLE dir, parent, master, thread, lastwritefile;
+    HWND hwnd;
+    std::wstring streamfile, dirpath, subvolpath, lastwritepath;
+    DWORD lastwriteatt;
+    ULONG num_received;
+    UINT64 stransid;
+    BTRFS_UUID subvol_uuid;
+    BOOL running, cancelling;
+    std::vector<subvol_cache> cache;
+};

Modification de propriétés sur dll/shellext/shellbtrfs/recv.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/resource.h
===================================================================
--- dll/shellext/shellbtrfs/resource.h	(révision 75772)
+++ dll/shellext/shellbtrfs/resource.h	(copie de travail)
@@ -8,12 +8,17 @@
 #define IDD_SIZE_DETAILS                103
 #define IDS_NEW_SUBVOL_FILENAME         104
 #define IDS_CREATE_SNAPSHOT             105
+#define IDD_VOL_PROP_SHEET              105
 #define IDS_CREATE_SNAPSHOT_HELP_TEXT   106
+#define IDD_VOL_USAGE                   106
 #define IDS_SNAPSHOT_FILENAME           107
 #define IDD_PROP_SHEET                  107
 #define IDS_PROP_SHEET_TITLE            108
+#define IDD_BALANCE_OPTIONS             108
 #define IDS_INODE_FILE                  109
+#define IDD_BALANCE                     109
 #define IDS_INODE_DIR                   110
+#define IDD_DEVICES                     110
 #define IDS_INODE_CHAR                  111
 #define IDS_INODE_BLOCK                 112
 #define IDS_INODE_FIFO                  113
@@ -21,6 +26,7 @@
 #define IDS_INODE_SYMLINK               115
 #define IDS_INODE_UNKNOWN               116
 #define IDS_SET_INODE_INFO_ERROR        117
+#define IDS_CANNOT_FIND_DEVICE          117
 #define IDS_SIZE_BYTE                   118
 #define IDS_SIZE_BYTES                  119
 #define IDS_SIZE_KB                     120
@@ -32,7 +38,164 @@
 #define IDS_VARIOUS                     126
 #define IDS_INODE_CHAR_SIMPLE           127
 #define IDS_INODE_BLOCK_SIMPLE          128
+#define IDS_VOL_PROP_SHEET_TITLE        129
 #define IDS_SIZE_LARGE                  130
+#define IDS_SINGLE                      131
+#define IDS_DUP                         132
+#define IDS_RAID0                       133
+#define IDS_RAID1                       134
+#define IDS_RAID10                      135
+#define IDS_RAID5                       136
+#define IDS_RAID6                       137
+#define IDS_USAGE_DATA                  138
+#define IDS_USAGE_MIXED                 139
+#define IDS_USAGE_METADATA              140
+#define IDS_USAGE_SYSTEM                141
+#define IDS_USAGE_UNALLOC               142
+#define IDS_UNKNOWN_DEVICE              143
+#define IDS_USAGE_DEV_SIZE              144
+#define IDS_USAGE_DEV_ALLOC             145
+#define IDS_USAGE_DEV_UNALLOC           146
+#define IDS_USAGE_DATA_RATIO            147
+#define IDS_USAGE_METADATA_RATIO        148
+#define IDS_NO_BALANCE                  149
+#define IDS_SINGLE2                     150
+#define IDS_DEVID_LIST                  151
+#define IDS_BALANCE_RUNNING             152
+#define IDS_DRANGE_END_BEFORE_START     153
+#define IDS_VRANGE_END_BEFORE_START     154
+#define IDS_LIMIT_END_BEFORE_START      155
+#define IDS_STRIPES_END_BEFORE_START    156
+#define IDS_USAGE_END_BEFORE_START      157
+#define IDS_ERROR                       158
+#define IDS_BALANCE_COMPLETE            159
+#define IDS_BALANCE_PAUSED              160
+#define IDS_BALANCE_CANCELLED           161
+#define IDS_DEVLIST_ID                  162
+#define IDS_DEVLIST_DESC                163
+#define IDS_DEVLIST_READONLY            164
+#define IDS_DEVLIST_SIZE                165
+#define IDS_DEVLIST_READONLY_YES        166
+#define IDS_DEVLIST_READONLY_NO         167
+#define IDD_DEVICE_ADD                  167
+#define IDS_DEVLIST_ALLOC               168
+#define IDD_SCRUB                       168
+#define IDS_DEVLIST_ALLOC_PC            169
+#define IDD_DEVICE_STATS                169
+#define IDS_BALANCE_RUNNING_REMOVAL     170
+#define IDD_RECV_PROGRESS               170
+#define IDS_BALANCE_PAUSED_REMOVAL      171
+#define IDD_SEND_SUBVOL                 171
+#define IDS_BALANCE_CANCELLED_REMOVAL   172
+#define IDD_RESIZE                      172
+#define IDS_BALANCE_COMPLETE_REMOVAL    173
+#define IDS_PARTITION                   174
+#define IDS_WHOLE_DISK                  175
+#define IDS_CANNOT_REMOVE_RAID          176
+#define IDS_REMOVE_DEVICE_CONFIRMATION  177
+#define IDS_CONFIRMATION_TITLE          178
+#define IDS_ADD_DEVICE_CONFIRMATION     179
+#define IDS_ADD_DEVICE_CONFIRMATION_FS  180
+#define IDS_BALANCE_FAILED              181
+#define IDS_BALANCE_FAILED_REMOVAL      182
+#define IDS_DISK_NUM                    183
+#define IDS_DISK_PART_NUM               184
+#define IDS_NO_SCRUB                    185
+#define IDS_SCRUB_RUNNING               186
+#define IDS_SCRUB_FINISHED              187
+#define IDS_SCRUB_PAUSED                188
+#define IDS_SCRUB_MSG_STARTED           189
+#define IDS_SCRUB_MSG_RECOVERABLE_DATA  190
+#define IDS_SCRUB_MSG_RECOVERABLE_METADATA 191
+#define IDS_SCRUB_MSG_UNRECOVERABLE_DATA 192
+#define IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL 193
+#define IDS_SCRUB_MSG_UNRECOVERABLE_METADATA 194
+#define IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM 195
+#define IDS_SCRUB_MSG_FINISHED          196
+#define IDS_SCRUB_MSG_SUMMARY           197
+#define IDS_BALANCE_SCRUB_RUNNING       198
+#define IDS_SCRUB_BALANCE_RUNNING       199
+#define IDS_SCRUB_MSG_SUMMARY_ERRORS_RECOVERABLE 200
+#define IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE 201
+#define IDS_SCRUB_FAILED                202
+#define IDS_LOCK_FAILED                 203
+#define IDS_SCRUB_MSG_RECOVERABLE_PARITY 204
+#define IDS_COMPRESS_ANY                205
+#define IDS_COMPRESS_ZLIB               206
+#define IDS_COMPRESS_LZO                207
+#define IDS_STANDALONE_PROPSHEET_TITLE  208
+#define IDS_REFLINK_PASTE               209
+#define IDS_REFLINK_PASTE_HELP          210
+#define IDS_RECV_SUBVOL                 211
+#define IDS_RECV_SUBVOL_HELP            212
+#define IDS_RECV_CANT_OPEN_FILE         213
+#define IDS_RECV_READFILE_FAILED        214
+#define IDS_OUT_OF_MEMORY               215
+#define IDS_RECV_UNKNOWN_COMMAND        216
+#define IDS_RECV_CANT_OPEN_PATH         217
+#define IDS_RECV_RTLUTF8TOUNICODEN_FAILED 218
+#define IDS_RECV_CREATE_SUBVOL_FAILED   219
+#define IDS_RECV_MISSING_PARAM          220
+#define IDS_RECV_SHORT_PARAM            221
+#define IDS_RECV_MKNOD_FAILED           222
+#define IDS_RECV_SET_REPARSE_POINT_FAILED 223
+#define IDS_RECV_MOVEFILE_FAILED        224
+#define IDS_RECV_SETFILEPOINTER_FAILED  225
+#define IDS_RECV_WRITEFILE_FAILED       226
+#define IDS_RECV_CREATEHARDLINK_FAILED  227
+#define IDS_RECV_SETENDOFFILE_FAILED    228
+#define IDS_RECV_CANT_CREATE_FILE       229
+#define IDS_RECV_SETFILEINFO_FAILED     230
+#define IDS_RECV_SETINODEINFO_FAILED    231
+#define IDS_RECV_SUCCESS                232
+#define IDS_RECV_BUTTON_OK              233
+#define IDS_RECV_SETFILEATTRIBUTES_FAILED 234
+#define IDS_RECV_GETFILEATTRIBUTES_FAILED 235
+#define IDS_RECV_CSUM_ERROR             236
+#define IDS_RECV_NOT_A_SEND_STREAM      237
+#define IDS_RECV_UNSUPPORTED_VERSION    238
+#define IDS_RECV_SETEAFILE_FAILED       239
+#define IDS_RECV_RECEIVED_SUBVOL_FAILED 240
+#define IDS_RECV_SETSECURITYOBJECT_FAILED 241
+#define IDS_RECV_SETXATTR_FAILED        242
+#define IDS_RECV_CREATETHREAD_FAILED    243
+#define IDS_RECV_FILE_TRUNCATED         244
+#define IDS_RECV_RESERVE_SUBVOL_FAILED  245
+#define IDS_RECV_CANCELLED              246
+#define IDS_RECV_CANT_FIND_PARENT_SUBVOL 247
+#define IDS_RECV_FIND_SUBVOL_FAILED     248
+#define IDS_RECV_CREATE_SNAPSHOT_FAILED 249
+#define IDS_RECV_GETVOLUMEPATHNAME_FAILED 250
+#define IDS_RECV_DELETEFILE_FAILED      251
+#define IDS_RECV_REMOVEDIRECTORY_FAILED 252
+#define IDS_RECV_CANT_FIND_CLONE_SUBVOL 253
+#define IDS_RECV_GETFILESIZEEX_FAILED   254
+#define IDS_RECV_DUPLICATE_EXTENTS_FAILED 255
+#define IDS_RECV_SUCCESS_PLURAL         256
+#define IDS_SEND_SUBVOL                 257
+#define IDS_SEND_SUBVOL_HELP            258
+#define IDS_SEND_CANT_OPEN_FILE         259
+#define IDS_SEND_CANT_OPEN_DIR          260
+#define IDS_SEND_FSCTL_BTRFS_SEND_SUBVOL_FAILED 261
+#define IDS_SEND_FSCTL_BTRFS_READ_SEND_BUFFER_FAILED 262
+#define IDS_SEND_SUCCESS                263
+#define IDS_SEND_WRITEFILE_FAILED       264
+#define IDS_SEND_GET_FILE_INFO_FAILED   265
+#define IDS_SEND_NOT_READONLY           266
+#define IDS_NOT_SUBVOL                  267
+#define IDS_GET_FILE_IDS_FAILED         268
+#define IDS_SHPARSEDISPLAYNAME_FAILED   269
+#define IDS_SHGETPATHFROMIDLIST_FAILED  270
+#define IDS_SEND_PARENT_NOT_READONLY    271
+#define IDS_SEND_CANCEL                 272
+#define IDS_SEND_WRITING                273
+#define IDS_MISSING                     274
+#define IDS_RESIZE_SUCCESSFUL           275
+#define IDS_BALANCE_RUNNING_SHRINK      276
+#define IDS_BALANCE_PAUSED_SHRINK       277
+#define IDS_BALANCE_CANCELLED_SHRINK    278
+#define IDS_BALANCE_COMPLETE_SHRINK     279
+#define IDS_BALANCE_FAILED_SHRINK       280
 #define IDC_UID                         1001
 #define IDC_GID                         1002
 #define IDC_USERR                       1003
@@ -52,18 +215,118 @@
 #define IDC_SIZE_ON_DISK                1017
 #define IDC_GROUP_INFORMATION           1018
 #define IDC_SIZE_INLINE                 1019
+#define IDC_SETUID                      1019
 #define IDC_SIZE_UNCOMPRESSED           1020
+#define IDC_USAGE_BOX                   1020
+#define IDC_SETGID                      1020
 #define IDC_SIZE_ZLIB                   1021
+#define IDC_USAGE_REFRESH               1021
+#define IDC_STICKY                      1021
 #define IDC_SIZE_ZLIB2                  1022
 #define IDC_SIZE_LZO                    1022
+#define IDC_VOL_SHOW_USAGE              1022
+#define IDC_VOL_BALANCE                 1023
+#define IDC_PROFILES                    1024
+#define IDC_PROFILES_SINGLE             1025
+#define IDC_PROFILES_DUP                1026
+#define IDC_VOL_DEVICES                 1026
+#define IDC_PROFILES_RAID0              1027
+#define IDC_VOL_DEVICES2                1027
+#define IDC_VOL_SCRUB                   1027
+#define IDC_PROFILES_RAID1              1028
+#define IDC_PROFILES_RAID10             1029
+#define IDC_PROFILES_RAID5              1030
+#define IDC_PROFILES_RAID6              1031
+#define IDC_USAGE                       1032
+#define IDC_USAGE_START_SPINNER         1033
+#define IDC_USAGE_START                 1034
+#define IDC_USAGE_END_SPINNER           1035
+#define IDC_USAGE_END                   1036
+#define IDC_DEVID                       1037
+#define IDC_DEVID_COMBO                 1038
+#define IDC_DRANGE_START                1039
+#define IDC_DRANGE                      1040
+#define IDC_PROGRESS1                   1040
+#define IDC_BALANCE_PROGRESS            1040
+#define IDC_RECV_PROGRESS               1040
+#define IDC_DRANGE_END                  1041
+#define IDC_DATA                        1041
+#define IDC_LIMIT                       1042
+#define IDC_METADATA                    1042
+#define IDC_VRANGE                      1043
+#define IDC_SYSTEM                      1043
+#define IDC_LIMIT_START_SPINNER         1044
+#define IDC_BUTTON1                     1044
+#define IDC_DATA_OPTIONS                1044
+#define IDC_DEVICE_ADD                  1044
+#define IDC_RESET_STATS                 1044
+#define IDC_OPEN_ADMIN                  1044
+#define IDC_BROWSE                      1044
+#define IDC_LIMIT_START                 1045
+#define IDC_BUTTON2                     1045
+#define IDC_METADATA_OPTIONS            1045
+#define IDC_DEVICE_REFRESH              1045
+#define IDC_PARENT_BROWSE               1045
+#define IDC_VRANGE_END                  1046
+#define IDC_BUTTON3                     1046
+#define IDC_SYSTEM_OPTIONS              1046
+#define IDC_DEVICE_REMOVE               1046
+#define IDC_CLONE_ADD                   1046
+#define IDC_VRANGE_START                1047
+#define IDC_BALANCE_STATUS              1047
+#define IDC_DEVICE_SHOW_STATS           1047
+#define IDC_CLONE_REMOVE                1047
+#define IDC_LIMIT_END_SPINNER           1048
+#define IDC_START_BALANCE               1048
+#define IDC_DEVICE_RESIZE               1048
+#define IDC_LIMIT_END                   1049
+#define IDC_PAUSE_BALANCE               1049
+#define IDC_STRIPES                     1050
+#define IDC_BUTTON6                     1050
+#define IDC_CANCEL_BALANCE              1050
+#define IDC_STRIPES_START_SPINNER       1051
+#define IDC_STRIPES_START               1052
+#define IDC_DEVLIST                     1052
+#define IDC_STRIPES_END_SPINNER         1053
+#define IDC_DEVICE_TREE                 1053
+#define IDC_STRIPES_END                 1054
+#define IDC_UUID                        1054
+#define IDC_CONVERT                     1055
+#define IDC_SCRUB_INFO                  1055
+#define IDC_CONVERT_COMBO               1056
+#define IDC_START_SCRUB                 1056
+#define IDC_SOFT                        1057
+#define IDC_PAUSE_SCRUB                 1057
+#define IDC_CANCEL_SCRUB                1058
+#define IDC_SCRUB_PROGRESS              1059
+#define IDC_SCRUB_STATUS                1060
+#define IDC_COMPRESS_TYPE               1061
+#define IDC_CHECK1                      1062
+#define IDC_SUBVOL_RO                   1062
+#define IDC_INCREMENTAL                 1062
+#define IDC_DEVICE_ID                   1063
+#define IDC_WRITE_ERRS                  1064
+#define IDC_READ_ERRS                   1065
+#define IDC_RECV_MSG                    1065
+#define IDC_FLUSH_ERRS                  1066
+#define IDC_STREAM_DEST                 1066
+#define IDC_CORRUPTION_ERRS             1067
+#define IDC_SEND_STATUS                 1067
+#define IDC_GENERATION_ERRS             1068
+#define IDC_PARENT_SUBVOL               1068
+#define IDC_CLONE_LIST                  1069
+#define IDC_RESIZE_DEVICE_ID            1070
+#define IDC_RESIZE_CURSIZE              1071
+#define IDC_RESIZE_SLIDER               1072
+#define IDC_RESIZE_NEWSIZE              1073
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        104
+#define _APS_NEXT_RESOURCE_VALUE        173
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1020
+#define _APS_NEXT_CONTROL_VALUE         1073
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
Index: dll/shellext/shellbtrfs/scrub.cpp
===================================================================
--- dll/shellext/shellbtrfs/scrub.cpp	(nonexistent)
+++ dll/shellext/shellbtrfs/scrub.cpp	(copie de travail)
@@ -0,0 +1,692 @@
+/* Copyright (c) Mark Harmstone 2017
+ *
+ * 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 "shellext.h"
+#include "scrub.h"
+#include "resource.h"
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "btrfsioctl.h"
+#endif
+#include <shlobj.h>
+#include <uxtheme.h>
+#include <stdio.h>
+#ifndef __REACTOS__
+#include <strsafe.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <strsafe.h>
+#include <ndk/iofuncs.h>
+#include <ndk/iotypes.h>
+#endif
+#include <string>
+
+#define NO_SHLWAPI_STRFCNS
+#include <shlwapi.h>
+#include <uxtheme.h>
+
+void BtrfsScrub::UpdateTextBox(HWND hwndDlg, btrfs_query_scrub* bqs) {
+    btrfs_query_scrub* bqs2 = NULL;
+    BOOL alloc_bqs2 = FALSE;
+    NTSTATUS Status;
+    std::wstring s;
+    WCHAR t[255], u[255], dt[255], tm[255];
+    FILETIME filetime;
+    SYSTEMTIME systime;
+    UINT64 recoverable_errors = 0, unrecoverable_errors = 0;
+
+    if (bqs->num_errors > 0) {
+        HANDLE h;
+        IO_STATUS_BLOCK iosb;
+        ULONG len;
+
+        h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ShowError(hwndDlg, GetLastError());
+            return;
+        }
+
+        len = 0;
+
+        do {
+            len += 1024;
+
+            if (bqs2)
+                free(bqs2);
+
+            bqs2 = (btrfs_query_scrub*)malloc(len);
+
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_SCRUB, NULL, 0, bqs2, len);
+
+            if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+                ShowNtStatusError(hwndDlg, Status);
+                CloseHandle(h);
+                free(bqs2);
+                return;
+            }
+        } while (Status == STATUS_BUFFER_OVERFLOW);
+
+        alloc_bqs2 = TRUE;
+
+        CloseHandle(h);
+    } else
+        bqs2 = bqs;
+
+    s[0] = 0;
+
+    // "scrub started"
+    if (bqs2->start_time.QuadPart > 0) {
+        filetime.dwLowDateTime = bqs2->start_time.LowPart;
+        filetime.dwHighDateTime = bqs2->start_time.HighPart;
+
+        if (!FileTimeToSystemTime(&filetime, &systime)) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!SystemTimeToTzSpecificLocalTime(NULL, &systime, &systime)) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!LoadStringW(module, IDS_SCRUB_MSG_STARTED, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, dt, sizeof(dt) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, NULL, tm, sizeof(tm) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, dt, tm) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        s += u;
+        s += L"\r\n";
+    }
+
+    // errors
+    if (bqs2->num_errors > 0) {
+        btrfs_scrub_error* bse = &bqs2->errors;
+
+        do {
+            if (bse->recovered)
+                recoverable_errors++;
+            else
+                unrecoverable_errors++;
+
+            if (bse->parity) {
+                if (!LoadStringW(module, IDS_SCRUB_MSG_RECOVERABLE_PARITY, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    goto end;
+                }
+
+                if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    goto end;
+            } else if (bse->is_metadata) {
+                int message;
+
+                if (bse->recovered)
+                    message = IDS_SCRUB_MSG_RECOVERABLE_METADATA;
+                else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0)
+                    message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA;
+                else
+                    message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM;
+
+                if (!LoadStringW(module, message, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    goto end;
+                }
+
+                if (bse->recovered) {
+                    if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+                } else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0) {
+                    if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device,
+                        bse->metadata.root, bse->metadata.level) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+                } else {
+                    if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device,
+                        bse->metadata.root, bse->metadata.level, bse->metadata.firstitem.obj_id, bse->metadata.firstitem.obj_type,
+                        bse->metadata.firstitem.offset) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+                }
+            } else {
+                int message;
+
+                if (bse->recovered)
+                    message = IDS_SCRUB_MSG_RECOVERABLE_DATA;
+                else if (bse->data.subvol != 0)
+                    message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL;
+                else
+                    message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA;
+
+                if (!LoadStringW(module, message, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    goto end;
+                }
+
+                if (bse->recovered) {
+                    if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+                } else if (bse->data.subvol != 0) {
+                    if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device, bse->data.subvol,
+                        bse->data.filename_length / sizeof(WCHAR), bse->data.filename, bse->data.offset) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+                } else {
+                    if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device, bse->data.filename_length / sizeof(WCHAR),
+                        bse->data.filename, bse->data.offset) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+                }
+            }
+
+            s += u;
+            s += L"\r\n";
+
+            if (bse->next_entry == 0)
+                break;
+            else
+                bse = (btrfs_scrub_error*)((UINT8*)bse + bse->next_entry);
+        } while (TRUE);
+    }
+
+    if (bqs2->finish_time.QuadPart > 0) {
+        WCHAR d1[255], d2[255];
+        float speed;
+
+        // "scrub finished"
+
+        filetime.dwLowDateTime = bqs2->finish_time.LowPart;
+        filetime.dwHighDateTime = bqs2->finish_time.HighPart;
+
+        if (!FileTimeToSystemTime(&filetime, &systime)) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!SystemTimeToTzSpecificLocalTime(NULL, &systime, &systime)) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!LoadStringW(module, IDS_SCRUB_MSG_FINISHED, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, dt, sizeof(dt) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, NULL, tm, sizeof(tm) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, dt, tm) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        s += u;
+        s += L"\r\n";
+
+        // summary
+
+        if (!LoadStringW(module, IDS_SCRUB_MSG_SUMMARY, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        format_size(bqs2->data_scrubbed, d1, sizeof(d1) / sizeof(WCHAR), FALSE);
+
+        speed = (float)bqs2->data_scrubbed / ((float)bqs2->duration / 10000000.0f);
+
+        format_size((UINT64)speed, d2, sizeof(d2) / sizeof(WCHAR), FALSE);
+
+        if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, d1, bqs2->duration / 10000000, d2) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        s += u;
+        s += L"\r\n";
+
+        // recoverable errors
+
+        if (!LoadStringW(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_RECOVERABLE, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, recoverable_errors) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        s += u;
+        s += L"\r\n";
+
+        // unrecoverable errors
+
+        if (!LoadStringW(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, unrecoverable_errors) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        s += u;
+        s += L"\r\n";
+    }
+
+    SetWindowTextW(GetDlgItem(hwndDlg, IDC_SCRUB_INFO), s.c_str());
+
+end:
+    if (alloc_bqs2)
+        free(bqs2);
+}
+
+void BtrfsScrub::RefreshScrubDlg(HWND hwndDlg, BOOL first_time) {
+    HANDLE h;
+    btrfs_query_scrub bqs;
+
+    h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_SCRUB, NULL, 0, &bqs, sizeof(btrfs_query_scrub));
+
+        if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+            ShowNtStatusError(hwndDlg, Status);
+            CloseHandle(h);
+            return;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    if (first_time || status != bqs.status || chunks_left != bqs.chunks_left) {
+        WCHAR s[255];
+
+        if (bqs.status == BTRFS_SCRUB_STOPPED) {
+            EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), TRUE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), FALSE);
+
+            if (bqs.error != STATUS_SUCCESS) {
+                WCHAR t[255];
+
+                if (!LoadStringW(module, IDS_SCRUB_FAILED, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return;
+                }
+
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, bqs.error) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return;
+            } else {
+                if (!LoadStringW(module, bqs.total_chunks == 0 ? IDS_NO_SCRUB : IDS_SCRUB_FINISHED, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return;
+                }
+            }
+        } else {
+            WCHAR t[255];
+            float pc;
+
+            EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), FALSE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), TRUE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), TRUE);
+
+            if (!LoadStringW(module, bqs.status == BTRFS_SCRUB_PAUSED ? IDS_SCRUB_PAUSED : IDS_SCRUB_RUNNING, t, sizeof(t) / sizeof(WCHAR))) {
+                ShowError(hwndDlg, GetLastError());
+                return;
+            }
+
+            pc = ((float)(bqs.total_chunks - bqs.chunks_left) / (float)bqs.total_chunks) * 100.0f;
+
+            if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, bqs.total_chunks - bqs.chunks_left, bqs.total_chunks, pc) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                return;
+        }
+
+        SetDlgItemTextW(hwndDlg, IDC_SCRUB_STATUS, s);
+
+        if (first_time || status != bqs.status) {
+            EnableWindow(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), bqs.status != BTRFS_SCRUB_STOPPED);
+
+            if (bqs.status != BTRFS_SCRUB_STOPPED) {
+                SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)bqs.total_chunks);
+                SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
+
+                if (bqs.status == BTRFS_SCRUB_PAUSED)
+                    SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_PAUSED, 0);
+                else
+                    SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
+            } else {
+                SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, 0);
+                SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, 0, 0);
+            }
+
+            chunks_left = bqs.chunks_left;
+        }
+    }
+
+    if (bqs.status != BTRFS_SCRUB_STOPPED && chunks_left != bqs.chunks_left) {
+        SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
+        chunks_left = bqs.chunks_left;
+    }
+
+    if (first_time || status != bqs.status || num_errors != bqs.num_errors) {
+        UpdateTextBox(hwndDlg, &bqs);
+
+        num_errors = bqs.num_errors;
+    }
+
+    status = bqs.status;
+}
+
+void BtrfsScrub::StartScrub(HWND hwndDlg) {
+    HANDLE h;
+
+    h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_START_SCRUB, NULL, 0, NULL, 0);
+
+        if (Status == STATUS_DEVICE_NOT_READY) {
+            btrfs_query_balance bqb;
+            NTSTATUS Status2;
+
+            Status2 = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_BALANCE, NULL, 0, &bqb, sizeof(btrfs_query_balance));
+
+            if (NT_SUCCESS(Status2) && bqb.status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED)) {
+                ShowStringError(hwndDlg, IDS_SCRUB_BALANCE_RUNNING);
+                CloseHandle(h);
+                return;
+            }
+        }
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwndDlg, Status);
+            CloseHandle(h);
+            return;
+        }
+
+        RefreshScrubDlg(hwndDlg, TRUE);
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+}
+
+void BtrfsScrub::PauseScrub(HWND hwndDlg) {
+    HANDLE h;
+    btrfs_query_scrub bqs;
+
+    h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_SCRUB, NULL, 0, &bqs, sizeof(btrfs_query_scrub));
+
+        if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
+            ShowNtStatusError(hwndDlg, Status);
+            CloseHandle(h);
+            return;
+        }
+
+        if (bqs.status == BTRFS_SCRUB_PAUSED)
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESUME_SCRUB, NULL, 0, NULL, 0);
+        else
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_PAUSE_SCRUB, NULL, 0, NULL, 0);
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwndDlg, Status);
+            CloseHandle(h);
+            return;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+}
+
+void BtrfsScrub::StopScrub(HWND hwndDlg) {
+    HANDLE h;
+
+    h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_STOP_SCRUB, NULL, 0, NULL, 0);
+
+        if (!NT_SUCCESS(Status)) {
+            ShowNtStatusError(hwndDlg, Status);
+            CloseHandle(h);
+            return;
+        }
+
+        CloseHandle(h);
+    } else {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+}
+
+INT_PTR CALLBACK BtrfsScrub::ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+            RefreshScrubDlg(hwndDlg, TRUE);
+            SetTimer(hwndDlg, 1, 1000, NULL);
+        break;
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                        case IDCANCEL:
+                            EndDialog(hwndDlg, 0);
+                        return TRUE;
+
+                        case IDC_START_SCRUB:
+                            StartScrub(hwndDlg);
+                        return TRUE;
+
+                        case IDC_PAUSE_SCRUB:
+                            PauseScrub(hwndDlg);
+                        return TRUE;
+
+                        case IDC_CANCEL_SCRUB:
+                            StopScrub(hwndDlg);
+                        return TRUE;
+                    }
+                break;
+            }
+        break;
+
+        case WM_TIMER:
+            RefreshScrubDlg(hwndDlg, FALSE);
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsScrub* bs;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bs = (BtrfsScrub*)lParam;
+    } else {
+        bs = (BtrfsScrub*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (bs)
+        return bs->ScrubDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void CALLBACK ShowScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    HANDLE token;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+    BtrfsScrub* scrub;
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    set_dpi_aware();
+
+    scrub = new BtrfsScrub(lpszCmdLine);
+
+    DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SCRUB), hwnd, stub_ScrubDlgProc, (LPARAM)scrub);
+
+    delete scrub;
+
+end:
+    CloseHandle(token);
+}
+
+void CALLBACK StartScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    LPWSTR* args;
+    int num_args;
+
+    args = CommandLineToArgvW(lpszCmdLine, &num_args);
+
+    if (!args)
+        return;
+
+    if (num_args >= 1) {
+        HANDLE h, token;
+        LUID luid;
+        TOKEN_PRIVILEGES tp;
+
+        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
+            goto end;
+
+        if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid))
+            goto end;
+
+        tp.PrivilegeCount = 1;
+        tp.Privileges[0].Luid = luid;
+        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+        if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
+            goto end;
+
+        CloseHandle(token);
+
+        h = CreateFileW(args[0], FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+        if (h != INVALID_HANDLE_VALUE) {
+            IO_STATUS_BLOCK iosb;
+
+            NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_START_SCRUB, NULL, 0, NULL, 0);
+
+            CloseHandle(h);
+        }
+    }
+
+end:
+    LocalFree(args);
+}
+
+void CALLBACK StopScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    LPWSTR* args;
+    int num_args;
+
+    args = CommandLineToArgvW(lpszCmdLine, &num_args);
+
+    if (!args)
+        return;
+
+    if (num_args >= 1) {
+        HANDLE h, token;
+        LUID luid;
+        TOKEN_PRIVILEGES tp;
+
+        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
+            goto end;
+
+        if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid))
+            goto end;
+
+        tp.PrivilegeCount = 1;
+        tp.Privileges[0].Luid = luid;
+        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+        if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
+            goto end;
+
+        CloseHandle(token);
+
+        h = CreateFileW(args[0], FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+        if (h != INVALID_HANDLE_VALUE) {
+            IO_STATUS_BLOCK iosb;
+
+            NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_STOP_SCRUB, NULL, 0, NULL, 0);
+
+            CloseHandle(h);
+        }
+    }
+
+end:
+    LocalFree(args);
+}

Modification de propriétés sur dll/shellext/shellbtrfs/scrub.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/scrub.h
===================================================================
--- dll/shellext/shellbtrfs/scrub.h	(nonexistent)
+++ dll/shellext/shellbtrfs/scrub.h	(copie de travail)
@@ -0,0 +1,46 @@
+/* Copyright (c) Mark Harmstone 2017
+ *
+ * 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 <windows.h>
+#ifndef __REACTOS__
+#include "../btrfs.h"
+#include "../btrfsioctl.h"
+#else
+#include "btrfs.h"
+#include "btrfsioctl.h"
+#endif
+
+class BtrfsScrub {
+public:
+    BtrfsScrub(WCHAR* drive) {
+        wcscpy(fn, drive);
+    }
+
+    INT_PTR CALLBACK ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+private:
+    void RefreshScrubDlg(HWND hwndDlg, BOOL first_time);
+    void UpdateTextBox(HWND hwndDlg, btrfs_query_scrub* bqs);
+    void StartScrub(HWND hwndDlg);
+    void PauseScrub(HWND hwndDlg);
+    void StopScrub(HWND hwndDlg);
+
+    WCHAR fn[MAX_PATH];
+    UINT32 status;
+    UINT64 chunks_left;
+    UINT32 num_errors;
+};

Modification de propriétés sur dll/shellext/shellbtrfs/scrub.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/send.cpp
===================================================================
--- dll/shellext/shellbtrfs/send.cpp	(nonexistent)
+++ dll/shellext/shellbtrfs/send.cpp	(copie de travail)
@@ -0,0 +1,755 @@
+/* Copyright (c) Mark Harmstone 2017
+ *
+ * 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 "shellext.h"
+#include "send.h"
+#include "resource.h"
+#include <stddef.h>
+#include <shlobj.h>
+#ifdef __REACTOS__
+#undef DeleteFile
+#endif
+
+#define SEND_BUFFER_LEN 1048576
+
+void BtrfsSend::ShowSendError(UINT msg, ...) {
+    WCHAR s[1024], t[1024];
+    va_list ap;
+
+    if (!LoadStringW(module, msg, s, sizeof(s) / sizeof(WCHAR))) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    va_start(ap, msg);
+#ifndef __REACTOS__
+    vswprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
+#else
+    vsnwprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
+#endif
+
+    SetDlgItemTextW(hwnd, IDC_SEND_STATUS, t);
+
+    va_end(ap);
+}
+
+DWORD BtrfsSend::Thread() {
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_send_subvol* bss;
+    btrfs_send_header header;
+    btrfs_send_command end;
+    BOOL success = FALSE;
+    ULONG bss_size, i;
+
+    buf = (char*)malloc(SEND_BUFFER_LEN);
+
+    dirh = CreateFileW(subvol.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    if (dirh == INVALID_HANDLE_VALUE) {
+        ShowSendError(IDS_SEND_CANT_OPEN_DIR, subvol.c_str(), GetLastError(), format_message(GetLastError()).c_str());
+        goto end3;
+    }
+
+    bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE));
+    bss = (btrfs_send_subvol*)malloc(bss_size);
+    memset(bss, 0, bss_size);
+
+    if (incremental) {
+        WCHAR parent[MAX_PATH];
+        HANDLE parenth;
+
+        parent[0] = 0;
+
+        GetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent, sizeof(parent) / sizeof(WCHAR));
+
+        parenth = CreateFileW(parent, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (parenth == INVALID_HANDLE_VALUE) {
+            ShowSendError(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str());
+            goto end2;
+        }
+
+        bss->parent = parenth;
+    } else
+        bss->parent = NULL;
+
+    bss->num_clones = clones.size();
+
+    for (i = 0; i < bss->num_clones; i++) {
+        HANDLE h;
+
+        h = CreateFileW(clones[i].c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ULONG j;
+
+            ShowSendError(IDS_SEND_CANT_OPEN_DIR, clones[i].c_str(), GetLastError(), format_message(GetLastError()).c_str());
+
+            for (j = 0; j < i; j++) {
+                CloseHandle(bss->clones[j]);
+            }
+
+            if (bss->parent) CloseHandle(bss->parent);
+            goto end2;
+        }
+
+        bss->clones[i] = h;
+    }
+
+    Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, bss_size, NULL, 0);
+
+    for (i = 0; i < bss->num_clones; i++) {
+        CloseHandle(bss->clones[i]);
+    }
+
+    if (!NT_SUCCESS(Status)) {
+        if (Status == (NTSTATUS)STATUS_INVALID_PARAMETER) {
+            BY_HANDLE_FILE_INFORMATION fileinfo;
+            if (!GetFileInformationByHandle(dirh, &fileinfo)) {
+                ShowSendError(IDS_SEND_GET_FILE_INFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                if (bss->parent) CloseHandle(bss->parent);
+                goto end2;
+            }
+
+            if (!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
+                ShowSendError(IDS_SEND_NOT_READONLY);
+                if (bss->parent) CloseHandle(bss->parent);
+                goto end2;
+            }
+
+            if (bss->parent) {
+                if (!GetFileInformationByHandle(bss->parent, &fileinfo)) {
+                    ShowSendError(IDS_SEND_GET_FILE_INFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+                    CloseHandle(bss->parent);
+                    goto end2;
+                }
+
+                if (!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
+                    ShowSendError(IDS_SEND_PARENT_NOT_READONLY);
+                    CloseHandle(bss->parent);
+                    goto end2;
+                }
+            }
+        }
+
+        ShowSendError(IDS_SEND_FSCTL_BTRFS_SEND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
+        if (bss->parent) CloseHandle(bss->parent);
+        goto end2;
+    }
+
+    if (bss->parent) CloseHandle(bss->parent);
+
+    stream = CreateFileW(file, FILE_WRITE_DATA | DELETE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (stream == INVALID_HANDLE_VALUE) {
+        ShowSendError(IDS_SEND_CANT_OPEN_FILE, file, GetLastError(), format_message(GetLastError()).c_str());
+        goto end2;
+    }
+
+    memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(BTRFS_SEND_MAGIC));
+    header.version = 1;
+
+    if (!WriteFile(stream, &header, sizeof(header), NULL, NULL)) {
+        ShowSendError(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        goto end;
+    }
+
+    do {
+        Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, NULL, 0, buf, SEND_BUFFER_LEN);
+
+        if (NT_SUCCESS(Status)) {
+            if (!WriteFile(stream, buf, iosb.Information, NULL, NULL))
+                ShowSendError(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        }
+    } while (NT_SUCCESS(Status));
+
+    if (Status != STATUS_END_OF_FILE) {
+        ShowSendError(IDS_SEND_FSCTL_BTRFS_READ_SEND_BUFFER_FAILED, Status, format_ntstatus(Status).c_str());
+        goto end;
+    }
+
+    end.length = 0;
+    end.cmd = BTRFS_SEND_CMD_END;
+    end.csum = 0x9dc96c50;
+
+    if (!WriteFile(stream, &end, sizeof(end), NULL, NULL)) {
+        ShowSendError(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        goto end;
+    }
+
+    SetEndOfFile(stream);
+
+    ShowSendError(IDS_SEND_SUCCESS);
+    success = TRUE;
+
+end:
+    if (!success) {
+        FILE_DISPOSITION_INFO fdi;
+
+        fdi.DeleteFile = TRUE;
+
+        SetFileInformationByHandle(stream, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO));
+    }
+
+    CloseHandle(stream);
+    stream = INVALID_HANDLE_VALUE;
+
+end2:
+    CloseHandle(dirh);
+    dirh = INVALID_HANDLE_VALUE;
+
+end3:
+    free(buf);
+    buf = NULL;
+
+    started = FALSE;
+
+    SetDlgItemTextW(hwnd, IDCANCEL, closetext);
+    EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
+    EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), TRUE);
+    EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), TRUE);
+
+    return 0;
+}
+
+static DWORD WINAPI send_thread(LPVOID lpParameter) {
+    BtrfsSend* bs = (BtrfsSend*)lpParameter;
+
+    return bs->Thread();
+}
+
+void BtrfsSend::StartSend(HWND hwnd) {
+    WCHAR s[255];
+    HWND cl;
+    ULONG num_clones;
+
+    if (started)
+        return;
+
+    GetDlgItemTextW(hwnd, IDC_STREAM_DEST, file, sizeof(file) / sizeof(WCHAR));
+
+    if (file[0] == 0)
+        return;
+
+    if (incremental) {
+        WCHAR parent[MAX_PATH];
+
+        GetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent, sizeof(parent) / sizeof(WCHAR));
+
+        if (parent[0] == 0)
+            return;
+    }
+
+    started = TRUE;
+    ShowSendError(IDS_SEND_WRITING);
+
+    LoadStringW(module, IDS_SEND_CANCEL, s, sizeof(s) / sizeof(WCHAR));
+    SetDlgItemTextW(hwnd, IDCANCEL, s);
+
+    EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
+    EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), FALSE);
+    EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), FALSE);
+
+    clones.clear();
+
+    cl = GetDlgItem(hwnd, IDC_CLONE_LIST);
+    num_clones = SendMessageW(cl, LB_GETCOUNT, 0, 0);
+
+    if ((LRESULT)num_clones != LB_ERR) {
+        ULONG i;
+
+        for (i = 0; i < num_clones; i++) {
+            WCHAR* s;
+            ULONG len;
+
+            len = SendMessageW(cl, LB_GETTEXTLEN, i, 0);
+            s = (WCHAR*)malloc((len + 1) * sizeof(WCHAR));
+
+            SendMessageW(cl, LB_GETTEXT, i, (LPARAM)s);
+
+            clones.push_back(s);
+
+            free(s);
+        }
+    }
+
+    thread = CreateThread(NULL, 0, send_thread, this, 0, NULL);
+
+    if (!thread)
+        ShowError(NULL, GetLastError());
+}
+
+void BtrfsSend::Browse(HWND hwnd) {
+    OPENFILENAMEW ofn;
+
+    file[0] = 0;
+
+    memset(&ofn, 0, sizeof(OPENFILENAMEW));
+    ofn.lStructSize = sizeof(OPENFILENAMEW);
+    ofn.hwndOwner = hwnd;
+    ofn.hInstance = module;
+    ofn.lpstrFile = file;
+    ofn.nMaxFile = sizeof(file) / sizeof(WCHAR);
+
+    if (!GetSaveFileNameW(&ofn))
+        return;
+
+    SetDlgItemTextW(hwnd, IDC_STREAM_DEST, file);
+}
+
+void BtrfsSend::BrowseParent(HWND hwnd) {
+    BROWSEINFOW bi;
+    PIDLIST_ABSOLUTE root, pidl;
+    HRESULT hr;
+    WCHAR parent[MAX_PATH], volpathw[MAX_PATH];
+    HANDLE h;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_get_file_ids bgfi;
+
+    if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) {
+        ShowStringError(hwnd, IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return;
+    }
+
+    hr = SHParseDisplayName(volpathw, 0, &root, 0, 0);
+    if (FAILED(hr)) {
+        ShowStringError(hwnd, IDS_SHPARSEDISPLAYNAME_FAILED);
+        return;
+    }
+
+    memset(&bi, 0, sizeof(BROWSEINFOW));
+
+    bi.hwndOwner = hwnd;
+    bi.pidlRoot = root;
+    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON;
+
+    pidl = SHBrowseForFolderW(&bi);
+
+    if (!pidl)
+        return;
+
+    if (!SHGetPathFromIDListW(pidl, parent)) {
+        ShowStringError(hwnd, IDS_SHGETPATHFROMIDLIST_FAILED);
+        return;
+    }
+
+    h = CreateFileW(parent, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowStringError(hwnd, IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str());
+        return;
+    }
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
+    if (!NT_SUCCESS(Status)) {
+        ShowStringError(hwnd, IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str());
+        CloseHandle(h);
+        return;
+    }
+
+    CloseHandle(h);
+
+    if (bgfi.inode != 0x100 || bgfi.top) {
+        ShowStringError(hwnd, IDS_NOT_SUBVOL);
+        return;
+    }
+
+    SetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent);
+}
+
+void BtrfsSend::AddClone(HWND hwnd) {
+    BROWSEINFOW bi;
+    PIDLIST_ABSOLUTE root, pidl;
+    HRESULT hr;
+    WCHAR path[MAX_PATH], volpathw[MAX_PATH];
+    HANDLE h;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_get_file_ids bgfi;
+
+    if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) {
+        ShowStringError(hwnd, IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
+        return;
+    }
+
+    hr = SHParseDisplayName(volpathw, 0, &root, 0, 0);
+    if (FAILED(hr)) {
+        ShowStringError(hwnd, IDS_SHPARSEDISPLAYNAME_FAILED);
+        return;
+    }
+
+    memset(&bi, 0, sizeof(BROWSEINFOW));
+
+    bi.hwndOwner = hwnd;
+    bi.pidlRoot = root;
+    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON;
+
+    pidl = SHBrowseForFolderW(&bi);
+
+    if (!pidl)
+        return;
+
+    if (!SHGetPathFromIDListW(pidl, path)) {
+        ShowStringError(hwnd, IDS_SHGETPATHFROMIDLIST_FAILED);
+        return;
+    }
+
+    h = CreateFileW(path, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowStringError(hwnd, IDS_SEND_CANT_OPEN_DIR, path, GetLastError(), format_message(GetLastError()).c_str());
+        return;
+    }
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
+    if (!NT_SUCCESS(Status)) {
+        ShowStringError(hwnd, IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str());
+        CloseHandle(h);
+        return;
+    }
+
+    CloseHandle(h);
+
+    if (bgfi.inode != 0x100 || bgfi.top) {
+        ShowStringError(hwnd, IDS_NOT_SUBVOL);
+        return;
+    }
+
+    SendMessageW(GetDlgItem(hwnd, IDC_CLONE_LIST), LB_ADDSTRING, 0, (LPARAM)path);
+}
+
+void BtrfsSend::RemoveClone(HWND hwnd) {
+    LRESULT sel;
+    HWND cl = GetDlgItem(hwnd, IDC_CLONE_LIST);
+
+    sel = SendMessageW(cl, LB_GETCURSEL, 0, 0);
+
+    if (sel == LB_ERR)
+        return;
+
+    SendMessageW(cl, LB_DELETESTRING, sel, 0);
+
+    if (SendMessageW(cl, LB_GETCURSEL, 0, 0) == LB_ERR)
+        EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), FALSE);
+}
+
+INT_PTR BtrfsSend::SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+            this->hwnd = hwndDlg;
+
+            GetDlgItemTextW(hwndDlg, IDCANCEL, closetext, sizeof(closetext) / sizeof(WCHAR));
+        break;
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                            StartSend(hwndDlg);
+                        return TRUE;
+
+                        case IDCANCEL:
+                            if (started) {
+                                TerminateThread(thread, 0);
+
+                                if (stream != INVALID_HANDLE_VALUE) {
+                                    FILE_DISPOSITION_INFO fdi;
+
+                                    fdi.DeleteFile = TRUE;
+
+                                    SetFileInformationByHandle(stream, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO));
+                                    CloseHandle(stream);
+                                }
+
+                                if (dirh != INVALID_HANDLE_VALUE)
+                                    CloseHandle(dirh);
+
+                                started = FALSE;
+
+                                SetDlgItemTextW(hwndDlg, IDCANCEL, closetext);
+
+                                EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
+                                EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), TRUE);
+                                EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), TRUE);
+                            } else
+                                EndDialog(hwndDlg, 1);
+                        return TRUE;
+
+                        case IDC_BROWSE:
+                            Browse(hwndDlg);
+                        return TRUE;
+
+                        case IDC_INCREMENTAL:
+                            incremental = IsDlgButtonChecked(hwndDlg, LOWORD(wParam));
+
+                            EnableWindow(GetDlgItem(hwnd, IDC_PARENT_SUBVOL), incremental);
+                            EnableWindow(GetDlgItem(hwnd, IDC_PARENT_BROWSE), incremental);
+                        return TRUE;
+
+                        case IDC_PARENT_BROWSE:
+                            BrowseParent(hwndDlg);
+                        return TRUE;
+
+                        case IDC_CLONE_ADD:
+                            AddClone(hwndDlg);
+                        return TRUE;
+
+                        case IDC_CLONE_REMOVE:
+                            RemoveClone(hwndDlg);
+                        return TRUE;
+                    }
+                break;
+
+                case LBN_SELCHANGE:
+                    switch (LOWORD(wParam)) {
+                        case IDC_CLONE_LIST:
+                            EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), TRUE);
+                        return TRUE;
+                    }
+                break;
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsSend* bs;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bs = (BtrfsSend*)lParam;
+    } else
+        bs = (BtrfsSend*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+    if (bs)
+        return bs->SendDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsSend::Open(HWND hwnd, LPWSTR path) {
+    subvol = path;
+
+    if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SEND_SUBVOL), hwnd, stub_SendDlgProc, (LPARAM)this) <= 0)
+        ShowError(hwnd, GetLastError());
+}
+
+void CALLBACK SendSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    HANDLE token;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+    BtrfsSend* bs;
+
+    set_dpi_aware();
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    bs = new BtrfsSend;
+
+    bs->Open(hwnd, lpszCmdLine);
+
+    delete bs;
+
+    CloseHandle(token);
+}
+
+static void send_subvol(std::wstring subvol, std::wstring file, std::wstring parent, std::vector<std::wstring> clones) {
+    char* buf;
+    HANDLE dirh, stream;
+    ULONG bss_size, i;
+    btrfs_send_subvol* bss;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS Status;
+    btrfs_send_header header;
+    btrfs_send_command end;
+    BOOL success = FALSE;
+
+    buf = (char*)malloc(SEND_BUFFER_LEN);
+
+    dirh = CreateFileW(subvol.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    if (dirh == INVALID_HANDLE_VALUE)
+        goto end3;
+
+    stream = CreateFileW(file.c_str(), FILE_WRITE_DATA | DELETE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (stream == INVALID_HANDLE_VALUE) {
+        CloseHandle(dirh);
+        goto end3;
+    }
+
+    bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE));
+    bss = (btrfs_send_subvol*)malloc(bss_size);
+    memset(bss, 0, bss_size);
+
+    if (parent != L"") {
+        HANDLE parenth;
+
+        parenth = CreateFileW(parent.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (parenth == INVALID_HANDLE_VALUE)
+            goto end2;
+
+        bss->parent = parenth;
+    } else
+        bss->parent = NULL;
+
+    bss->num_clones = clones.size();
+
+    for (i = 0; i < bss->num_clones; i++) {
+        HANDLE h;
+
+        h = CreateFileW(clones[i].c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+        if (h == INVALID_HANDLE_VALUE) {
+            ULONG j;
+
+            for (j = 0; j < i; j++) {
+                CloseHandle(bss->clones[j]);
+            }
+
+            if (bss->parent) CloseHandle(bss->parent);
+            goto end2;
+        }
+
+        bss->clones[i] = h;
+    }
+
+    Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, bss_size, NULL, 0);
+
+    for (i = 0; i < bss->num_clones; i++) {
+        CloseHandle(bss->clones[i]);
+    }
+
+    if (bss->parent) CloseHandle(bss->parent);
+
+    if (!NT_SUCCESS(Status))
+        goto end2;
+
+    memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(BTRFS_SEND_MAGIC));
+    header.version = 1;
+
+    if (!WriteFile(stream, &header, sizeof(header), NULL, NULL))
+        goto end2;
+
+    do {
+        Status = NtFsControlFile(dirh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, NULL, 0, buf, SEND_BUFFER_LEN);
+
+        if (NT_SUCCESS(Status))
+            WriteFile(stream, buf, iosb.Information, NULL, NULL);
+    } while (NT_SUCCESS(Status));
+
+    if (Status != STATUS_END_OF_FILE)
+        goto end2;
+
+    end.length = 0;
+    end.cmd = BTRFS_SEND_CMD_END;
+    end.csum = 0x9dc96c50;
+
+    if (!WriteFile(stream, &end, sizeof(end), NULL, NULL))
+        goto end2;
+
+    SetEndOfFile(stream);
+
+    success = TRUE;
+
+end2:
+    if (!success) {
+        FILE_DISPOSITION_INFO fdi;
+
+        fdi.DeleteFile = TRUE;
+
+        SetFileInformationByHandle(stream, FileDispositionInfo, &fdi, sizeof(FILE_DISPOSITION_INFO));
+    }
+
+    CloseHandle(dirh);
+    CloseHandle(stream);
+
+end3:
+    free(buf);
+}
+
+void CALLBACK SendSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    LPWSTR* args;
+    int num_args;
+    std::wstring subvol = L"", parent = L"", file = L"";
+    std::vector<std::wstring> clones;
+
+    args = CommandLineToArgvW(lpszCmdLine, &num_args);
+
+    if (!args)
+        return;
+
+    if (num_args >= 2) {
+        HANDLE token;
+        TOKEN_PRIVILEGES tp;
+        LUID luid;
+        int i;
+
+        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
+            goto end;
+
+        if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid))
+            goto end;
+
+        tp.PrivilegeCount = 1;
+        tp.Privileges[0].Luid = luid;
+        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+        if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
+            goto end;
+
+        CloseHandle(token);
+
+        for (i = 0; i < num_args; i++) {
+            if (args[i][0] == '-') {
+                if (args[i][2] == 0 && i < num_args - 1) {
+                    if (args[i][1] == 'p') {
+                        parent = args[i+1];
+                        i++;
+                    } else if (args[i][1] == 'c') {
+                        clones.push_back(args[i+1]);
+                        i++;
+                    }
+                }
+            } else {
+                if (subvol == L"")
+                    subvol = args[i];
+                else if (file == L"")
+                    file = args[i];
+            }
+        }
+
+        if (subvol != L"" && file != L"")
+            send_subvol(subvol, file, parent, clones);
+    }
+
+end:
+    LocalFree(args);
+}

Modification de propriétés sur dll/shellext/shellbtrfs/send.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/send.h
===================================================================
--- dll/shellext/shellbtrfs/send.h	(nonexistent)
+++ dll/shellext/shellbtrfs/send.h	(copie de travail)
@@ -0,0 +1,64 @@
+/* Copyright (c) Mark Harmstone 2017
+ *
+ * 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/>. */
+
+#ifdef __REACTOS__
+#include "btrfs.h"
+#include <stdlib.h>
+#else
+#include "../btrfs.h"
+#endif
+#include <string>
+#include <vector>
+
+class BtrfsSend {
+public:
+    BtrfsSend() {
+        started = FALSE;
+        file[0] = 0;
+        dirh = INVALID_HANDLE_VALUE;
+        stream = INVALID_HANDLE_VALUE;
+        subvol = L"";
+        buf = NULL;
+        incremental = FALSE;
+    }
+
+    ~BtrfsSend() {
+        if (buf)
+            free(buf);
+    }
+
+    void Open(HWND hwnd, WCHAR* path);
+    INT_PTR SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+    DWORD Thread();
+
+private:
+    void StartSend(HWND hwnd);
+    void Browse(HWND hwnd);
+    void BrowseParent(HWND hwnd);
+    void AddClone(HWND hwnd);
+    void RemoveClone(HWND hwnd);
+    void ShowSendError(UINT msg, ...);
+
+    BOOL started;
+    BOOL incremental;
+    WCHAR file[MAX_PATH], closetext[255];
+    HANDLE thread, dirh, stream;
+    HWND hwnd;
+    std::wstring subvol;
+    char* buf;
+    std::vector <std::wstring> clones;
+};

Modification de propriétés sur dll/shellext/shellbtrfs/send.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/shellbtrfs.rc
===================================================================
--- dll/shellext/shellbtrfs/shellbtrfs.rc	(révision 75772)
+++ dll/shellext/shellbtrfs/shellbtrfs.rc	(copie de travail)
@@ -7,7 +7,7 @@
 //
 // Generated from the TEXTINCLUDE 2 resource.
 //
-#include "afxres.h"
+#include "winres.h"
 
 /////////////////////////////////////////////////////////////////////////////
 #undef APSTUDIO_READONLY_SYMBOLS
@@ -27,18 +27,18 @@
 // TEXTINCLUDE
 //
 
-1 TEXTINCLUDE 
+1 TEXTINCLUDE
 BEGIN
     "resource.h\0"
 END
 
-2 TEXTINCLUDE 
+2 TEXTINCLUDE
 BEGIN
-    "#include ""afxres.h""\r\n"
+    "#include ""winres.h""\r\n"
     "\0"
 END
 
-3 TEXTINCLUDE 
+3 TEXTINCLUDE
 BEGIN
     "\r\n"
     "\0"
@@ -79,12 +79,12 @@
         BLOCK "080904b0"
         BEGIN
             VALUE "FileDescription", "WinBtrfs shell extension"
-            VALUE "FileVersion", "0.7"
+            VALUE "FileVersion", "1.0"
             VALUE "InternalName", "btrfs"
-            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016-17"
             VALUE "OriginalFilename", "shellbtrfs.dll"
             VALUE "ProductName", "WinBtrfs"
-            VALUE "ProductVersion", "0.7"
+            VALUE "ProductVersion", "1.0"
         END
     END
     BLOCK "VarFileInfo"
@@ -101,7 +101,7 @@
 
 IDD_PROP_SHEET DIALOGEX 0, 0, 235, 245
 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION
-CAPTION "s"
+CAPTION "Inode property sheet"
 FONT 8, "MS Shell Dlg", 400, 0, 0x0
 BEGIN
     LTEXT           "Subvolume:",IDC_STATIC,14,21,38,8
@@ -119,9 +119,9 @@
     LTEXT           "Read",IDC_STATIC,50,134,17,8
     LTEXT           "Write",IDC_STATIC,89,134,18,8
     LTEXT           "Execute",IDC_STATIC,129,134,30,8
-    CONTROL         "",IDC_USERR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,146,16,10
-    CONTROL         "",IDC_GROUPR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,157,16,10
-    CONTROL         "",IDC_OTHERR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,167,16,10
+    CONTROL         "",IDC_USERR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,147,16,10
+    CONTROL         "",IDC_GROUPR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,158,16,10
+    CONTROL         "",IDC_OTHERR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,168,16,10
     CONTROL         "",IDC_USERW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,93,147,16,10
     CONTROL         "",IDC_GROUPW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,93,158,16,10
     CONTROL         "",IDC_OTHERW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,93,168,16,10
@@ -130,12 +130,18 @@
     CONTROL         "",IDC_OTHERX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,135,168,16,10
     GROUPBOX        "Flags",IDC_STATIC,7,190,221,48
     CONTROL         "Disable Copy-on-Write",IDC_NODATACOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,204,86,10
-    LTEXT           "(blank)",IDC_SUBVOL,78,21,99,8
-    LTEXT           "(blank)",IDC_INODE,78,35,100,8
+    LTEXT           "(blank)",IDC_INODE,78,35,70,8
     LTEXT           "(blank)",IDC_TYPE,78,49,116,8
     CONTROL         "Compress",IDC_COMPRESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,218,46,10
     LTEXT           "Size on disk:",IDC_STATIC,14,63,61,8
     CONTROL         "%s (<a>Details</a>)",IDC_SIZE_ON_DISK,"SysLink",WS_TABSTOP,78,63,142,8
+    COMBOBOX        IDC_COMPRESS_TYPE,63,217,48,13,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Readonly subvolume",IDC_SUBVOL_RO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,124,204,80,10
+    LTEXT           "(blank)",IDC_SUBVOL,78,21,70,8
+    PUSHBUTTON      "&Open as Admin",IDC_OPEN_ADMIN,151,21,70,14
+    CONTROL         "Set UID",IDC_SETUID,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,177,147,40,10
+    CONTROL         "Set GID",IDC_SETGID,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,177,158,40,10
+    CONTROL         "Sticky",IDC_STICKY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,177,168,34,10
 END
 
 IDD_SIZE_DETAILS DIALOGEX 0, 0, 212, 85
@@ -154,7 +160,208 @@
     LTEXT           "(blank)",IDC_SIZE_LZO,63,46,142,8
 END
 
+IDD_VOL_PROP_SHEET DIALOGEX 0, 0, 235, 251
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION
+CAPTION "s"
+FONT 8, "MS Shell Dlg", 400, 0, 0x0
+BEGIN
+    PUSHBUTTON      "Show &usage...",IDC_VOL_SHOW_USAGE,154,47,67,19
+    PUSHBUTTON      "&Balance...",IDC_VOL_BALANCE,154,105,67,19
+    PUSHBUTTON      "&Devices...",IDC_VOL_DEVICES,154,162,67,19
+    LTEXT           "UUID:",IDC_STATIC,7,15,20,8
+    LTEXT           "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",IDC_UUID,32,15,294,8
+    GROUPBOX        "Usage",IDC_STATIC,7,31,221,53
+    LTEXT           "Show detailed information about internal filesystem usage. This is the equivalent to the command ""btrfs fi usage"" on Linux.",IDC_STATIC,14,44,131,33
+    GROUPBOX        "Balance",IDC_STATIC,7,87,221,53
+    LTEXT           "Balancing reads and rewrites data and metadata. It can be used to consolidate free space, as well as to convert between different RAID types.",IDC_STATIC,15,98,131,39
+    GROUPBOX        "Devices",IDC_STATIC,7,146,221,45
+    LTEXT           "Allows you to add disks or partitions to this filesystem, or remove those already present.",IDC_STATIC,14,159,131,30
+    GROUPBOX        "Scrub",IDC_STATIC,7,199,221,45
+    LTEXT           "Scrubbing verifies the data and metadata of a filesystem, and where possible will correct any errors.",IDC_STATIC,15,212,131,27
+    PUSHBUTTON      "&Scrub...",IDC_VOL_SCRUB,154,215,67,19
+END
 
+IDD_VOL_USAGE DIALOGEX 0, 0, 235, 242
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Volume usage"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,62,221,50,14
+    PUSHBUTTON      "&Refresh",IDC_USAGE_REFRESH,124,221,50,14
+    EDITTEXT        IDC_USAGE_BOX,7,7,221,208,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL
+END
+
+IDD_BALANCE_OPTIONS DIALOGEX 0, 0, 303, 138
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Balance options"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,91,117,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,161,117,50,14
+    CONTROL         "&Profiles:",IDC_PROFILES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,6,41,10
+    CONTROL         "Single",IDC_PROFILES_SINGLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,19,35,10
+    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         "&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
+    EDITTEXT        IDC_USAGE_END,58,94,19,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_USAGE_END_SPINNER,"msctls_updown32",UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,77,94,11,14
+    LTEXT           "% to",IDC_STATIC,39,97,16,8
+    CONTROL         "&Device:",IDC_DEVID,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,6,34,10
+    COMBOBOX        IDC_DEVID_COMBO,141,6,155,30,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "%",IDC_STATIC,91,97,8,8
+    CONTROL         "Device &range:",IDC_DRANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,19,59,10
+    EDITTEXT        IDC_DRANGE_END,159,32,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "to",IDC_STATIC,148,34,8,8
+    EDITTEXT        IDC_DRANGE_START,104,32,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "&Virtual range:",IDC_VRANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,49,59,10
+    EDITTEXT        IDC_VRANGE_END,160,62,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "to",IDC_STATIC,148,64,8,8
+    EDITTEXT        IDC_VRANGE_START,104,62,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "&Limit:",IDC_LIMIT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,80,33,10
+    EDITTEXT        IDC_LIMIT_START,104,94,19,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_LIMIT_START_SPINNER,"msctls_updown32",UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,123,94,11,14
+    EDITTEXT        IDC_LIMIT_END,150,94,19,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_LIMIT_END_SPINNER,"msctls_updown32",UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,169,94,11,14
+    LTEXT           "to",IDC_STATIC,139,97,8,8
+    CONTROL         "&Stripes:",IDC_STRIPES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,210,19,40,10
+    EDITTEXT        IDC_STRIPES_START,210,32,19,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_STRIPES_START_SPINNER,"msctls_updown32",UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,228,32,11,14
+    EDITTEXT        IDC_STRIPES_END,253,32,19,14,ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_STRIPES_END_SPINNER,"msctls_updown32",UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,272,32,11,14
+    LTEXT           "to",IDC_STATIC,242,35,8,8
+    CONTROL         "&Convert:",IDC_CONVERT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,210,49,36,10
+    COMBOBOX        IDC_CONVERT_COMBO,248,49,48,30,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "So&ft",IDC_SOFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,210,64,28,10
+END
+
+IDD_BALANCE DIALOGEX 0, 0, 254, 167
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Balance"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,102,146,50,14
+    CONTROL         "",IDC_BALANCE_PROGRESS,"msctls_progress32",WS_BORDER,7,95,240,14
+    CONTROL         "&Data",IDC_DATA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,30,10
+    CONTROL         "&Metadata",IDC_METADATA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,28,45,10
+    CONTROL         "&System",IDC_SYSTEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,51,38,10
+    PUSHBUTTON      "Options...",IDC_DATA_OPTIONS,70,6,50,14
+    PUSHBUTTON      "Options...",IDC_METADATA_OPTIONS,70,26,50,14
+    PUSHBUTTON      "Options...",IDC_SYSTEM_OPTIONS,70,47,50,14
+    LTEXT           "Status",IDC_BALANCE_STATUS,8,80,239,8
+    PUSHBUTTON      "&Start balance",IDC_START_BALANCE,13,117,69,14
+    PUSHBUTTON      "&Pause / resume",IDC_PAUSE_BALANCE,93,117,69,14
+    PUSHBUTTON      "&Cancel balance",IDC_CANCEL_BALANCE,173,117,69,14
+END
+
+IDD_DEVICES DIALOGEX 0, 0, 318, 203
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Devices"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,207,182,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,261,182,50,14
+    CONTROL         "",IDC_DEVLIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,7,304,144
+    PUSHBUTTON      "&Add device...",IDC_DEVICE_ADD,7,182,76,14
+    PUSHBUTTON      "&Refresh",IDC_DEVICE_REFRESH,7,159,76,14
+    PUSHBUTTON      "Remove &device",IDC_DEVICE_REMOVE,93,182,76,14
+    PUSHBUTTON      "Show &stats...",IDC_DEVICE_SHOW_STATS,235,159,76,14,WS_DISABLED
+    PUSHBUTTON      "Re&size...",IDC_DEVICE_RESIZE,149,159,76,14,WS_DISABLED
+END
+
+IDD_DEVICE_ADD DIALOGEX 0, 0, 261, 185
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Add device"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,66,164,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,145,164,50,14
+    CONTROL         "",IDC_DEVICE_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_SHOWSELALWAYS | WS_BORDER | WS_HSCROLL | WS_TABSTOP,7,7,247,148
+END
+
+IDD_SCRUB DIALOGEX 0, 0, 254, 162
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Scrub"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,102,141,50,14
+    CONTROL         "",IDC_SCRUB_PROGRESS,"msctls_progress32",WS_BORDER,7,95,240,14
+    LTEXT           "Status",IDC_SCRUB_STATUS,8,81,239,8
+    PUSHBUTTON      "&Start scrub",IDC_START_SCRUB,13,117,69,14
+    PUSHBUTTON      "&Pause / resume",IDC_PAUSE_SCRUB,93,117,69,14
+    PUSHBUTTON      "&Cancel scrub",IDC_CANCEL_SCRUB,173,117,69,14
+    EDITTEXT        IDC_SCRUB_INFO,7,7,240,69,ES_MULTILINE | ES_READONLY | WS_VSCROLL
+END
+
+IDD_DEVICE_STATS DIALOGEX 0, 0, 159, 113
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Device stats"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,24,91,50,14
+    LTEXT           "Device %llx:",IDC_DEVICE_ID,7,7,40,8
+    LTEXT           "Write errors:",IDC_STATIC,7,21,79,8
+    LTEXT           "Read errors:",IDC_STATIC,7,34,79,8
+    LTEXT           "Flush errors:",IDC_STATIC,7,47,79,8
+    LTEXT           "Corruption errors:",IDC_STATIC,7,60,79,8
+    LTEXT           "Generation errors:",IDC_STATIC,7,73,79,8
+    RTEXT           "%llu",IDC_WRITE_ERRS,87,21,65,8
+    RTEXT           "%llu",IDC_READ_ERRS,87,34,65,8
+    RTEXT           "%llu",IDC_FLUSH_ERRS,87,47,65,8
+    RTEXT           "%llu",IDC_CORRUPTION_ERRS,87,60,65,8
+    RTEXT           "%llu",IDC_GENERATION_ERRS,87,73,65,8
+    PUSHBUTTON      "&Reset",IDC_RESET_STATS,85,91,50,14
+END
+
+IDD_RECV_PROGRESS DIALOGEX 0, 0, 311, 83
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Receiving subvolume"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    PUSHBUTTON      "Cancel",IDCANCEL,130,62,50,14
+    CONTROL         "",IDC_RECV_PROGRESS,"msctls_progress32",WS_BORDER,7,33,297,24
+    LTEXT           "Receiving subvolume...",IDC_RECV_MSG,7,7,297,18
+END
+
+IDD_SEND_SUBVOL DIALOGEX 0, 0, 288, 149
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Send subvolume"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "&Write",IDOK,83,128,50,14
+    PUSHBUTTON      "&Close",IDCANCEL,156,128,50,14
+    EDITTEXT        IDC_STREAM_DEST,57,7,166,14,ES_AUTOHSCROLL
+    LTEXT           "Stream:",IDC_STATIC,7,11,26,8
+    PUSHBUTTON      "&Browse...",IDC_BROWSE,231,7,50,14
+    LTEXT           "Select a destination for the subvolume stream.",IDC_SEND_STATUS,7,93,274,22
+    CONTROL         "Incremental",IDC_INCREMENTAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,32,54,10
+    EDITTEXT        IDC_PARENT_SUBVOL,69,29,154,14,ES_AUTOHSCROLL | WS_DISABLED
+    PUSHBUTTON      "&Browse...",IDC_PARENT_BROWSE,231,29,50,14,WS_DISABLED
+    LTEXT           "Clone sources:",IDC_STATIC,7,52,46,8
+    LISTBOX         IDC_CLONE_LIST,69,50,154,36,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+    PUSHBUTTON      "&Add...",IDC_CLONE_ADD,231,50,50,14
+    PUSHBUTTON      "&Remove",IDC_CLONE_REMOVE,231,69,50,14,WS_DISABLED
+END
+
+IDD_RESIZE DIALOGEX 0, 0, 279, 133
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Resize device"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,81,112,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,148,112,50,14
+    LTEXT           "Device %llx:",IDC_RESIZE_DEVICE_ID,18,21,238,8
+    LTEXT           "Current size: %s",IDC_RESIZE_CURSIZE,18,37,238,8
+    CONTROL         "",IDC_RESIZE_SLIDER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,7,74,265,24
+    LTEXT           "New size: %s",IDC_RESIZE_NEWSIZE,18,53,238,8
+END
+
+
 /////////////////////////////////////////////////////////////////////////////
 //
 // DESIGNINFO
@@ -161,7 +368,7 @@
 //
 
 #ifdef APSTUDIO_INVOKED
-GUIDELINES DESIGNINFO 
+GUIDELINES DESIGNINFO
 BEGIN
     IDD_PROP_SHEET, DIALOG
     BEGIN
@@ -178,6 +385,94 @@
         TOPMARGIN, 7
         BOTTOMMARGIN, 78
     END
+
+    IDD_VOL_PROP_SHEET, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 228
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 244
+    END
+
+    IDD_VOL_USAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 228
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 235
+    END
+
+    IDD_BALANCE_OPTIONS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 296
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 131
+    END
+
+    IDD_BALANCE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 247
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 160
+    END
+
+    IDD_DEVICES, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 311
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 196
+    END
+
+    IDD_DEVICE_ADD, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 254
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 178
+    END
+
+    IDD_SCRUB, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 247
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 155
+    END
+
+    IDD_DEVICE_STATS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 152
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 105
+    END
+
+    IDD_RECV_PROGRESS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 304
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 76
+    END
+
+    IDD_SEND_SUBVOL, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 281
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 142
+    END
+
+    IDD_RESIZE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 272
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 126
+    END
 END
 #endif    // APSTUDIO_INVOKED
 
@@ -194,10 +489,10 @@
 // String Table
 //
 
-STRINGTABLE 
+STRINGTABLE
 BEGIN
     IDS_NEW_SUBVOL_HELP_TEXT "Creates a new Btrfs subvolume."
-    IDS_NEW_SUBVOL          "New subvolume"
+    IDS_NEW_SUBVOL          "&New subvolume"
     IDS_NEW_SUBVOL_FILENAME "New subvolume"
     IDS_CREATE_SNAPSHOT     "Create snapshot"
     IDS_CREATE_SNAPSHOT_HELP_TEXT "Creates a snapshot of a Btrfs subvolume."
@@ -205,17 +500,17 @@
     IDS_PROP_SHEET_TITLE    "Btrfs properties"
     IDS_INODE_FILE          "File"
     IDS_INODE_DIR           "Directory"
-    IDS_INODE_CHAR          "Character device (major %llu, minor %u)"
+    IDS_INODE_CHAR          "Character device (major %llu, minor %llu)"
 END
 
-STRINGTABLE 
+STRINGTABLE
 BEGIN
-    IDS_INODE_BLOCK         "Block device (major %llu, minor %u)"
+    IDS_INODE_BLOCK         "Block device (major %llu, minor %llu)"
     IDS_INODE_FIFO          "FIFO"
     IDS_INODE_SOCKET        "Socket"
     IDS_INODE_SYMLINK       "Symbolic link"
     IDS_INODE_UNKNOWN       "Unknown inode type %x"
-    IDS_SET_INODE_INFO_ERROR "FSCTL_BTRFS_SET_INODE_INFO returned %08x"
+    IDS_CANNOT_FIND_DEVICE  "Cannot find device."
     IDS_SIZE_BYTE           "%s byte"
     IDS_SIZE_BYTES          "%s bytes"
     IDS_SIZE_KB             "%1.1f KB"
@@ -228,12 +523,231 @@
     IDS_INODE_CHAR_SIMPLE   "Character device"
 END
 
-STRINGTABLE 
+STRINGTABLE
 BEGIN
     IDS_INODE_BLOCK_SIMPLE  "Block device"
+    IDS_VOL_PROP_SHEET_TITLE "Btrfs"
     IDS_SIZE_LARGE          "%s (%s)"
+    IDS_SINGLE              "single"
+    IDS_DUP                 "DUP"
+    IDS_RAID0               "RAID0"
+    IDS_RAID1               "RAID1"
+    IDS_RAID10              "RAID10"
+    IDS_RAID5               "RAID5"
+    IDS_RAID6               "RAID6"
+    IDS_USAGE_DATA          "Data, %s: size: %s, used: %s"
+    IDS_USAGE_MIXED         "Data / metadata, %s: size: %s, used: %s"
+    IDS_USAGE_METADATA      "Metadata, %s: size: %s, used: %s"
+    IDS_USAGE_SYSTEM        "System, %s: size: %s, used: %s"
+    IDS_USAGE_UNALLOC       "Unallocated:"
+    IDS_UNKNOWN_DEVICE      "(unknown device %llu)"
 END
 
+STRINGTABLE
+BEGIN
+    IDS_USAGE_DEV_SIZE      "Device size:\t\t%s"
+    IDS_USAGE_DEV_ALLOC     "Device allocated:\t\t%s"
+    IDS_USAGE_DEV_UNALLOC   "Device unallocated:\t\t%s"
+    IDS_USAGE_DATA_RATIO    "Data ratio:\t\t%1.2f"
+    IDS_USAGE_METADATA_RATIO "Metadata ratio:\t\t%1.2f"
+    IDS_NO_BALANCE          "No balance is currently running."
+    IDS_SINGLE2             "Single"
+    IDS_DEVID_LIST          "%llu: %s"
+    IDS_BALANCE_RUNNING     "Balance is currently running (%llu out of %llu chunks processed, %1.1f%%)"
+    IDS_DRANGE_END_BEFORE_START "Device range end is before start."
+    IDS_VRANGE_END_BEFORE_START "Virtual range end is before start."
+    IDS_LIMIT_END_BEFORE_START "Limit end is before start."
+    IDS_STRIPES_END_BEFORE_START "Stripes end is before start."
+    IDS_USAGE_END_BEFORE_START "Usage end is before start."
+    IDS_ERROR               "Error"
+    IDS_BALANCE_COMPLETE    "Balance completed successfully."
+END
+
+STRINGTABLE
+BEGIN
+    IDS_BALANCE_PAUSED      "Balance is currently paused (%llu out of %llu chunks processed, %1.1f%%)"
+    IDS_BALANCE_CANCELLED   "Balance cancelled."
+    IDS_DEVLIST_ID          "ID"
+    IDS_DEVLIST_DESC        "Description"
+    IDS_DEVLIST_READONLY    "Read-only"
+    IDS_DEVLIST_SIZE        "Size"
+    IDS_DEVLIST_READONLY_YES "Yes"
+    IDS_DEVLIST_READONLY_NO "No"
+    IDS_DEVLIST_ALLOC       "Allocated"
+    IDS_DEVLIST_ALLOC_PC    "%"
+    IDS_BALANCE_RUNNING_REMOVAL
+                            "Currently removing device %llu (%llu out of %llu chunks processed, %1.1f%%)"
+    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."
+    IDS_PARTITION           "Partition %u"
+    IDS_WHOLE_DISK          "Whole disk"
+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
+                            "Are you sure that you want to remove device %s, %s?"
+    IDS_CONFIRMATION_TITLE  "Confirmation"
+    IDS_ADD_DEVICE_CONFIRMATION
+                            "Are you sure that you want to add this device?"
+    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)"
+    IDS_DISK_NUM            "Disk %u"
+    IDS_DISK_PART_NUM       "Disk %u, partition %u"
+    IDS_NO_SCRUB            "Scrub not running."
+    IDS_SCRUB_RUNNING       "Scrub currently running (%llu out of %llu chunks processed, %1.1f%%)"
+    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
+                            "Recovered from data checksum error at %llx on device %llx."
+    IDS_SCRUB_MSG_RECOVERABLE_METADATA
+                            "Recovered from metadata checksum error at %llx on device %llx."
+END
+
+STRINGTABLE
+BEGIN
+    IDS_SCRUB_MSG_UNRECOVERABLE_DATA
+                            "Unrecoverable data checksum error at %llx on device %llx (%.*s, offset %llx)"
+    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
+                            "Unrecoverable metadata checksum error at %llx on device %llx (root %llx, level %x)"
+    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
+                            "%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
+                            "Recovered from parity error at %llx on device %llx."
+    IDS_COMPRESS_ANY        "(any)"
+    IDS_COMPRESS_ZLIB       "Zlib"
+    IDS_COMPRESS_LZO        "LZO"
+END
+
+STRINGTABLE
+BEGIN
+    IDS_STANDALONE_PROPSHEET_TITLE "Inode property sheet"
+    IDS_REFLINK_PASTE       "Ref&link Paste"
+    IDS_REFLINK_PASTE_HELP  "Do a lightweight copy of files using reference counting."
+    IDS_RECV_SUBVOL         "Re&ceive subvolume..."
+    IDS_RECV_SUBVOL_HELP    "Recreate a previously exported subvolume."
+    IDS_RECV_CANT_OPEN_FILE "%S: Couldn't open %s (error %u, %s)."
+    IDS_RECV_READFILE_FAILED "ReadFile failed (error %u, %s)."
+    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_RTLUTF8TOUNICODEN_FAILED "RtlUTF8ToUnicodeN returned %08x (%s)."
+    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
+                            "FSCTL_SET_REPARSE_POINT returned %08x (%s)."
+END
+
+STRINGTABLE
+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
+                            "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_SETFILEINFO_FAILED
+                            "SetFileInformationByHandle failed (error %u, %s)."
+    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
+                            "SetFileAttributes failed (error %u, %s)."
+    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."
+    IDS_RECV_UNSUPPORTED_VERSION "Unsupported version %u."
+    IDS_RECV_SETEAFILE_FAILED "NtSetEaFile returned %08x (%s)."
+END
+
+STRINGTABLE
+BEGIN
+    IDS_RECV_RECEIVED_SUBVOL_FAILED
+                            "FSCTL_BTRFS_RECEIVED_SUBVOL returned %08x (%s)."
+    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
+                            "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
+                            "FSCTL_BTRFS_CREATE_SNAPSHOT returned %08x (%s)."
+    IDS_RECV_GETVOLUMEPATHNAME_FAILED
+                            "GetVolumePathName failed (error %u, %s)."
+    IDS_RECV_DELETEFILE_FAILED "DeleteFile failed for %s (error %u, %s)."
+    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
+                            "FSCTL_DUPLICATE_EXTENTS_TO_FILE returned %08x (%s)."
+END
+
+STRINGTABLE
+BEGIN
+    IDS_RECV_SUCCESS_PLURAL "Received %u subvolumes successfully."
+    IDS_SEND_SUBVOL         "&Send subvolume..."
+    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
+                            "FSCTL_BTRFS_SEND_SUBVOL returned error %08x (%s)."
+    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
+                            "GetFileInformationByHandle failed (error %u, %s)."
+    IDS_SEND_NOT_READONLY   "Subvolume not readonly."
+    IDS_NOT_SUBVOL          "Directory was not a subvolume."
+    IDS_GET_FILE_IDS_FAILED "FSCTL_BTRFS_GET_FILE_IDS returned error %08x (%s)."
+    IDS_SHPARSEDISPLAYNAME_FAILED "SHParseDisplayName failed."
+    IDS_SHGETPATHFROMIDLIST_FAILED "SHGetPathFromIDList failed."
+    IDS_SEND_PARENT_NOT_READONLY "Parent subvolume not readonly."
+END
+
+STRINGTABLE
+BEGIN
+    IDS_SEND_CANCEL         "&Cancel"
+    IDS_SEND_WRITING        "Writing..."
+    IDS_MISSING             "(missing)"
+    IDS_RESIZE_SUCCESSFUL   "Device %llx successfully resized to %s."
+    IDS_BALANCE_RUNNING_SHRINK
+                            "Currently shrinking device %llu (%llu out of %llu chunks processed, %1.1f%%)"
+    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."
+    IDS_BALANCE_FAILED_SHRINK "Device shrinking failed (error %08x, %s)"
+END
+
 #endif    // English (U.K.) resources
 /////////////////////////////////////////////////////////////////////////////
 
Index: dll/shellext/shellbtrfs/shellext.h
===================================================================
--- dll/shellext/shellbtrfs/shellext.h	(nonexistent)
+++ dll/shellext/shellbtrfs/shellext.h	(copie de travail)
@@ -0,0 +1,185 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ *
+ * 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/>. */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#ifndef __REACTOS__
+#define WINVER 0x0A00 // Windows 10
+#define _WIN32_WINNT 0x0A00
+#endif
+
+#ifndef __REACTOS__
+#include <windows.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <strsafe.h>
+#include <ndk/iofuncs.h>
+#endif
+#include <string>
+#ifndef __REACTOS__
+#include "../btrfs.h"
+#include "../btrfsioctl.h"
+#else
+#include "btrfs.h"
+#include "btrfsioctl.h"
+#endif
+
+#ifndef __REACTOS__
+#define STATUS_SUCCESS                  (NTSTATUS)0x00000000
+#define STATUS_BUFFER_OVERFLOW          (NTSTATUS)0x80000005
+#define STATUS_END_OF_FILE              (NTSTATUS)0xc0000011
+#define STATUS_MORE_PROCESSING_REQUIRED (NTSTATUS)0xc0000016
+#define STATUS_BUFFER_TOO_SMALL         (NTSTATUS)0xc0000023
+#define STATUS_DEVICE_NOT_READY         (NTSTATUS)0xc00000a3
+#define STATUS_CANNOT_DELETE            (NTSTATUS)0xc0000121
+#define STATUS_NOT_FOUND                (NTSTATUS)0xc0000225
+#endif
+
+#define BLOCK_FLAG_DATA         0x001
+#define BLOCK_FLAG_SYSTEM       0x002
+#define BLOCK_FLAG_METADATA     0x004
+#define BLOCK_FLAG_RAID0        0x008
+#define BLOCK_FLAG_RAID1        0x010
+#define BLOCK_FLAG_DUPLICATE    0x020
+#define BLOCK_FLAG_RAID10       0x040
+#define BLOCK_FLAG_RAID5        0x080
+#define BLOCK_FLAG_RAID6        0x100
+
+#define BTRFS_TYPE_FILE      1
+#define BTRFS_TYPE_DIRECTORY 2
+#define BTRFS_TYPE_CHARDEV   3
+#define BTRFS_TYPE_BLOCKDEV  4
+#define BTRFS_TYPE_FIFO      5
+#define BTRFS_TYPE_SOCKET    6
+#define BTRFS_TYPE_SYMLINK   7
+
+#ifdef _MSC_VER
+#define funcname __FUNCTION__
+#else
+#define funcname __func__
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifndef __REACTOS__
+NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
+                                            PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength,
+                                            PVOID OutputBuffer, ULONG OutputBufferLength);
+
+NTSTATUS NTAPI NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
+                          ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key);
+#endif
+
+NTSTATUS WINAPI RtlUTF8ToUnicodeN(PWSTR UnicodeStringDestination, ULONG UnicodeStringMaxWCharCount,
+                                  PULONG UnicodeStringActualWCharCount, PCCH UTF8StringSource,
+                                  ULONG UTF8StringByteCount);
+
+#ifndef __REACTOS__
+NTSTATUS WINAPI NtSetEaFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length);
+
+NTSTATUS WINAPI NtSetSecurityObject(HANDLE Handle, SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor);
+
+NTSTATUS NTAPI NtQueryInformationFile(HANDLE hFile, PIO_STATUS_BLOCK io, PVOID ptr, ULONG len, FILE_INFORMATION_CLASS FileInformationClass);
+#ifdef __cplusplus
+}
+#endif
+#else
+BOOL
+WINAPI
+SetFileInformationByHandle(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize);
+BOOL
+WINAPI
+GetFileInformationByHandleEx(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize);
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+typedef struct _REPARSE_DATA_BUFFER {
+    ULONG ReparseTag;
+    USHORT ReparseDataLength;
+    USHORT Reserved;
+
+    union {
+        struct {
+            USHORT SubstituteNameOffset;
+            USHORT SubstituteNameLength;
+            USHORT PrintNameOffset;
+            USHORT PrintNameLength;
+            ULONG Flags;
+            WCHAR PathBuffer[1];
+        } SymbolicLinkReparseBuffer;
+
+        struct {
+            USHORT SubstituteNameOffset;
+            USHORT SubstituteNameLength;
+            USHORT PrintNameOffset;
+            USHORT PrintNameLength;
+            WCHAR PathBuffer[1];
+        } MountPointReparseBuffer;
+
+        struct {
+            UCHAR DataBuffer[1];
+        } GenericReparseBuffer;
+    };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+#define SYMLINK_FLAG_RELATIVE 1
+
+#ifndef FILE_SUPPORTS_BLOCK_REFCOUNTING
+
+typedef struct _DUPLICATE_EXTENTS_DATA {
+    HANDLE FileHandle;
+    LARGE_INTEGER SourceFileOffset;
+    LARGE_INTEGER TargetFileOffset;
+    LARGE_INTEGER ByteCount;
+} DUPLICATE_EXTENTS_DATA, *PDUPLICATE_EXTENTS_DATA;
+
+#define FSCTL_DUPLICATE_EXTENTS_TO_FILE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 209, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+
+typedef struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER {
+    WORD ChecksumAlgorithm;
+    WORD Reserved;
+    DWORD Flags;
+    DWORD ChecksumChunkSizeInBytes;
+    DWORD ClusterSizeInBytes;
+} FSCTL_GET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER;
+
+typedef struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER {
+    WORD ChecksumAlgorithm;
+    WORD Reserved;
+    DWORD Flags;
+} FSCTL_SET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER;
+
+#define FSCTL_GET_INTEGRITY_INFORMATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 159, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_SET_INTEGRITY_INFORMATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 160, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+
+#endif
+
+extern HMODULE module;
+void ShowError(HWND hwnd, ULONG err);
+void ShowNtStatusError(HWND hwnd, NTSTATUS Status);
+void ShowStringError(HWND hwndDlg, int num, ...);
+void format_size(UINT64 size, WCHAR* s, ULONG len, BOOL show_bytes);
+void set_dpi_aware();
+std::wstring format_message(ULONG last_error);
+std::wstring format_ntstatus(NTSTATUS Status);

Modification de propriétés sur dll/shellext/shellbtrfs/shellext.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: dll/shellext/shellbtrfs/volpropsheet.cpp
===================================================================
--- dll/shellext/shellbtrfs/volpropsheet.cpp	(nonexistent)
+++ dll/shellext/shellbtrfs/volpropsheet.cpp	(copie de travail)
@@ -0,0 +1,1618 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ *
+ * 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/>. */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#include "shellext.h"
+#ifndef __REACTOS__
+#include <windows.h>
+#include <strsafe.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <strsafe.h>
+#include <ndk/iofuncs.h>
+#include <ndk/iotypes.h>
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include <shlwapi.h>
+#include <uxtheme.h>
+
+#include "volpropsheet.h"
+#include "resource.h"
+
+HRESULT __stdcall BtrfsVolPropSheet::QueryInterface(REFIID riid, void **ppObj) {
+    if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
+        *ppObj = static_cast<IShellPropSheetExt*>(this);
+        AddRef();
+        return S_OK;
+    } else if (riid == IID_IShellExtInit) {
+        *ppObj = static_cast<IShellExtInit*>(this);
+        AddRef();
+        return S_OK;
+    }
+
+    *ppObj = NULL;
+    return E_NOINTERFACE;
+}
+
+HRESULT __stdcall BtrfsVolPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
+    HANDLE h;
+    ULONG num_files;
+    FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+    HDROP hdrop;
+
+    if (pidlFolder)
+        return E_FAIL;
+
+    if (!pdtobj)
+        return E_FAIL;
+
+    stgm.tymed = TYMED_HGLOBAL;
+
+    if (FAILED(pdtobj->GetData(&format, &stgm)))
+        return E_INVALIDARG;
+
+    stgm_set = TRUE;
+
+    hdrop = (HDROP)GlobalLock(stgm.hGlobal);
+
+    if (!hdrop) {
+        ReleaseStgMedium(&stgm);
+        stgm_set = FALSE;
+        return E_INVALIDARG;
+    }
+
+    num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+
+    if (num_files > 1) {
+        GlobalUnlock(hdrop);
+        return E_FAIL;
+    }
+
+    if (DragQueryFileW((HDROP)stgm.hGlobal, 0, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+        h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+        if (h != INVALID_HANDLE_VALUE) {
+            NTSTATUS Status;
+            IO_STATUS_BLOCK iosb;
+            ULONG devsize, i;
+
+            i = 0;
+            devsize = 1024;
+
+            devices = (btrfs_device*)malloc(devsize);
+
+            while (TRUE) {
+                Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_DEVICES, NULL, 0, devices, devsize);
+                if (Status == STATUS_BUFFER_OVERFLOW) {
+                    if (i < 8) {
+                        devsize += 1024;
+
+                        free(devices);
+                        devices = (btrfs_device*)malloc(devsize);
+
+                        i++;
+                    } else {
+                        CloseHandle(h);
+                        GlobalUnlock(hdrop);
+                        return E_FAIL;
+                    }
+                } else
+                    break;
+            }
+
+            if (!NT_SUCCESS(Status)) {
+                CloseHandle(h);
+                GlobalUnlock(hdrop);
+                return E_FAIL;
+            }
+
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_UUID, NULL, 0, &uuid, sizeof(BTRFS_UUID));
+            uuid_set = NT_SUCCESS(Status);
+
+            ignore = FALSE;
+            balance = new BtrfsBalance(fn);
+
+            CloseHandle(h);
+        } else {
+            GlobalUnlock(hdrop);
+            return E_FAIL;
+        }
+    } else {
+        GlobalUnlock(hdrop);
+        return E_FAIL;
+    }
+
+    GlobalUnlock(hdrop);
+
+    return S_OK;
+}
+
+typedef struct {
+    UINT64 dev_id;
+    WCHAR* name;
+    UINT64 alloc;
+    UINT64 size;
+} dev;
+
+void BtrfsVolPropSheet::FormatUsage(HWND hwndDlg, WCHAR* s, ULONG size, btrfs_usage* usage) {
+    UINT8 i, j;
+    UINT64 num_devs, k, dev_size, dev_alloc, data_size, data_alloc, metadata_size, metadata_alloc;
+    btrfs_device* bd;
+    dev* devs = NULL;
+    btrfs_usage* bue;
+    WCHAR t[255], u[255], v[255];
+
+    static const UINT64 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 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 };
+
+    s[0] = 0;
+
+    num_devs = 0;
+    bd = devices;
+
+    while (TRUE) {
+        num_devs++;
+
+        if (bd->next_entry > 0)
+            bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+        else
+            break;
+    }
+
+    devs = (dev*)malloc(sizeof(dev) * num_devs);
+    memset(devs, 0, sizeof(dev) * num_devs);
+
+    bd = devices;
+    k = 0;
+
+    dev_size = 0;
+
+    while (TRUE) {
+        if (bd->missing) {
+            if (!LoadStringW(module, IDS_MISSING, u, sizeof(u) / sizeof(WCHAR))) {
+                ShowError(hwndDlg, GetLastError());
+                goto end;
+            }
+
+            devs[k].name = (WCHAR*)malloc((wcslen(u) + 1) * sizeof(WCHAR));
+            wcscpy(devs[k].name, u);
+        } else if (bd->device_number == 0xffffffff) {
+            devs[k].name = (WCHAR*)malloc(bd->namelen + sizeof(WCHAR));
+            memcpy(devs[k].name, bd->name, bd->namelen);
+            devs[k].name[bd->namelen / sizeof(WCHAR)] = 0;
+        } else if (bd->partition_number == 0) {
+            if (!LoadStringW(module, IDS_DISK_NUM, u, sizeof(u) / sizeof(WCHAR))) {
+                ShowError(hwndDlg, GetLastError());
+                goto end;
+            }
+
+            if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, bd->device_number) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                goto end;
+
+            devs[k].name = (WCHAR*)malloc((wcslen(t) + 1) * sizeof(WCHAR));
+            wcscpy(devs[k].name, t);
+        } else {
+            if (!LoadStringW(module, IDS_DISK_PART_NUM, u, sizeof(u) / sizeof(WCHAR))) {
+                ShowError(hwndDlg, GetLastError());
+                goto end;
+            }
+
+            if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, bd->device_number, bd->partition_number) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                goto end;
+
+            devs[k].name = (WCHAR*)malloc((wcslen(t) + 1) * sizeof(WCHAR));
+            wcscpy(devs[k].name, t);
+        }
+
+        devs[k].dev_id = bd->dev_id;
+        devs[k].alloc = 0;
+        devs[k].size = bd->size;
+
+        dev_size += bd->size;
+
+        k++;
+
+        if (bd->next_entry > 0)
+            bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+        else
+            break;
+    }
+
+    dev_alloc = 0;
+    data_size = data_alloc = 0;
+    metadata_size = metadata_alloc = 0;
+
+    bue = usage;
+    while (TRUE) {
+        for (k = 0; k < bue->num_devices; k++) {
+            dev_alloc += bue->devices[k].alloc;
+
+            if (bue->type & BLOCK_FLAG_DATA) {
+                data_alloc += bue->devices[k].alloc;
+            }
+
+            if (bue->type & BLOCK_FLAG_METADATA) {
+                metadata_alloc += bue->devices[k].alloc;
+            }
+        }
+
+        if (bue->type & BLOCK_FLAG_DATA) {
+            data_size += bue->size;
+        }
+
+        if (bue->type & BLOCK_FLAG_METADATA) {
+            metadata_size += bue->size;
+        }
+
+        if (bue->next_entry > 0)
+            bue = (btrfs_usage*)((UINT8*)bue + bue->next_entry);
+        else
+            break;
+    }
+
+    // device size
+
+    if (!LoadStringW(module, IDS_USAGE_DEV_SIZE, u, sizeof(u) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        goto end;
+    }
+
+    format_size(dev_size, v, sizeof(v) / sizeof(WCHAR), FALSE);
+
+    if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, v) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    // device allocated
+
+    if (!LoadStringW(module, IDS_USAGE_DEV_ALLOC, u, sizeof(u) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        goto end;
+    }
+
+    format_size(dev_alloc, v, sizeof(v) / sizeof(WCHAR), FALSE);
+
+    if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, v) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    // device unallocated
+
+    if (!LoadStringW(module, IDS_USAGE_DEV_UNALLOC, u, sizeof(u) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        goto end;
+    }
+
+    format_size(dev_size - dev_alloc, v, sizeof(v) / sizeof(WCHAR), FALSE);
+
+    if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, v) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    // data ratio
+
+    if (data_alloc > 0) {
+        if (!LoadStringW(module, IDS_USAGE_DATA_RATIO, u, sizeof(u) / sizeof(WCHAR))) {
+            ShowError(hwndDlg, GetLastError());
+            goto end;
+        }
+
+        if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, (float)data_alloc / (float)data_size) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+    }
+
+    // metadata ratio
+
+    if (!LoadStringW(module, IDS_USAGE_METADATA_RATIO, u, sizeof(u) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        goto end;
+    }
+
+    if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), u, (float)metadata_alloc / (float)metadata_size) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    for (i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
+        for (j = 0; j < sizeof(duptypes) / sizeof(duptypes[0]); j++) {
+            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])) {
+                    WCHAR typestring[255], dupstring[255], sizestring[255], usedstring[255];
+
+                    if (bue->type & BLOCK_FLAG_DATA && bue->type & BLOCK_FLAG_METADATA && (types[i] == BLOCK_FLAG_DATA || types[i] == BLOCK_FLAG_METADATA))
+                        break;
+
+                    if (!LoadStringW(module, typestrings[i], typestring, sizeof(typestring) / sizeof(WCHAR))) {
+                        ShowError(hwndDlg, GetLastError());
+                        goto end;
+                    }
+
+                    if (!LoadStringW(module, dupstrings[j], dupstring, sizeof(dupstring) / sizeof(WCHAR))) {
+                        ShowError(hwndDlg, GetLastError());
+                        goto end;
+                    }
+
+                    format_size(bue->size, sizestring, sizeof(sizestring) / sizeof(WCHAR), FALSE);
+                    format_size(bue->used, usedstring, sizeof(usedstring) / sizeof(WCHAR), FALSE);
+
+                    if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), typestring, dupstring, sizestring, usedstring) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+
+                    if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+
+                    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+
+                    for (k = 0; k < bue->num_devices; k++) {
+                        UINT64 l;
+                        BOOL found = FALSE;
+
+                        format_size(bue->devices[k].alloc, sizestring, sizeof(sizestring) / sizeof(WCHAR), FALSE);
+
+                        for (l = 0; l < num_devs; l++) {
+                            if (devs[l].dev_id == bue->devices[k].dev_id) {
+                                if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), L"%s\t%s", devs[l].name, sizestring) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                    goto end;
+
+                                if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                    goto end;
+
+                                if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                    goto end;
+
+                                devs[l].alloc += bue->devices[k].alloc;
+
+                                found = TRUE;
+                                break;
+                            }
+                        }
+
+                        if (!found) {
+                            if (!LoadStringW(module, IDS_UNKNOWN_DEVICE, typestring, sizeof(typestring) / sizeof(WCHAR))) {
+                                ShowError(hwndDlg, GetLastError());
+                                goto end;
+                            }
+
+                            if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), typestring, bue->devices[k].dev_id) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                goto end;
+
+                            if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                goto end;
+
+                            if (StringCchCatW(s, size, L"\t") == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                goto end;
+
+                            if (StringCchCatW(s, size, sizestring) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                goto end;
+
+                            if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                goto end;
+                        }
+                    }
+
+                    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+                        goto end;
+
+                    break;
+                }
+
+                if (bue->next_entry > 0)
+                    bue = (btrfs_usage*)((UINT8*)bue + bue->next_entry);
+                else
+                    break;
+            }
+        }
+    }
+
+    if (!LoadStringW(module, IDS_USAGE_UNALLOC, t, sizeof(t) / sizeof(WCHAR))) {
+        ShowError(hwndDlg, GetLastError());
+        goto end;
+    }
+
+    if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+        goto end;
+
+    for (k = 0; k < num_devs; k++) {
+        WCHAR sizestring[255];
+
+        format_size(devs[k].size - devs[k].alloc, sizestring, sizeof(sizestring) / sizeof(WCHAR), FALSE);
+
+        if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), L"%s\t%s", devs[k].name, sizestring) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        if (StringCchCatW(s, size, t) == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+
+        if (StringCchCatW(s, size, L"\r\n") == STRSAFE_E_INSUFFICIENT_BUFFER)
+            goto end;
+    }
+
+end:
+    if (devs) {
+        for (k = 0; k < num_devs; k++) {
+            if (devs[k].name) free(devs[k].name);
+        }
+
+        free(devs);
+    }
+}
+
+void BtrfsVolPropSheet::RefreshUsage(HWND hwndDlg) {
+    HANDLE h;
+    WCHAR s[4096];
+    btrfs_usage* usage;
+
+    h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+        ULONG devsize, usagesize, i;
+
+        i = 0;
+        devsize = 1024;
+
+        devices = (btrfs_device*)malloc(devsize);
+
+        while (TRUE) {
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_DEVICES, NULL, 0, devices, devsize);
+            if (Status == STATUS_BUFFER_OVERFLOW) {
+                if (i < 8) {
+                    devsize += 1024;
+
+                    free(devices);
+                    devices = (btrfs_device*)malloc(devsize);
+
+                    i++;
+                } else
+                    return;
+            } else
+                break;
+        }
+
+        if (!NT_SUCCESS(Status)) {
+            CloseHandle(h);
+            return;
+        }
+
+        i = 0;
+        usagesize = 1024;
+
+        usage = (btrfs_usage*)malloc(usagesize);
+
+        while (TRUE) {
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_USAGE, NULL, 0, usage, usagesize);
+            if (Status == STATUS_BUFFER_OVERFLOW) {
+                if (i < 8) {
+                    usagesize += 1024;
+
+                    free(usage);
+                    usage = (btrfs_usage*)malloc(usagesize);
+
+                    i++;
+                } else
+                    return;
+            } else
+                break;
+        }
+
+        if (!NT_SUCCESS(Status)) {
+            free(usage);
+            CloseHandle(h);
+            return;
+        }
+
+        ignore = FALSE;
+
+        CloseHandle(h);
+    } else
+        return;
+
+    FormatUsage(hwndDlg, s, sizeof(s) / sizeof(WCHAR), usage);
+
+    SetDlgItemTextW(hwndDlg, IDC_USAGE_BOX, s);
+
+    free(usage);
+}
+
+INT_PTR CALLBACK BtrfsVolPropSheet::UsageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            WCHAR s[4096];
+            int i;
+            ULONG usagesize;
+            NTSTATUS Status;
+            HANDLE h;
+            IO_STATUS_BLOCK iosb;
+
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+
+            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+            if (h != INVALID_HANDLE_VALUE) {
+                btrfs_usage* usage;
+
+                i = 0;
+                usagesize = 1024;
+
+                usage = (btrfs_usage*)malloc(usagesize);
+
+                while (TRUE) {
+                    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_USAGE, NULL, 0, usage, usagesize);
+                    if (Status == STATUS_BUFFER_OVERFLOW) {
+                        if (i < 8) {
+                            usagesize += 1024;
+
+                            free(usage);
+                            usage = (btrfs_usage*)malloc(usagesize);
+
+                            i++;
+                        } else
+                            break;
+                    } else
+                        break;
+                }
+
+                if (!NT_SUCCESS(Status)) {
+                    free(usage);
+                    CloseHandle(h);
+                    break;
+                }
+
+                CloseHandle(h);
+
+                FormatUsage(hwndDlg, s, sizeof(s) / sizeof(WCHAR), usage);
+
+                SetDlgItemTextW(hwndDlg, IDC_USAGE_BOX, s);
+
+                free(usage);
+            }
+
+            break;
+        }
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                        case IDCANCEL:
+                            EndDialog(hwndDlg, 0);
+                        return TRUE;
+
+                        case IDC_USAGE_REFRESH:
+                            RefreshUsage(hwndDlg);
+                        return TRUE;
+                    }
+                break;
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_UsageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsVolPropSheet* bvps;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bvps = (BtrfsVolPropSheet*)lParam;
+    } else {
+        bvps = (BtrfsVolPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (bvps)
+        return bvps->UsageDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsVolPropSheet::ShowUsage(HWND hwndDlg) {
+   DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_VOL_USAGE), hwndDlg, stub_UsageDlgProc, (LPARAM)this);
+}
+
+static void add_lv_column(HWND list, int string, int cx) {
+    LVCOLUMNW lvc;
+    WCHAR s[255];
+
+    if (!LoadStringW(module, string, s, sizeof(s) / sizeof(WCHAR))) {
+        ShowError(GetParent(list), GetLastError());
+        return;
+    }
+
+    lvc.mask = LVCF_TEXT|LVCF_WIDTH;
+    lvc.pszText = s;
+    lvc.cx = cx;
+    SendMessageW(list, LVM_INSERTCOLUMNW, 0, (LPARAM)&lvc);
+}
+
+static int CALLBACK lv_sort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) {
+    if (lParam1 < lParam2)
+        return -1;
+    else if (lParam1 > lParam2)
+        return 1;
+    else
+        return 0;
+}
+
+static UINT64 find_dev_alloc(UINT64 dev_id, btrfs_usage* usage) {
+    btrfs_usage* bue;
+    UINT64 alloc;
+
+    alloc = 0;
+
+    bue = usage;
+    while (TRUE) {
+        UINT64 k;
+
+        for (k = 0; k < bue->num_devices; k++) {
+            if (bue->devices[k].dev_id == dev_id)
+                alloc += bue->devices[k].alloc;
+        }
+
+        if (bue->next_entry > 0)
+            bue = (btrfs_usage*)((UINT8*)bue + bue->next_entry);
+        else
+            break;
+    }
+
+    return alloc;
+}
+
+void BtrfsVolPropSheet::RefreshDevList(HWND devlist) {
+    HANDLE h;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    ULONG usagesize, devsize;
+    btrfs_usage* usage;
+    btrfs_device* bd;
+    int i;
+    UINT64 num_rw_devices;
+
+    h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowError(GetParent(devlist), GetLastError());
+        return;
+    }
+
+    i = 0;
+    devsize = 1024;
+
+    if (devices)
+        free(devices);
+
+    devices = (btrfs_device*)malloc(devsize);
+
+    while (TRUE) {
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_DEVICES, NULL, 0, devices, devsize);
+        if (Status == STATUS_BUFFER_OVERFLOW) {
+            if (i < 8) {
+                devsize += 1024;
+
+                free(devices);
+                devices = (btrfs_device*)malloc(devsize);
+
+                i++;
+            } else
+                return;
+        } else
+            break;
+    }
+
+    if (!NT_SUCCESS(Status)) {
+        CloseHandle(h);
+        return;
+    }
+
+    bd = devices;
+
+    i = 0;
+    usagesize = 1024;
+
+    usage = (btrfs_usage*)malloc(usagesize);
+
+    while (TRUE) {
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_USAGE, NULL, 0, usage, usagesize);
+        if (Status == STATUS_BUFFER_OVERFLOW) {
+            if (i < 8) {
+                usagesize += 1024;
+
+                free(usage);
+                usage = (btrfs_usage*)malloc(usagesize);
+
+                i++;
+            } else {
+                free(usage);
+                CloseHandle(h);
+                return;
+            }
+        } else
+            break;
+    }
+
+    if (!NT_SUCCESS(Status)) {
+        free(usage);
+        CloseHandle(h);
+        return;
+    }
+
+    CloseHandle(h);
+
+    SendMessageW(devlist, LVM_DELETEALLITEMS, 0, 0);
+
+    num_rw_devices = 0;
+
+    i = 0;
+    while (TRUE) {
+        LVITEMW lvi;
+        WCHAR s[255], u[255];
+        UINT64 alloc;
+
+        // ID
+
+        RtlZeroMemory(&lvi, sizeof(LVITEMW));
+        lvi.mask = LVIF_TEXT | LVIF_PARAM;
+        lvi.iItem = SendMessageW(devlist, LVM_GETITEMCOUNT, 0, 0);
+        lvi.lParam = bd->dev_id;
+
+        StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llu", bd->dev_id);
+        lvi.pszText = s;
+
+        SendMessageW(devlist, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
+
+        // description
+
+        lvi.mask = LVIF_TEXT;
+        lvi.iSubItem = 1;
+
+        if (bd->missing) {
+            if (!LoadStringW(module, IDS_MISSING, u, sizeof(u) / sizeof(WCHAR))) {
+                ShowError(GetParent(devlist), GetLastError());
+                break;
+            }
+
+            wcscpy(s, u);
+        } else if (bd->device_number == 0xffffffff) {
+            memcpy(s, bd->name, bd->namelen);
+            s[bd->namelen / sizeof(WCHAR)] = 0;
+        } else if (bd->partition_number == 0) {
+            if (!LoadStringW(module, IDS_DISK_NUM, u, sizeof(u) / sizeof(WCHAR))) {
+                ShowError(GetParent(devlist), GetLastError());
+                break;
+            }
+
+            if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), u, bd->device_number) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                break;
+        } else {
+            if (!LoadStringW(module, IDS_DISK_PART_NUM, u, sizeof(u) / sizeof(WCHAR))) {
+                ShowError(GetParent(devlist), GetLastError());
+                break;
+            }
+
+            if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), u, bd->device_number, bd->partition_number) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                break;
+        }
+
+        lvi.pszText = s;
+
+        SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi);
+
+        // readonly
+
+        lvi.iSubItem = 2;
+        LoadStringW(module, bd->readonly ? IDS_DEVLIST_READONLY_YES : IDS_DEVLIST_READONLY_NO, s, sizeof(s) / sizeof(WCHAR));
+        lvi.pszText = s;
+        SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi);
+
+        if (!bd->readonly)
+            num_rw_devices++;
+
+        // size
+
+        lvi.iSubItem = 3;
+        format_size(bd->size, s, sizeof(s) / sizeof(WCHAR), FALSE);
+        lvi.pszText = s;
+        SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi);
+
+        // alloc
+
+        alloc = find_dev_alloc(bd->dev_id, usage);
+
+        lvi.iSubItem = 4;
+        format_size(alloc, s, sizeof(s) / sizeof(WCHAR), FALSE);
+        lvi.pszText = s;
+        SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi);
+
+        // alloc %
+
+        StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%1.1f%%", (float)alloc * 100.0f / (float)bd->size);
+        lvi.iSubItem = 5;
+        lvi.pszText = s;
+        SendMessageW(devlist, LVM_SETITEMW, 0, (LPARAM)&lvi);
+
+        i++;
+
+        if (bd->next_entry > 0)
+            bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+        else
+            break;
+    }
+
+    free(usage);
+
+    SendMessageW(devlist, LVM_SORTITEMS, 0, (LPARAM)lv_sort);
+
+    EnableWindow(GetDlgItem(GetParent(devlist), IDC_DEVICE_ADD), num_rw_devices > 0);
+    EnableWindow(GetDlgItem(GetParent(devlist), IDC_DEVICE_REMOVE), num_rw_devices > 1);
+}
+
+void BtrfsVolPropSheet::ResetStats(HWND hwndDlg) {
+    HANDLE h;
+    WCHAR t[MAX_PATH + 100], sel[10];
+    SHELLEXECUTEINFOW sei;
+
+    _itow(stats_dev, sel, 10);
+
+    t[0] = '"';
+    GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+    wcscat(t, L"\",ResetStats ");
+    wcscat(t, fn);
+    wcscat(t, L"|");
+    wcscat(t, sel);
+
+    RtlZeroMemory(&sei, sizeof(sei));
+
+    sei.cbSize = sizeof(sei);
+    sei.hwnd = hwndDlg;
+    sei.lpVerb = L"runas";
+    sei.lpFile = L"rundll32.exe";
+    sei.lpParameters = t;
+    sei.nShow = SW_SHOW;
+    sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+    if (!ShellExecuteExW(&sei)) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    WaitForSingleObject(sei.hProcess, INFINITE);
+    CloseHandle(sei.hProcess);
+
+    h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        NTSTATUS Status;
+        IO_STATUS_BLOCK iosb;
+        ULONG devsize, i;
+
+        i = 0;
+        devsize = 1024;
+
+        free(devices);
+        devices = (btrfs_device*)malloc(devsize);
+
+        while (TRUE) {
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_DEVICES, NULL, 0, devices, devsize);
+            if (Status == STATUS_BUFFER_OVERFLOW) {
+                if (i < 8) {
+                    devsize += 1024;
+
+                    free(devices);
+                    devices = (btrfs_device*)malloc(devsize);
+
+                    i++;
+                } else
+                    break;
+            } else
+                break;
+        }
+
+        CloseHandle(h);
+    }
+
+    EndDialog(hwndDlg, 0);
+}
+
+INT_PTR CALLBACK BtrfsVolPropSheet::StatsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            WCHAR s[255], t[255];
+            btrfs_device *bd, *dev = NULL;
+            int i;
+
+            static int stat_ids[] = { IDC_WRITE_ERRS, IDC_READ_ERRS, IDC_FLUSH_ERRS, IDC_CORRUPTION_ERRS, IDC_GENERATION_ERRS };
+
+            bd = devices;
+
+            while (TRUE) {
+                if (bd->dev_id == stats_dev) {
+                    dev = bd;
+                    break;
+                }
+
+                if (bd->next_entry > 0)
+                    bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+                else
+                    break;
+            }
+
+            if (!dev) {
+                EndDialog(hwndDlg, 0);
+                ShowStringError(hwndDlg, IDS_CANNOT_FIND_DEVICE);
+                return FALSE;
+            }
+
+            GetDlgItemTextW(hwndDlg, IDC_DEVICE_ID, s, sizeof(s) / sizeof(WCHAR));
+
+            if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, dev->dev_id) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                return FALSE;
+
+            SetDlgItemTextW(hwndDlg, IDC_DEVICE_ID, t);
+
+            for (i = 0; i < 5; i++) {
+                GetDlgItemTextW(hwndDlg, stat_ids[i], s, sizeof(s) / sizeof(WCHAR));
+
+                if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, dev->stats[i]) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return FALSE;
+
+                SetDlgItemTextW(hwndDlg, stat_ids[i], t);
+            }
+
+            SendMessageW(GetDlgItem(hwndDlg, IDC_RESET_STATS), BCM_SETSHIELD, 0, TRUE);
+            EnableWindow(GetDlgItem(hwndDlg, IDC_RESET_STATS), !readonly);
+
+            break;
+        }
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                        case IDCANCEL:
+                            EndDialog(hwndDlg, 0);
+                        return TRUE;
+
+                        case IDC_RESET_STATS:
+                            ResetStats(hwndDlg);
+                        return TRUE;
+                    }
+                break;
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_StatsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsVolPropSheet* bvps;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bvps = (BtrfsVolPropSheet*)lParam;
+    } else {
+        bvps = (BtrfsVolPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (bvps)
+        return bvps->StatsDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsVolPropSheet::ShowStats(HWND hwndDlg, UINT64 devid) {
+    stats_dev = devid;
+
+    DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DEVICE_STATS), hwndDlg, stub_StatsDlgProc, (LPARAM)this);
+}
+
+INT_PTR CALLBACK BtrfsVolPropSheet::DeviceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            HWND devlist;
+            RECT rect;
+            ULONG w;
+
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+
+            devlist = GetDlgItem(hwndDlg, IDC_DEVLIST);
+
+            GetClientRect(devlist, &rect);
+            w = rect.right - rect.left;
+
+            add_lv_column(devlist, IDS_DEVLIST_ALLOC_PC, w * 5 / 44);
+            add_lv_column(devlist, IDS_DEVLIST_ALLOC, w * 6 / 44);
+            add_lv_column(devlist, IDS_DEVLIST_SIZE, w * 6 / 44);
+            add_lv_column(devlist, IDS_DEVLIST_READONLY, w * 7 / 44);
+            add_lv_column(devlist, IDS_DEVLIST_DESC, w * 16 / 44);
+            add_lv_column(devlist, IDS_DEVLIST_ID, w * 4 / 44);
+
+            SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_ADD), BCM_SETSHIELD, 0, TRUE);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_REMOVE), BCM_SETSHIELD, 0, TRUE);
+            SendMessageW(GetDlgItem(hwndDlg, IDC_DEVICE_RESIZE), BCM_SETSHIELD, 0, TRUE);
+
+            RefreshDevList(devlist);
+
+            break;
+        }
+
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case BN_CLICKED:
+                    switch (LOWORD(wParam)) {
+                        case IDOK:
+                        case IDCANCEL:
+                            KillTimer(hwndDlg, 1);
+                            EndDialog(hwndDlg, 0);
+                        return TRUE;
+
+                        case IDC_DEVICE_ADD:
+                        {
+                            WCHAR t[MAX_PATH + 100];
+                            SHELLEXECUTEINFOW sei;
+
+                            t[0] = '"';
+                            GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+                            wcscat(t, L"\",AddDevice ");
+                            wcscat(t, fn);
+
+                            RtlZeroMemory(&sei, sizeof(sei));
+
+                            sei.cbSize = sizeof(sei);
+                            sei.hwnd = hwndDlg;
+                            sei.lpVerb = L"runas";
+                            sei.lpFile = L"rundll32.exe";
+                            sei.lpParameters = t;
+                            sei.nShow = SW_SHOW;
+                            sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+                            if (!ShellExecuteExW(&sei)) {
+                                ShowError(hwndDlg, GetLastError());
+                                return TRUE;
+                            }
+
+                            WaitForSingleObject(sei.hProcess, INFINITE);
+                            CloseHandle(sei.hProcess);
+
+                            RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST));
+
+                            return TRUE;
+                        }
+
+                        case IDC_DEVICE_REFRESH:
+                            RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST));
+                            return TRUE;
+
+                        case IDC_DEVICE_SHOW_STATS:
+                        {
+                            WCHAR sel[MAX_PATH];
+                            HWND devlist;
+                            int index;
+                            LVITEMW lvi;
+
+                            devlist = GetDlgItem(hwndDlg, IDC_DEVLIST);
+
+                            index = SendMessageW(devlist, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
+
+                            if (index == -1)
+                                return TRUE;
+
+                            RtlZeroMemory(&lvi, sizeof(LVITEMW));
+                            lvi.mask = LVIF_TEXT;
+                            lvi.iItem = index;
+                            lvi.iSubItem = 0;
+                            lvi.pszText = sel;
+                            lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR);
+                            SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi);
+
+                            ShowStats(hwndDlg, _wtoi(sel));
+                            return TRUE;
+                        }
+
+                        case IDC_DEVICE_REMOVE:
+                        {
+                            WCHAR t[2*MAX_PATH + 100], sel[MAX_PATH], sel2[MAX_PATH], mess[255], mess2[255], title[255];
+                            HWND devlist;
+                            SHELLEXECUTEINFOW sei;
+                            int index;
+                            LVITEMW lvi;
+
+                            devlist = GetDlgItem(hwndDlg, IDC_DEVLIST);
+
+                            index = SendMessageW(devlist, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
+
+                            if (index == -1)
+                                return TRUE;
+
+                            RtlZeroMemory(&lvi, sizeof(LVITEMW));
+                            lvi.mask = LVIF_TEXT;
+                            lvi.iItem = index;
+                            lvi.iSubItem = 0;
+                            lvi.pszText = sel;
+                            lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR);
+                            SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi);
+
+                            lvi.iSubItem = 1;
+                            lvi.pszText = sel2;
+                            lvi.cchTextMax = sizeof(sel2) / sizeof(WCHAR);
+                            SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi);
+
+                            if (!LoadStringW(module, IDS_REMOVE_DEVICE_CONFIRMATION, mess, sizeof(mess) / sizeof(WCHAR))) {
+                                ShowError(hwndDlg, GetLastError());
+                                return TRUE;
+                            }
+
+                            if (StringCchPrintfW(mess2, sizeof(mess2) / sizeof(WCHAR), mess, sel, sel2) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                                return TRUE;
+
+                            if (!LoadStringW(module, IDS_CONFIRMATION_TITLE, title, sizeof(title) / sizeof(WCHAR))) {
+                                ShowError(hwndDlg, GetLastError());
+                                return TRUE;
+                            }
+
+                            if (MessageBoxW(hwndDlg, mess2, title, MB_YESNO) != IDYES)
+                                return TRUE;
+
+                            t[0] = '"';
+                            GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+                            wcscat(t, L"\",RemoveDevice ");
+                            wcscat(t, fn);
+                            wcscat(t, L"|");
+                            wcscat(t, sel);
+
+                            RtlZeroMemory(&sei, sizeof(sei));
+
+                            sei.cbSize = sizeof(sei);
+                            sei.hwnd = hwndDlg;
+                            sei.lpVerb = L"runas";
+                            sei.lpFile = L"rundll32.exe";
+                            sei.lpParameters = t;
+                            sei.nShow = SW_SHOW;
+                            sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+                            if (!ShellExecuteExW(&sei)) {
+                                ShowError(hwndDlg, GetLastError());
+                                return TRUE;
+                            }
+
+                            WaitForSingleObject(sei.hProcess, INFINITE);
+                            CloseHandle(sei.hProcess);
+
+                            RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST));
+
+                            return TRUE;
+                        }
+
+                        case IDC_DEVICE_RESIZE:
+                        {
+                            HWND devlist;
+                            int index;
+                            LVITEMW lvi;
+                            WCHAR sel[100], t[2*MAX_PATH + 100];
+                            SHELLEXECUTEINFOW sei;
+
+                            devlist = GetDlgItem(hwndDlg, IDC_DEVLIST);
+
+                            index = SendMessageW(devlist, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
+
+                            if (index == -1)
+                                return TRUE;
+
+                            RtlZeroMemory(&lvi, sizeof(LVITEMW));
+                            lvi.mask = LVIF_TEXT;
+                            lvi.iItem = index;
+                            lvi.iSubItem = 0;
+                            lvi.pszText = sel;
+                            lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR);
+                            SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi);
+
+                            t[0] = '"';
+                            GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+                            wcscat(t, L"\",ResizeDevice ");
+                            wcscat(t, fn);
+                            wcscat(t, L"|");
+                            wcscat(t, sel);
+
+                            RtlZeroMemory(&sei, sizeof(sei));
+
+                            sei.cbSize = sizeof(sei);
+                            sei.hwnd = hwndDlg;
+                            sei.lpVerb = L"runas";
+                            sei.lpFile = L"rundll32.exe";
+                            sei.lpParameters = t;
+                            sei.nShow = SW_SHOW;
+                            sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+                            if (!ShellExecuteExW(&sei)) {
+                                ShowError(hwndDlg, GetLastError());
+                                return TRUE;
+                            }
+
+                            WaitForSingleObject(sei.hProcess, INFINITE);
+                            CloseHandle(sei.hProcess);
+
+                            RefreshDevList(GetDlgItem(hwndDlg, IDC_DEVLIST));
+                        }
+                    }
+                break;
+            }
+        break;
+
+        case WM_NOTIFY:
+            switch (((LPNMHDR)lParam)->code) {
+                case LVN_ITEMCHANGED:
+                {
+                    NMLISTVIEW* nmv = (NMLISTVIEW*)lParam;
+
+                    EnableWindow(GetDlgItem(hwndDlg, IDC_DEVICE_SHOW_STATS), nmv->uNewState & LVIS_SELECTED);
+
+                    if (nmv->uNewState & LVIS_SELECTED && !readonly) {
+                        HWND devlist;
+                        btrfs_device* bd;
+                        BOOL device_readonly = FALSE;
+                        LVITEMW lvi;
+                        WCHAR sel[MAX_PATH];
+                        UINT64 devid;
+
+                        devlist = GetDlgItem(hwndDlg, IDC_DEVLIST);
+
+                        RtlZeroMemory(&lvi, sizeof(LVITEMW));
+                        lvi.mask = LVIF_TEXT;
+                        lvi.iItem = nmv->iItem;
+                        lvi.iSubItem = 0;
+                        lvi.pszText = sel;
+                        lvi.cchTextMax = sizeof(sel) / sizeof(WCHAR);
+                        SendMessageW(devlist, LVM_GETITEMW, 0, (LPARAM)&lvi);
+                        devid = _wtoi(sel);
+
+                        bd = devices;
+
+                        while (TRUE) {
+                            if (bd->dev_id == devid) {
+                                device_readonly = bd->readonly;
+                                break;
+                            }
+
+                            if (bd->next_entry > 0)
+                                bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+                            else
+                                break;
+                        }
+
+                        EnableWindow(GetDlgItem(hwndDlg, IDC_DEVICE_RESIZE), !device_readonly);
+                    } else
+                        EnableWindow(GetDlgItem(hwndDlg, IDC_DEVICE_RESIZE), FALSE);
+
+                    break;
+                }
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK stub_DeviceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    BtrfsVolPropSheet* bvps;
+
+    if (uMsg == WM_INITDIALOG) {
+        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+        bvps = (BtrfsVolPropSheet*)lParam;
+    } else {
+        bvps = (BtrfsVolPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+    }
+
+    if (bvps)
+        return bvps->DeviceDlgProc(hwndDlg, uMsg, wParam, lParam);
+    else
+        return FALSE;
+}
+
+void BtrfsVolPropSheet::ShowDevices(HWND hwndDlg) {
+   DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_DEVICES), hwndDlg, stub_DeviceDlgProc, (LPARAM)this);
+}
+
+void BtrfsVolPropSheet::ShowScrub(HWND hwndDlg) {
+    WCHAR t[MAX_PATH + 100];
+    SHELLEXECUTEINFOW sei;
+
+    t[0] = '"';
+    GetModuleFileNameW(module, t + 1, (sizeof(t) / sizeof(WCHAR)) - 1);
+    wcscat(t, L"\",ShowScrub ");
+    wcscat(t, fn);
+
+    RtlZeroMemory(&sei, sizeof(sei));
+
+    sei.cbSize = sizeof(sei);
+    sei.hwnd = hwndDlg;
+    sei.lpVerb = L"runas";
+    sei.lpFile = L"rundll32.exe";
+    sei.lpParameters = t;
+    sei.nShow = SW_SHOW;
+    sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+
+    if (!ShellExecuteExW(&sei)) {
+        ShowError(hwndDlg, GetLastError());
+        return;
+    }
+
+    WaitForSingleObject(sei.hProcess, INFINITE);
+    CloseHandle(sei.hProcess);
+}
+
+static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
+            BtrfsVolPropSheet* bps = (BtrfsVolPropSheet*)psp->lParam;
+            btrfs_device* bd;
+
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+
+            SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
+
+            bps->readonly = TRUE;
+            bd = bps->devices;
+
+            while (TRUE) {
+                if (!bd->readonly) {
+                    bps->readonly = FALSE;
+                    break;
+                }
+
+                if (bd->next_entry > 0)
+                    bd = (btrfs_device*)((UINT8*)bd + bd->next_entry);
+                else
+                    break;
+            }
+
+            if (bps->uuid_set) {
+                WCHAR s[255], t[255];
+
+                GetDlgItemTextW(hwndDlg, IDC_UUID, s, sizeof(s) / sizeof(WCHAR));
+
+                if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s,
+                    bps->uuid.uuid[0], bps->uuid.uuid[1], bps->uuid.uuid[2], bps->uuid.uuid[3], bps->uuid.uuid[4], bps->uuid.uuid[5], bps->uuid.uuid[6], bps->uuid.uuid[7],
+                    bps->uuid.uuid[8], bps->uuid.uuid[9], bps->uuid.uuid[10], bps->uuid.uuid[11], bps->uuid.uuid[12], bps->uuid.uuid[13], bps->uuid.uuid[14], bps->uuid.uuid[15]
+                    ) != STRSAFE_E_INSUFFICIENT_BUFFER)
+                    SetDlgItemTextW(hwndDlg, IDC_UUID, t);
+                else
+                    SetDlgItemTextW(hwndDlg, IDC_UUID, L"");
+            } else
+                SetDlgItemTextW(hwndDlg, IDC_UUID, L"");
+
+            SendMessageW(GetDlgItem(hwndDlg, IDC_VOL_SCRUB), BCM_SETSHIELD, 0, TRUE);
+
+            return FALSE;
+        }
+
+        case WM_NOTIFY:
+        {
+            switch (((LPNMHDR)lParam)->code) {
+                case PSN_KILLACTIVE:
+                    SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, FALSE);
+                break;
+            }
+            break;
+        }
+
+        case WM_COMMAND:
+        {
+            BtrfsVolPropSheet* bps = (BtrfsVolPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+            if (bps) {
+                switch (HIWORD(wParam)) {
+                    case BN_CLICKED: {
+                        switch (LOWORD(wParam)) {
+                            case IDC_VOL_SHOW_USAGE:
+                                bps->ShowUsage(hwndDlg);
+                            break;
+
+                            case IDC_VOL_BALANCE:
+                                bps->balance->ShowBalance(hwndDlg);
+                            break;
+
+                            case IDC_VOL_DEVICES:
+                                bps->ShowDevices(hwndDlg);
+                            break;
+
+                            case IDC_VOL_SCRUB:
+                                bps->ShowScrub(hwndDlg);
+                            break;
+                        }
+                    }
+                }
+            }
+
+            break;
+        }
+    }
+
+    return FALSE;
+}
+
+HRESULT __stdcall BtrfsVolPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) {
+    PROPSHEETPAGE psp;
+    HPROPSHEETPAGE hPage;
+    INITCOMMONCONTROLSEX icex;
+
+    if (ignore)
+        return S_OK;
+
+    icex.dwSize = sizeof(icex);
+    icex.dwICC = ICC_LINK_CLASS;
+
+    if (!InitCommonControlsEx(&icex))
+        MessageBoxW(NULL, L"InitCommonControlsEx failed", L"Error", MB_ICONERROR);
+
+    psp.dwSize = sizeof(psp);
+    psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
+    psp.hInstance = module;
+    psp.pszTemplate = MAKEINTRESOURCE(IDD_VOL_PROP_SHEET);
+    psp.hIcon = 0;
+    psp.pszTitle = MAKEINTRESOURCE(IDS_VOL_PROP_SHEET_TITLE);
+    psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
+    psp.pcRefParent = (UINT*)&objs_loaded;
+    psp.pfnCallback = NULL;
+    psp.lParam = (LPARAM)this;
+
+    hPage = CreatePropertySheetPage(&psp);
+
+    if (hPage) {
+        if (pfnAddPage(hPage, lParam)) {
+            this->AddRef();
+            return S_OK;
+        } else
+            DestroyPropertySheetPage(hPage);
+    } else
+        return E_OUTOFMEMORY;
+
+    return E_FAIL;
+}
+
+HRESULT __stdcall BtrfsVolPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) {
+    return S_OK;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void CALLBACK ResetStatsW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
+    HANDLE token, h;
+    NTSTATUS Status;
+    TOKEN_PRIVILEGES tp;
+    LUID luid;
+    UINT64 devid;
+    WCHAR *s, *vol, *dev;
+    IO_STATUS_BLOCK iosb;
+
+    set_dpi_aware();
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
+        ShowError(hwnd, GetLastError());
+        return;
+    }
+
+    if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Luid = luid;
+    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    s = wcsstr(lpszCmdLine, L"|");
+    if (!s)
+        goto end;
+
+    s[0] = 0;
+
+    vol = lpszCmdLine;
+    dev = &s[1];
+
+    devid = _wtoi(dev);
+    if (devid == 0)
+        goto end;
+
+    h = CreateFileW(vol, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+    if (h == INVALID_HANDLE_VALUE) {
+        ShowError(hwnd, GetLastError());
+        goto end;
+    }
+
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESET_STATS, &devid, sizeof(UINT64), NULL, 0);
+    if (!NT_SUCCESS(Status)) {
+        ShowNtStatusError(hwnd, Status);
+
+        CloseHandle(h);
+        goto end;
+    }
+
+    CloseHandle(h);
+
+end:
+    CloseHandle(token);
+}
+
+#ifdef __cplusplus
+}
+#endif

Modification de propriétés sur dll/shellext/shellbtrfs/volpropsheet.cpp
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: dll/shellext/shellbtrfs/volpropsheet.h
===================================================================
--- dll/shellext/shellbtrfs/volpropsheet.h	(nonexistent)
+++ dll/shellext/shellbtrfs/volpropsheet.h	(copie de travail)
@@ -0,0 +1,110 @@
+/* Copyright (c) Mark Harmstone 2016-17
+ *
+ * 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 <shlobj.h>
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#include "../btrfs.h"
+#else
+#include "btrfsioctl.h"
+#include "btrfs.h"
+#endif
+#include "balance.h"
+#include "scrub.h"
+
+extern LONG objs_loaded;
+
+class BtrfsVolPropSheet : public IShellExtInit, IShellPropSheetExt {
+public:
+    BtrfsVolPropSheet() {
+        refcount = 0;
+        ignore = TRUE;
+        stgm_set = FALSE;
+        devices = NULL;
+
+        InterlockedIncrement(&objs_loaded);
+
+        balance = NULL;
+    }
+
+    virtual ~BtrfsVolPropSheet() {
+        if (stgm_set) {
+            GlobalUnlock(stgm.hGlobal);
+            ReleaseStgMedium(&stgm);
+        }
+
+        if (devices)
+            free(devices);
+
+        InterlockedDecrement(&objs_loaded);
+
+        if (balance)
+            delete balance;
+    }
+
+    // IUnknown
+
+    HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
+
+    ULONG __stdcall AddRef() {
+        return InterlockedIncrement(&refcount);
+    }
+
+    ULONG __stdcall Release() {
+        LONG rc = InterlockedDecrement(&refcount);
+
+        if (rc == 0)
+            delete this;
+
+        return rc;
+    }
+
+    // IShellExtInit
+
+    virtual HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID);
+
+    // IShellPropSheetExt
+
+    virtual HRESULT __stdcall AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
+    virtual HRESULT __stdcall ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam);
+
+    void FormatUsage(HWND hwndDlg, WCHAR* s, ULONG size, btrfs_usage* usage);
+    void RefreshUsage(HWND hwndDlg);
+    void ShowUsage(HWND hwndDlg);
+    INT_PTR CALLBACK UsageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+    void RefreshDevList(HWND devlist);
+    INT_PTR CALLBACK DeviceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+    void ShowDevices(HWND hwndDlg);
+    void ShowScrub(HWND hwndDlg);
+    INT_PTR CALLBACK StatsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+    void ShowStats(HWND hwndDlg, UINT64 devid);
+    void ResetStats(HWND hwndDlg);
+
+    btrfs_device* devices;
+    BOOL readonly;
+    BtrfsBalance* balance;
+    BTRFS_UUID uuid;
+    BOOL uuid_set;
+
+private:
+    LONG refcount;
+    BOOL ignore;
+    STGMEDIUM stgm;
+    BOOL stgm_set;
+    WCHAR fn[MAX_PATH];
+    UINT64 stats_dev;
+};
