diff --git a/reactos/dll/shellext/shellbtrfs/CMakeLists.txt b/reactos/dll/shellext/shellbtrfs/CMakeLists.txt
index 454179b71d6..fd11cc123c7 100644
--- a/reactos/dll/shellext/shellbtrfs/CMakeLists.txt
+++ b/reactos/dll/shellext/shellbtrfs/CMakeLists.txt
@@ -1,23 +1,33 @@
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})
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)
diff --git a/reactos/dll/shellext/shellbtrfs/balance.cpp b/reactos/dll/shellext/shellbtrfs/balance.cpp
new file mode 100644
index 00000000000..aa639a57d0c
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/balance.cpp
@@ -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 . */
+
+#include "shellext.h"
+#include "balance.h"
+#include "resource.h"
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "btrfsioctl.h"
+#endif
+#include
+#include
+#include
+#ifndef __REACTOS__
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include
+#include
+
+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);
+}
diff --git a/reactos/dll/shellext/shellbtrfs/balance.h b/reactos/dll/shellext/shellbtrfs/balance.h
new file mode 100644
index 00000000000..262f29923bc
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/balance.h
@@ -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 . */
+
+#include
+#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;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/contextmenu.cpp b/reactos/dll/shellext/shellbtrfs/contextmenu.cpp
index f04228b53aa..1e004ceea10 100644
--- a/reactos/dll/shellext/shellbtrfs/contextmenu.cpp
+++ b/reactos/dll/shellext/shellbtrfs/contextmenu.cpp
@@ -1,30 +1,42 @@
-/* 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 . */
+#ifndef __REACTOS__
+#define UNICODE
+#endif
+#include "shellext.h"
#ifndef __REACTOS__
#include
+#include
+#include
#include
#else
#define WIN32_NO_STATUS
#include
#include
#include
+#include
+#include
#include
+#undef DeleteFile
#endif
+#include
+#include
+#include
#define NO_SHLWAPI_STRFCNS
#include
@@ -34,45 +46,35 @@
#ifndef __REACTOS__
#include "../btrfsioctl.h"
#else
-#include "../../drivers/filesystems/btrfs/btrfsioctl.h"
+#include "btrfsioctl.h"
#endif
#define NEW_SUBVOL_VERBA "newsubvol"
#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
-
-#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;
+typedef struct {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+} reparse_header;
// 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(this);
+ *ppObj = static_cast(this);
AddRef();
return S_OK;
} else if (riid == IID_IShellExtInit) {
- *ppObj = static_cast(this);
+ *ppObj = static_cast(this);
AddRef();
return S_OK;
}
@@ -86,160 +88,339 @@ HRESULT __stdcall BtrfsContextMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDa
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;
- }
-
- MessageBoxW(hwnd, buf, L"Error", MB_ICONERROR);
-
- LocalFree(buf);
-}
+ if (!uacicon)
+ get_uac_icon();
-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;
+ 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++;
+ }
}
-
- 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,26 +428,26 @@ static void create_snapshot(HWND hwnd, WCHAR* fn) {
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;
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,37 +455,37 @@ static void create_snapshot(HWND hwnd, WCHAR* fn) {
CloseHandle(h);
return;
}
-
+
if (!LoadStringW(module, IDS_SNAPSHOT_FILENAME, templ, MAX_PATH)) {
ShowError(hwnd, GetLastError());
CloseHandle(h);
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);
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,157 +498,1297 @@ static void create_snapshot(HWND hwnd, WCHAR* fn) {
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;
- fff = FindFirstFileW(searchpath, &wfd);
- num++;
- } while (fff != INVALID_HANDLE_VALUE);
+ 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);
+ }
+ }
+
+ 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;
}
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);
+}
diff --git a/reactos/dll/shellext/shellbtrfs/contextmenu.h b/reactos/dll/shellext/shellbtrfs/contextmenu.h
index 0ea33abb422..d9d32ed91c7 100644
--- a/reactos/dll/shellext/shellbtrfs/contextmenu.h
+++ b/reactos/dll/shellext/shellbtrfs/contextmenu.h
@@ -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 . */
@@ -25,6 +25,8 @@ public:
refcount = 0;
ignore = TRUE;
stgm_set = FALSE;
+ uacicon = NULL;
+ allow_snapshot = FALSE;
InterlockedIncrement(&objs_loaded);
}
@@ -33,42 +35,49 @@ public:
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);
}
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);
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();
};
diff --git a/reactos/dll/shellext/shellbtrfs/devices.cpp b/reactos/dll/shellext/shellbtrfs/devices.cpp
new file mode 100755
index 00000000000..8feb747e10d
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/devices.cpp
@@ -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 . */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#include "shellext.h"
+#include "devices.h"
+#include "resource.h"
+#include "balance.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#ifndef __REACTOS__
+#include "../btrfs.h"
+#else
+#include
+#include
+#include
+#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_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
diff --git a/reactos/dll/shellext/shellbtrfs/devices.h b/reactos/dll/shellext/shellbtrfs/devices.h
new file mode 100644
index 00000000000..218a244ff35
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/devices.h
@@ -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 . */
+
+#ifndef __REACTOS__
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#include
+#endif
+#include
+#include
+#include
+#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_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;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/factory.cpp b/reactos/dll/shellext/shellbtrfs/factory.cpp
index dcde8334412..90083592caa 100644
--- a/reactos/dll/shellext/shellbtrfs/factory.cpp
+++ b/reactos/dll/shellext/shellbtrfs/factory.cpp
@@ -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 . */
+#include "shellext.h"
#include
#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(this);
} else {
@@ -52,31 +54,41 @@ HRESULT __stdcall Factory::CreateInstance(IUnknown* pUnknownOuter, const IID& ii
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;
}
diff --git a/reactos/dll/shellext/shellbtrfs/factory.h b/reactos/dll/shellext/shellbtrfs/factory.h
index 07790393a8c..af8b46bf8e9 100644
--- a/reactos/dll/shellext/shellbtrfs/factory.h
+++ b/reactos/dll/shellext/shellbtrfs/factory.h
@@ -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 . */
@@ -21,7 +21,8 @@ typedef enum {
FactoryUnknown,
FactoryIconHandler,
FactoryContextMenu,
- FactoryPropSheet
+ FactoryPropSheet,
+ FactoryVolPropSheet
} factory_type;
class Factory : public IClassFactory {
@@ -37,27 +38,27 @@ public:
}
// 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;
}
// IClassFactory
-
+
virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv);
virtual HRESULT __stdcall LockServer(BOOL bLock);
-
+
factory_type type;
private:
diff --git a/reactos/dll/shellext/shellbtrfs/iconoverlay.cpp b/reactos/dll/shellext/shellbtrfs/iconoverlay.cpp
index 1102c43cd64..cfc312c78eb 100644
--- a/reactos/dll/shellext/shellbtrfs/iconoverlay.cpp
+++ b/reactos/dll/shellext/shellbtrfs/iconoverlay.cpp
@@ -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 . */
+#include "shellext.h"
#ifndef __REACTOS__
#include
#include
@@ -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(this);
+ *ppObj = static_cast(this);
AddRef();
return S_OK;
}
@@ -57,31 +45,31 @@ HRESULT __stdcall BtrfsIconOverlay::QueryInterface(REFIID riid, void **ppObj) {
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;
}
HRESULT __stdcall BtrfsIconOverlay::GetPriority(int *pPriority) {
if (!pPriority)
return E_INVALIDARG;
-
+
*pPriority = 0;
-
+
return S_OK;
}
@@ -90,20 +78,20 @@ HRESULT __stdcall BtrfsIconOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib)
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;
}
diff --git a/reactos/dll/shellext/shellbtrfs/iconoverlay.h b/reactos/dll/shellext/shellbtrfs/iconoverlay.h
index fa50abbb2f1..efcc1e7b79d 100644
--- a/reactos/dll/shellext/shellbtrfs/iconoverlay.h
+++ b/reactos/dll/shellext/shellbtrfs/iconoverlay.h
@@ -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 . */
@@ -31,24 +31,24 @@ public:
}
// 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;
}
// 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);
diff --git a/reactos/dll/shellext/shellbtrfs/main.cpp b/reactos/dll/shellext/shellbtrfs/main.cpp
index ddf04acedf5..a7ac6a2cc05 100644
--- a/reactos/dll/shellext/shellbtrfs/main.cpp
+++ b/reactos/dll/shellext/shellbtrfs/main.cpp
@@ -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 . */
-#ifndef __REACTOS__
-#define _WIN32_WINNT 0x0600
-#endif
-
+#include "shellext.h"
#include
#include
+#include
+#include
#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 @@ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
return E_OUTOFMEMORY;
else {
fact->type = FactoryIconHandler;
-
+
return fact->QueryInterface(riid, ppv);
}
} else if (rclsid == CLSID_ShellBtrfsContextMenu) {
@@ -59,7 +303,7 @@ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
return E_OUTOFMEMORY;
else {
fact->type = FactoryContextMenu;
-
+
return fact->QueryInterface(riid, ppv);
}
} else if (rclsid == CLSID_ShellBtrfsPropSheet) {
@@ -68,7 +312,16 @@ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
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);
}
}
@@ -80,34 +333,34 @@ static BOOL write_reg_key(HKEY root, const WCHAR* keyname, const WCHAR* val, DWO
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 @@ static BOOL register_clsid(const GUID clsid, const WCHAR* description) {
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 @@ static BOOL unregister_clsid(const GUID clsid) {
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 @@ static BOOL reg_icon_overlay(const GUID clsid, const WCHAR* name) {
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 @@ end:
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 @@ static BOOL reg_context_menu_handler(const GUID clsid, const WCHAR* filetype, co
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 @@ end:
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 @@ static BOOL reg_prop_sheet_handler(const GUID clsid, const WCHAR* filetype, cons
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,74 +526,86 @@ end:
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;
}
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;
}
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,10 +622,117 @@ STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine) {
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
}
diff --git a/reactos/dll/shellext/shellbtrfs/propsheet.cpp b/reactos/dll/shellext/shellbtrfs/propsheet.cpp
index 9406b84941a..1f96d92f091 100755
--- a/reactos/dll/shellext/shellbtrfs/propsheet.cpp
+++ b/reactos/dll/shellext/shellbtrfs/propsheet.cpp
@@ -1,23 +1,24 @@
-/* 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 . */
#define ISOLATION_AWARE_ENABLED 1
#define STRSAFE_NO_DEPRECATE
+#include "shellext.h"
#ifndef __REACTOS__
#include
#include
@@ -28,6 +29,7 @@
#include
#include
#include
+#include
#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" {
+#define SUBVOL_ROOT_INODE 0x100
+
+#ifndef __MINGW32__ // in winternl.h in mingw
+
+typedef struct _FILE_ACCESS_INFORMATION {
+ ACCESS_MASK AccessFlags;
+} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
+
+#define FileAccessInformation (FILE_INFORMATION_CLASS)8
+
#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 (NTSTATUS)0x00000000
-#endif
-
-#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
-
-extern HMODULE module;
-
-extern void ShowNtStatusError(HWND hwnd, NTSTATUS Status);
-static void format_size(UINT64 size, WCHAR* s, ULONG len);
-static void ShowError(HWND hwnd, ULONG err);
HRESULT __stdcall BtrfsPropSheet::QueryInterface(REFIID riid, void **ppObj) {
if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
- *ppObj = static_cast(this);
+ *ppObj = static_cast(this);
AddRef();
return S_OK;
} else if (riid == IID_IShellExtInit) {
- *ppObj = static_cast(this);
+ *ppObj = static_cast(this);
AddRef();
return S_OK;
}
@@ -81,13 +68,13 @@ HRESULT __stdcall BtrfsPropSheet::QueryInterface(REFIID riid, void **ppObj) {
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,290 +82,429 @@ void BtrfsPropSheet::do_search(WCHAR* fn) {
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];
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);
}
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;
- FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
- UINT num_files, i;
- WCHAR fn[MAX_PATH];
- 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);
-
- min_mode = 0;
- max_mode = 0;
- min_flags = 0;
- max_flags = 0;
- various_subvols = various_inodes = various_types = various_uids = various_gids = 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);
+ FILE_ACCESS_INFORMATION fai;
+ BY_HANDLE_FILE_INFORMATION bhfi;
+ btrfs_inode_info bii2;
- 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(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];
}
-
- 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;
- }
-
- 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());
+ 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 };
+ 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;
+ }
+
+ 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;
+ 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;
+
+ 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];
+ }
+ }
+
+ 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;
+ }
+
+ 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());
+ }
+
+ 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 @@ void BtrfsPropSheet::change_inode_flag(HWND hDlg, UINT64 flag, UINT state) {
} 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_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?
- HANDLE h;
- IO_STATUS_BLOCK iosb;
- NTSTATUS Status;
- btrfs_set_inode_info bsii;
-
+
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;
- }
-
- 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);
+ for (i = 0; i < num_files; i++) {
+ if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+ apply_changes_file(hDlg, fn);
}
}
}
-
+
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,38 +675,40 @@ void BtrfsPropSheet::change_perm_flag(HWND hDlg, ULONG flag, UINT state) {
} 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) {
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,39 +719,39 @@ static INT_PTR CALLBACK SizeDetailsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wPara
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);
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,270 +762,425 @@ static void set_check_box(HWND hwndDlg, ULONG id, UINT64 min, UINT64 max) {
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:
{
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) {
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 @@ static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,
}
}
}
-
+
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 @@ HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPAR
psp.lParam = (LPARAM)this;
hPage = CreatePropertySheetPage(&psp);
-
+
if (hPage) {
if (pfnAddPage(hPage, lParam)) {
this->AddRef();
@@ -1035,10 +1243,54 @@ HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPAR
DestroyPropertySheetPage(hPage);
} else
return E_OUTOFMEMORY;
-
+
return E_FAIL;
}
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
diff --git a/reactos/dll/shellext/shellbtrfs/propsheet.h b/reactos/dll/shellext/shellbtrfs/propsheet.h
index d4606928439..90d9fc08745 100644
--- a/reactos/dll/shellext/shellbtrfs/propsheet.h
+++ b/reactos/dll/shellext/shellbtrfs/propsheet.h
@@ -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 . */
#include
#include
+#include
#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 @@ public:
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,36 +119,37 @@ public:
GlobalUnlock(stgm.hGlobal);
ReleaseStgMedium(&stgm);
}
-
+
InterlockedDecrement(&objs_loaded);
}
// 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 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,20 +160,22 @@ public:
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;
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 @@ private:
BOOL flags_changed, perms_changed, uid_changed, gid_changed;
UINT64 sizes[4], totalsize;
std::deque 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();
};
diff --git a/reactos/dll/shellext/shellbtrfs/reactos.cpp b/reactos/dll/shellext/shellbtrfs/reactos.cpp
new file mode 100644
index 00000000000..8de84fbf112
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/reactos.cpp
@@ -0,0 +1,207 @@
+#include "shellext.h"
+#include
+#include
+#include
+
+#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
diff --git a/reactos/dll/shellext/shellbtrfs/recv.cpp b/reactos/dll/shellext/shellbtrfs/recv.cpp
new file mode 100644
index 00000000000..c3597fa412e
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/recv.cpp
@@ -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 . */
+
+#include "shellext.h"
+#include
+#include
+#include
+#include
+#include
+#include "recv.h"
+#include "resource.h"
+
+#ifndef __REACTOS__
+#ifndef _MSC_VER
+#include
+#else
+#include
+#endif
+
+#include
+#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);
+}
diff --git a/reactos/dll/shellext/shellbtrfs/recv.h b/reactos/dll/shellext/shellbtrfs/recv.h
new file mode 100644
index 00000000000..85e93298c59
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/recv.h
@@ -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 . */
+
+#include
+#include
+#include
+#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 cache;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/resource.h b/reactos/dll/shellext/shellbtrfs/resource.h
index b778b22e296..2506652f743 100644
--- a/reactos/dll/shellext/shellbtrfs/resource.h
+++ b/reactos/dll/shellext/shellbtrfs/resource.h
@@ -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
diff --git a/reactos/dll/shellext/shellbtrfs/scrub.cpp b/reactos/dll/shellext/shellbtrfs/scrub.cpp
new file mode 100644
index 00000000000..9e184221ca1
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/scrub.cpp
@@ -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 . */
+
+#include "shellext.h"
+#include "scrub.h"
+#include "resource.h"
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "btrfsioctl.h"
+#endif
+#include
+#include
+#include
+#ifndef __REACTOS__
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#include
+#include
+#endif
+#include
+
+#define NO_SHLWAPI_STRFCNS
+#include
+#include
+
+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);
+}
diff --git a/reactos/dll/shellext/shellbtrfs/scrub.h b/reactos/dll/shellext/shellbtrfs/scrub.h
new file mode 100644
index 00000000000..8ac94ac0a18
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/scrub.h
@@ -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 . */
+
+#include
+#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;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/send.cpp b/reactos/dll/shellext/shellbtrfs/send.cpp
new file mode 100644
index 00000000000..7a582d4224c
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/send.cpp
@@ -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 . */
+
+#include "shellext.h"
+#include "send.h"
+#include "resource.h"
+#include
+#include
+#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 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 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);
+}
diff --git a/reactos/dll/shellext/shellbtrfs/send.h b/reactos/dll/shellext/shellbtrfs/send.h
new file mode 100644
index 00000000000..713031b3597
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/send.h
@@ -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 . */
+
+#ifdef __REACTOS__
+#include "btrfs.h"
+#include
+#else
+#include "../btrfs.h"
+#endif
+#include
+#include
+
+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 clones;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc b/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc
index 0bfb971d625..bf4e7c080dc 100644
--- a/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc
+++ b/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc
@@ -7,7 +7,7 @@
//
// Generated from the TEXTINCLUDE 2 resource.
//
-#include "afxres.h"
+#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
@@ -27,18 +27,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
// 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 @@ BEGIN
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 @@ END
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 @@ BEGIN
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 @@ BEGIN
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 (Details)",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,6 +160,207 @@ BEGIN
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
+
/////////////////////////////////////////////////////////////////////////////
//
@@ -161,7 +368,7 @@ END
//
#ifdef APSTUDIO_INVOKED
-GUIDELINES DESIGNINFO
+GUIDELINES DESIGNINFO
BEGIN
IDD_PROP_SHEET, DIALOG
BEGIN
@@ -178,6 +385,94 @@ BEGIN
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 @@ END
// 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 @@ BEGIN
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,10 +523,229 @@ BEGIN
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
diff --git a/reactos/dll/shellext/shellbtrfs/shellext.h b/reactos/dll/shellext/shellbtrfs/shellext.h
new file mode 100644
index 00000000000..62017d90c85
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/shellext.h
@@ -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 . */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#ifndef __REACTOS__
+#define WINVER 0x0A00 // Windows 10
+#define _WIN32_WINNT 0x0A00
+#endif
+
+#ifndef __REACTOS__
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#include
+#endif
+#include
+#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);
diff --git a/reactos/dll/shellext/shellbtrfs/volpropsheet.cpp b/reactos/dll/shellext/shellbtrfs/volpropsheet.cpp
new file mode 100755
index 00000000000..9f2ae8d7f6b
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/volpropsheet.cpp
@@ -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 . */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#include "shellext.h"
+#ifndef __REACTOS__
+#include
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include
+#include
+
+#include "volpropsheet.h"
+#include "resource.h"
+
+HRESULT __stdcall BtrfsVolPropSheet::QueryInterface(REFIID riid, void **ppObj) {
+ if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
+ *ppObj = static_cast(this);
+ AddRef();
+ return S_OK;
+ } else if (riid == IID_IShellExtInit) {
+ *ppObj = static_cast(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
diff --git a/reactos/dll/shellext/shellbtrfs/volpropsheet.h b/reactos/dll/shellext/shellbtrfs/volpropsheet.h
new file mode 100644
index 00000000000..a0e618b423b
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/volpropsheet.h
@@ -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 . */
+
+#include
+#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;
+};
diff --git a/reactos/media/doc/README.FSD b/reactos/media/doc/README.FSD
index c0edcb46d42..a3d8f513c16 100644
--- a/reactos/media/doc/README.FSD
+++ b/reactos/media/doc/README.FSD
@@ -4,7 +4,7 @@
The following FSD are shared with: https://github.com/maharmstone/btrfs.
reactos/drivers/filesystems/btrfs # Synced to 1.0
-reactos/dll/shellext/shellbtrfs # Synced to 0.7
+reactos/dll/shellext/shellbtrfs # Synced to 1.0
reactos/sdk/lib/fslib/btrfslib # Synced to 0.8
The following FSD are shared with: http://www.ext2fsd.com/