From c032d89e77509055ab647f51b82b028e0e6e2fce Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Fri, 8 Sep 2017 11:59:42 +0000 Subject: [PATCH] [SHELLBTRFS] Sync the btrfs shell extension to 1.0 CORE-13763 svn path=/trunk/; revision=75793 --- .../dll/shellext/shellbtrfs/CMakeLists.txt | 14 +- reactos/dll/shellext/shellbtrfs/balance.cpp | 1293 ++++++++++ reactos/dll/shellext/shellbtrfs/balance.h | 58 + .../dll/shellext/shellbtrfs/contextmenu.cpp | 1791 ++++++++++++-- reactos/dll/shellext/shellbtrfs/contextmenu.h | 39 +- reactos/dll/shellext/shellbtrfs/devices.cpp | 1081 +++++++++ reactos/dll/shellext/shellbtrfs/devices.h | 167 ++ reactos/dll/shellext/shellbtrfs/factory.cpp | 36 +- reactos/dll/shellext/shellbtrfs/factory.h | 25 +- .../dll/shellext/shellbtrfs/iconoverlay.cpp | 58 +- reactos/dll/shellext/shellbtrfs/iconoverlay.h | 20 +- reactos/dll/shellext/shellbtrfs/main.cpp | 518 +++- reactos/dll/shellext/shellbtrfs/propsheet.cpp | 1528 +++++++----- reactos/dll/shellext/shellbtrfs/propsheet.h | 73 +- reactos/dll/shellext/shellbtrfs/reactos.cpp | 207 ++ reactos/dll/shellext/shellbtrfs/recv.cpp | 2146 +++++++++++++++++ reactos/dll/shellext/shellbtrfs/recv.h | 88 + reactos/dll/shellext/shellbtrfs/resource.h | 267 +- reactos/dll/shellext/shellbtrfs/scrub.cpp | 692 ++++++ reactos/dll/shellext/shellbtrfs/scrub.h | 46 + reactos/dll/shellext/shellbtrfs/send.cpp | 755 ++++++ reactos/dll/shellext/shellbtrfs/send.h | 64 + reactos/dll/shellext/shellbtrfs/shellbtrfs.rc | 558 ++++- reactos/dll/shellext/shellbtrfs/shellext.h | 185 ++ .../dll/shellext/shellbtrfs/volpropsheet.cpp | 1618 +++++++++++++ .../dll/shellext/shellbtrfs/volpropsheet.h | 110 + reactos/media/doc/README.FSD | 2 +- 27 files changed, 12358 insertions(+), 1081 deletions(-) create mode 100644 reactos/dll/shellext/shellbtrfs/balance.cpp create mode 100644 reactos/dll/shellext/shellbtrfs/balance.h create mode 100755 reactos/dll/shellext/shellbtrfs/devices.cpp create mode 100644 reactos/dll/shellext/shellbtrfs/devices.h create mode 100644 reactos/dll/shellext/shellbtrfs/reactos.cpp create mode 100644 reactos/dll/shellext/shellbtrfs/recv.cpp create mode 100644 reactos/dll/shellext/shellbtrfs/recv.h create mode 100644 reactos/dll/shellext/shellbtrfs/scrub.cpp create mode 100644 reactos/dll/shellext/shellbtrfs/scrub.h create mode 100644 reactos/dll/shellext/shellbtrfs/send.cpp create mode 100644 reactos/dll/shellext/shellbtrfs/send.h create mode 100644 reactos/dll/shellext/shellbtrfs/shellext.h create mode 100755 reactos/dll/shellext/shellbtrfs/volpropsheet.cpp create mode 100644 reactos/dll/shellext/shellbtrfs/volpropsheet.h 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/