/* 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);
}