xref: /reactos/dll/shellext/shellbtrfs/scrub.cpp (revision 06042735)
1c2c66affSColin Finck /* Copyright (c) Mark Harmstone 2017
2c2c66affSColin Finck  *
3c2c66affSColin Finck  * This file is part of WinBtrfs.
4c2c66affSColin Finck  *
5c2c66affSColin Finck  * WinBtrfs is free software: you can redistribute it and/or modify
6c2c66affSColin Finck  * it under the terms of the GNU Lesser General Public Licence as published by
7c2c66affSColin Finck  * the Free Software Foundation, either version 3 of the Licence, or
8c2c66affSColin Finck  * (at your option) any later version.
9c2c66affSColin Finck  *
10c2c66affSColin Finck  * WinBtrfs is distributed in the hope that it will be useful,
11c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13c2c66affSColin Finck  * GNU Lesser General Public Licence for more details.
14c2c66affSColin Finck  *
15c2c66affSColin Finck  * You should have received a copy of the GNU Lesser General Public Licence
16c2c66affSColin Finck  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17c2c66affSColin Finck 
18c2c66affSColin Finck #include "shellext.h"
19c2c66affSColin Finck #include "scrub.h"
20c2c66affSColin Finck #include "resource.h"
21c2c66affSColin Finck #ifndef __REACTOS__
22c2c66affSColin Finck #include "../btrfsioctl.h"
23c2c66affSColin Finck #else
24c2c66affSColin Finck #include "btrfsioctl.h"
25c2c66affSColin Finck #endif
26c2c66affSColin Finck #include <shlobj.h>
27c2c66affSColin Finck #include <uxtheme.h>
28c2c66affSColin Finck #include <stdio.h>
29c2c66affSColin Finck #ifndef __REACTOS__
30c2c66affSColin Finck #include <strsafe.h>
31c2c66affSColin Finck #include <winternl.h>
32c2c66affSColin Finck #else
33c2c66affSColin Finck #define WIN32_NO_STATUS
34c2c66affSColin Finck #include <windef.h>
35c2c66affSColin Finck #include <winbase.h>
36c2c66affSColin Finck #include <strsafe.h>
37c2c66affSColin Finck #include <ndk/iofuncs.h>
38c2c66affSColin Finck #include <ndk/iotypes.h>
39c2c66affSColin Finck #endif
40c2c66affSColin Finck 
41c2c66affSColin Finck #define NO_SHLWAPI_STRFCNS
42c2c66affSColin Finck #include <shlwapi.h>
43c2c66affSColin Finck #include <uxtheme.h>
44c2c66affSColin Finck 
UpdateTextBox(HWND hwndDlg,btrfs_query_scrub * bqs)45c2c66affSColin Finck void BtrfsScrub::UpdateTextBox(HWND hwndDlg, btrfs_query_scrub* bqs) {
467b718d36SPierre Schweitzer     btrfs_query_scrub* bqs2 = nullptr;
477b718d36SPierre Schweitzer     bool alloc_bqs2 = false;
48c2c66affSColin Finck     NTSTATUS Status;
497b718d36SPierre Schweitzer     wstring s, t, u;
507b718d36SPierre Schweitzer     WCHAR dt[255], tm[255];
51c2c66affSColin Finck     FILETIME filetime;
52c2c66affSColin Finck     SYSTEMTIME systime;
537b718d36SPierre Schweitzer     uint64_t recoverable_errors = 0, unrecoverable_errors = 0;
54c2c66affSColin Finck 
557b718d36SPierre Schweitzer     try {
56c2c66affSColin Finck         if (bqs->num_errors > 0) {
577b718d36SPierre Schweitzer             win_handle h;
58c2c66affSColin Finck             IO_STATUS_BLOCK iosb;
59c2c66affSColin Finck             ULONG len;
60c2c66affSColin Finck 
617b718d36SPierre Schweitzer             h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
627b718d36SPierre Schweitzer                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
637b718d36SPierre Schweitzer             if (h == INVALID_HANDLE_VALUE)
647b718d36SPierre Schweitzer                 throw last_error(GetLastError());
65c2c66affSColin Finck 
66c2c66affSColin Finck             len = 0;
67c2c66affSColin Finck 
687b718d36SPierre Schweitzer             try {
69c2c66affSColin Finck                 do {
70c2c66affSColin Finck                     len += 1024;
71c2c66affSColin Finck 
727b718d36SPierre Schweitzer                     if (bqs2) {
73c2c66affSColin Finck                         free(bqs2);
747b718d36SPierre Schweitzer                         bqs2 = nullptr;
757b718d36SPierre Schweitzer                     }
76c2c66affSColin Finck 
77c2c66affSColin Finck                     bqs2 = (btrfs_query_scrub*)malloc(len);
78c2c66affSColin Finck 
797b718d36SPierre Schweitzer                     Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, bqs2, len);
80c2c66affSColin Finck 
817b718d36SPierre Schweitzer                     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
827b718d36SPierre Schweitzer                         throw ntstatus_error(Status);
83c2c66affSColin Finck                 } while (Status == STATUS_BUFFER_OVERFLOW);
847b718d36SPierre Schweitzer             } catch (...) {
857b718d36SPierre Schweitzer                 if (bqs2)
867b718d36SPierre Schweitzer                     free(bqs2);
87c2c66affSColin Finck 
887b718d36SPierre Schweitzer                 throw;
897b718d36SPierre Schweitzer             }
90c2c66affSColin Finck 
917b718d36SPierre Schweitzer             alloc_bqs2 = true;
92c2c66affSColin Finck         } else
93c2c66affSColin Finck             bqs2 = bqs;
94c2c66affSColin Finck 
95c2c66affSColin Finck         // "scrub started"
96c2c66affSColin Finck         if (bqs2->start_time.QuadPart > 0) {
97c2c66affSColin Finck             filetime.dwLowDateTime = bqs2->start_time.LowPart;
98c2c66affSColin Finck             filetime.dwHighDateTime = bqs2->start_time.HighPart;
99c2c66affSColin Finck 
1007b718d36SPierre Schweitzer             if (!FileTimeToSystemTime(&filetime, &systime))
1017b718d36SPierre Schweitzer                 throw last_error(GetLastError());
102c2c66affSColin Finck 
1037b718d36SPierre Schweitzer             if (!SystemTimeToTzSpecificLocalTime(nullptr, &systime, &systime))
1047b718d36SPierre Schweitzer                 throw last_error(GetLastError());
105c2c66affSColin Finck 
1067b718d36SPierre Schweitzer             if (!load_string(module, IDS_SCRUB_MSG_STARTED, t))
1077b718d36SPierre Schweitzer                 throw last_error(GetLastError());
108c2c66affSColin Finck 
1097b718d36SPierre Schweitzer             if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, nullptr, dt, sizeof(dt) / sizeof(WCHAR)))
1107b718d36SPierre Schweitzer                 throw last_error(GetLastError());
111c2c66affSColin Finck 
1127b718d36SPierre Schweitzer             if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, nullptr, tm, sizeof(tm) / sizeof(WCHAR)))
1137b718d36SPierre Schweitzer                 throw last_error(GetLastError());
114c2c66affSColin Finck 
1157b718d36SPierre Schweitzer             wstring_sprintf(u, t, dt, tm);
116c2c66affSColin Finck 
117c2c66affSColin Finck             s += u;
118c2c66affSColin Finck             s += L"\r\n";
119c2c66affSColin Finck         }
120c2c66affSColin Finck 
121c2c66affSColin Finck         // errors
122c2c66affSColin Finck         if (bqs2->num_errors > 0) {
123c2c66affSColin Finck             btrfs_scrub_error* bse = &bqs2->errors;
124c2c66affSColin Finck 
125c2c66affSColin Finck             do {
126c2c66affSColin Finck                 if (bse->recovered)
127c2c66affSColin Finck                     recoverable_errors++;
128c2c66affSColin Finck                 else
129c2c66affSColin Finck                     unrecoverable_errors++;
130c2c66affSColin Finck 
131c2c66affSColin Finck                 if (bse->parity) {
1327b718d36SPierre Schweitzer                     if (!load_string(module, IDS_SCRUB_MSG_RECOVERABLE_PARITY, t))
1337b718d36SPierre Schweitzer                         throw last_error(GetLastError());
134c2c66affSColin Finck 
1357b718d36SPierre Schweitzer                     wstring_sprintf(u, t, bse->address, bse->device);
136c2c66affSColin Finck                 } else if (bse->is_metadata) {
137c2c66affSColin Finck                     int message;
138c2c66affSColin Finck 
139c2c66affSColin Finck                     if (bse->recovered)
140c2c66affSColin Finck                         message = IDS_SCRUB_MSG_RECOVERABLE_METADATA;
141c2c66affSColin Finck                     else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0)
142c2c66affSColin Finck                         message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA;
143c2c66affSColin Finck                     else
144c2c66affSColin Finck                         message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM;
145c2c66affSColin Finck 
1467b718d36SPierre Schweitzer                     if (!load_string(module, message, t))
1477b718d36SPierre Schweitzer                         throw last_error(GetLastError());
148c2c66affSColin Finck 
1497b718d36SPierre Schweitzer                     if (bse->recovered)
1507b718d36SPierre Schweitzer                         wstring_sprintf(u, t, bse->address, bse->device);
1517b718d36SPierre Schweitzer                     else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0)
1527b718d36SPierre Schweitzer                         wstring_sprintf(u, t, bse->address, bse->device, bse->metadata.root, bse->metadata.level);
1537b718d36SPierre Schweitzer                     else
1547b718d36SPierre Schweitzer                         wstring_sprintf(u, t, bse->address, bse->device, bse->metadata.root, bse->metadata.level, bse->metadata.firstitem.obj_id,
1557b718d36SPierre Schweitzer                                         bse->metadata.firstitem.obj_type, bse->metadata.firstitem.offset);
156c2c66affSColin Finck                 } else {
157c2c66affSColin Finck                     int message;
158c2c66affSColin Finck 
159c2c66affSColin Finck                     if (bse->recovered)
160c2c66affSColin Finck                         message = IDS_SCRUB_MSG_RECOVERABLE_DATA;
161c2c66affSColin Finck                     else if (bse->data.subvol != 0)
162c2c66affSColin Finck                         message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL;
163c2c66affSColin Finck                     else
164c2c66affSColin Finck                         message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA;
165c2c66affSColin Finck 
1667b718d36SPierre Schweitzer                     if (!load_string(module, message, t))
1677b718d36SPierre Schweitzer                         throw last_error(GetLastError());
168c2c66affSColin Finck 
1697b718d36SPierre Schweitzer                     if (bse->recovered)
1707b718d36SPierre Schweitzer                         wstring_sprintf(u, t, bse->address, bse->device);
1717b718d36SPierre Schweitzer                     else if (bse->data.subvol != 0)
1727b718d36SPierre Schweitzer                         wstring_sprintf(u, t, bse->address, bse->device, bse->data.subvol,
1737b718d36SPierre Schweitzer                             bse->data.filename_length / sizeof(WCHAR), bse->data.filename, bse->data.offset);
1747b718d36SPierre Schweitzer                     else
1757b718d36SPierre Schweitzer                         wstring_sprintf(u, t, bse->address, bse->device, bse->data.filename_length / sizeof(WCHAR),
1767b718d36SPierre Schweitzer                             bse->data.filename, bse->data.offset);
177c2c66affSColin Finck                 }
178c2c66affSColin Finck 
179c2c66affSColin Finck                 s += u;
180c2c66affSColin Finck                 s += L"\r\n";
181c2c66affSColin Finck 
182c2c66affSColin Finck                 if (bse->next_entry == 0)
183c2c66affSColin Finck                     break;
184c2c66affSColin Finck                 else
1857b718d36SPierre Schweitzer                     bse = (btrfs_scrub_error*)((uint8_t*)bse + bse->next_entry);
1867b718d36SPierre Schweitzer             } while (true);
187c2c66affSColin Finck         }
188c2c66affSColin Finck 
189c2c66affSColin Finck         if (bqs2->finish_time.QuadPart > 0) {
1907b718d36SPierre Schweitzer             wstring d1, d2;
191c2c66affSColin Finck             float speed;
192c2c66affSColin Finck 
193c2c66affSColin Finck             // "scrub finished"
194c2c66affSColin Finck 
195c2c66affSColin Finck             filetime.dwLowDateTime = bqs2->finish_time.LowPart;
196c2c66affSColin Finck             filetime.dwHighDateTime = bqs2->finish_time.HighPart;
197c2c66affSColin Finck 
1987b718d36SPierre Schweitzer             if (!FileTimeToSystemTime(&filetime, &systime))
1997b718d36SPierre Schweitzer                 throw last_error(GetLastError());
200c2c66affSColin Finck 
2017b718d36SPierre Schweitzer             if (!SystemTimeToTzSpecificLocalTime(nullptr, &systime, &systime))
2027b718d36SPierre Schweitzer                 throw last_error(GetLastError());
203c2c66affSColin Finck 
2047b718d36SPierre Schweitzer             if (!load_string(module, IDS_SCRUB_MSG_FINISHED, t))
2057b718d36SPierre Schweitzer                 throw last_error(GetLastError());
206c2c66affSColin Finck 
2077b718d36SPierre Schweitzer             if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, nullptr, dt, sizeof(dt) / sizeof(WCHAR)))
2087b718d36SPierre Schweitzer                 throw last_error(GetLastError());
209c2c66affSColin Finck 
2107b718d36SPierre Schweitzer             if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, nullptr, tm, sizeof(tm) / sizeof(WCHAR)))
2117b718d36SPierre Schweitzer                 throw last_error(GetLastError());
212c2c66affSColin Finck 
2137b718d36SPierre Schweitzer             wstring_sprintf(u, t, dt, tm);
214c2c66affSColin Finck 
215c2c66affSColin Finck             s += u;
216c2c66affSColin Finck             s += L"\r\n";
217c2c66affSColin Finck 
218c2c66affSColin Finck             // summary
219c2c66affSColin Finck 
2207b718d36SPierre Schweitzer             if (!load_string(module, IDS_SCRUB_MSG_SUMMARY, t))
2217b718d36SPierre Schweitzer                 throw last_error(GetLastError());
222c2c66affSColin Finck 
2237b718d36SPierre Schweitzer             format_size(bqs2->data_scrubbed, d1, false);
224c2c66affSColin Finck 
225c2c66affSColin Finck             speed = (float)bqs2->data_scrubbed / ((float)bqs2->duration / 10000000.0f);
226c2c66affSColin Finck 
2277b718d36SPierre Schweitzer             format_size((uint64_t)speed, d2, false);
228c2c66affSColin Finck 
2297b718d36SPierre Schweitzer             wstring_sprintf(u, t, d1.c_str(), bqs2->duration / 10000000, d2.c_str());
230c2c66affSColin Finck 
231c2c66affSColin Finck             s += u;
232c2c66affSColin Finck             s += L"\r\n";
233c2c66affSColin Finck 
234c2c66affSColin Finck             // recoverable errors
235c2c66affSColin Finck 
2367b718d36SPierre Schweitzer             if (!load_string(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_RECOVERABLE, t))
2377b718d36SPierre Schweitzer                 throw last_error(GetLastError());
238c2c66affSColin Finck 
2397b718d36SPierre Schweitzer             wstring_sprintf(u, t, recoverable_errors);
240c2c66affSColin Finck 
241c2c66affSColin Finck             s += u;
242c2c66affSColin Finck             s += L"\r\n";
243c2c66affSColin Finck 
244c2c66affSColin Finck             // unrecoverable errors
245c2c66affSColin Finck 
2467b718d36SPierre Schweitzer             if (!load_string(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE, t))
2477b718d36SPierre Schweitzer                 throw last_error(GetLastError());
248c2c66affSColin Finck 
2497b718d36SPierre Schweitzer             wstring_sprintf(u, t, unrecoverable_errors);
250c2c66affSColin Finck 
251c2c66affSColin Finck             s += u;
252c2c66affSColin Finck             s += L"\r\n";
253c2c66affSColin Finck         }
254c2c66affSColin Finck 
255c2c66affSColin Finck         SetWindowTextW(GetDlgItem(hwndDlg, IDC_SCRUB_INFO), s.c_str());
2567b718d36SPierre Schweitzer     } catch (...) {
2577b718d36SPierre Schweitzer         if (alloc_bqs2)
2587b718d36SPierre Schweitzer             free(bqs2);
259c2c66affSColin Finck 
2607b718d36SPierre Schweitzer         throw;
2617b718d36SPierre Schweitzer     }
2627b718d36SPierre Schweitzer 
263c2c66affSColin Finck     if (alloc_bqs2)
264c2c66affSColin Finck         free(bqs2);
265c2c66affSColin Finck }
266c2c66affSColin Finck 
RefreshScrubDlg(HWND hwndDlg,bool first_time)2677b718d36SPierre Schweitzer void BtrfsScrub::RefreshScrubDlg(HWND hwndDlg, bool first_time) {
268c2c66affSColin Finck     btrfs_query_scrub bqs;
269c2c66affSColin Finck 
2707b718d36SPierre Schweitzer     {
2717b718d36SPierre Schweitzer         win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
2727b718d36SPierre Schweitzer                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
273c2c66affSColin Finck         if (h != INVALID_HANDLE_VALUE) {
274c2c66affSColin Finck             NTSTATUS Status;
275c2c66affSColin Finck             IO_STATUS_BLOCK iosb;
276c2c66affSColin Finck 
2777b718d36SPierre Schweitzer             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, &bqs, sizeof(btrfs_query_scrub));
278c2c66affSColin Finck 
2797b718d36SPierre Schweitzer             if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
2807b718d36SPierre Schweitzer                 throw ntstatus_error(Status);
2817b718d36SPierre Schweitzer         } else
2827b718d36SPierre Schweitzer             throw last_error(GetLastError());
283c2c66affSColin Finck     }
284c2c66affSColin Finck 
285c2c66affSColin Finck     if (first_time || status != bqs.status || chunks_left != bqs.chunks_left) {
2867b718d36SPierre Schweitzer         wstring s;
287c2c66affSColin Finck 
288c2c66affSColin Finck         if (bqs.status == BTRFS_SCRUB_STOPPED) {
2897b718d36SPierre Schweitzer             EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), true);
2907b718d36SPierre Schweitzer             EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), false);
2917b718d36SPierre Schweitzer             EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), false);
292c2c66affSColin Finck 
293c2c66affSColin Finck             if (bqs.error != STATUS_SUCCESS) {
2947b718d36SPierre Schweitzer                 wstring t;
295c2c66affSColin Finck 
2967b718d36SPierre Schweitzer                 if (!load_string(module, IDS_SCRUB_FAILED, t))
2977b718d36SPierre Schweitzer                     throw last_error(GetLastError());
298c2c66affSColin Finck 
2997b718d36SPierre Schweitzer                 wstring_sprintf(s, t, bqs.error);
300c2c66affSColin Finck             } else {
3017b718d36SPierre Schweitzer                 if (!load_string(module, bqs.total_chunks == 0 ? IDS_NO_SCRUB : IDS_SCRUB_FINISHED, s))
3027b718d36SPierre Schweitzer                     throw last_error(GetLastError());
303c2c66affSColin Finck             }
304c2c66affSColin Finck         } else {
3057b718d36SPierre Schweitzer             wstring t;
306c2c66affSColin Finck             float pc;
307c2c66affSColin Finck 
3087b718d36SPierre Schweitzer             EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), false);
3097b718d36SPierre Schweitzer             EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), true);
3107b718d36SPierre Schweitzer             EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), true);
311c2c66affSColin Finck 
3127b718d36SPierre Schweitzer             if (!load_string(module, bqs.status == BTRFS_SCRUB_PAUSED ? IDS_SCRUB_PAUSED : IDS_SCRUB_RUNNING, t))
3137b718d36SPierre Schweitzer                 throw last_error(GetLastError());
314c2c66affSColin Finck 
315c2c66affSColin Finck             pc = ((float)(bqs.total_chunks - bqs.chunks_left) / (float)bqs.total_chunks) * 100.0f;
316c2c66affSColin Finck 
3177b718d36SPierre Schweitzer             wstring_sprintf(s, t, bqs.total_chunks - bqs.chunks_left, bqs.total_chunks, pc);
318c2c66affSColin Finck         }
319c2c66affSColin Finck 
3207b718d36SPierre Schweitzer         SetDlgItemTextW(hwndDlg, IDC_SCRUB_STATUS, s.c_str());
321c2c66affSColin Finck 
322c2c66affSColin Finck         if (first_time || status != bqs.status) {
323c2c66affSColin Finck             EnableWindow(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), bqs.status != BTRFS_SCRUB_STOPPED);
324c2c66affSColin Finck 
325c2c66affSColin Finck             if (bqs.status != BTRFS_SCRUB_STOPPED) {
326c2c66affSColin Finck                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)bqs.total_chunks);
327c2c66affSColin Finck                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
328c2c66affSColin Finck 
329c2c66affSColin Finck                 if (bqs.status == BTRFS_SCRUB_PAUSED)
330c2c66affSColin Finck                     SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_PAUSED, 0);
331c2c66affSColin Finck                 else
332c2c66affSColin Finck                     SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
333c2c66affSColin Finck             } else {
334c2c66affSColin Finck                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, 0);
335c2c66affSColin Finck                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, 0, 0);
336c2c66affSColin Finck             }
337c2c66affSColin Finck 
338c2c66affSColin Finck             chunks_left = bqs.chunks_left;
339c2c66affSColin Finck         }
340c2c66affSColin Finck     }
341c2c66affSColin Finck 
342c2c66affSColin Finck     if (bqs.status != BTRFS_SCRUB_STOPPED && chunks_left != bqs.chunks_left) {
343c2c66affSColin Finck         SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
344c2c66affSColin Finck         chunks_left = bqs.chunks_left;
345c2c66affSColin Finck     }
346c2c66affSColin Finck 
347c2c66affSColin Finck     if (first_time || status != bqs.status || num_errors != bqs.num_errors) {
348c2c66affSColin Finck         UpdateTextBox(hwndDlg, &bqs);
349c2c66affSColin Finck 
350c2c66affSColin Finck         num_errors = bqs.num_errors;
351c2c66affSColin Finck     }
352c2c66affSColin Finck 
353c2c66affSColin Finck     status = bqs.status;
354c2c66affSColin Finck }
355c2c66affSColin Finck 
StartScrub(HWND hwndDlg)356c2c66affSColin Finck void BtrfsScrub::StartScrub(HWND hwndDlg) {
3577b718d36SPierre Schweitzer     win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
3587b718d36SPierre Schweitzer                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
359c2c66affSColin Finck 
360c2c66affSColin Finck     if (h != INVALID_HANDLE_VALUE) {
361c2c66affSColin Finck         NTSTATUS Status;
362c2c66affSColin Finck         IO_STATUS_BLOCK iosb;
363c2c66affSColin Finck 
3647b718d36SPierre Schweitzer         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_START_SCRUB, nullptr, 0, nullptr, 0);
365c2c66affSColin Finck 
366c2c66affSColin Finck         if (Status == STATUS_DEVICE_NOT_READY) {
367c2c66affSColin Finck             btrfs_query_balance bqb;
368c2c66affSColin Finck             NTSTATUS Status2;
369c2c66affSColin Finck 
3707b718d36SPierre Schweitzer             Status2 = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb, sizeof(btrfs_query_balance));
371c2c66affSColin Finck 
3727b718d36SPierre Schweitzer             if (NT_SUCCESS(Status2) && bqb.status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))
3737b718d36SPierre Schweitzer                 throw string_error(IDS_SCRUB_BALANCE_RUNNING);
374c2c66affSColin Finck         }
375c2c66affSColin Finck 
3767b718d36SPierre Schweitzer         if (!NT_SUCCESS(Status))
3777b718d36SPierre Schweitzer             throw ntstatus_error(Status);
378c2c66affSColin Finck 
3797b718d36SPierre Schweitzer         RefreshScrubDlg(hwndDlg, true);
3807b718d36SPierre Schweitzer     } else
3817b718d36SPierre Schweitzer         throw last_error(GetLastError());
382c2c66affSColin Finck }
383c2c66affSColin Finck 
PauseScrub(HWND hwndDlg)384c2c66affSColin Finck void BtrfsScrub::PauseScrub(HWND hwndDlg) {
385c2c66affSColin Finck     btrfs_query_scrub bqs;
386c2c66affSColin Finck 
3877b718d36SPierre Schweitzer     win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
3887b718d36SPierre Schweitzer                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
3897b718d36SPierre Schweitzer 
390c2c66affSColin Finck     if (h != INVALID_HANDLE_VALUE) {
391c2c66affSColin Finck         NTSTATUS Status;
392c2c66affSColin Finck         IO_STATUS_BLOCK iosb;
393c2c66affSColin Finck 
3947b718d36SPierre Schweitzer         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, &bqs, sizeof(btrfs_query_scrub));
395c2c66affSColin Finck 
3967b718d36SPierre Schweitzer         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
3977b718d36SPierre Schweitzer             throw ntstatus_error(Status);
398c2c66affSColin Finck 
399c2c66affSColin Finck         if (bqs.status == BTRFS_SCRUB_PAUSED)
4007b718d36SPierre Schweitzer             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESUME_SCRUB, nullptr, 0, nullptr, 0);
401c2c66affSColin Finck         else
4027b718d36SPierre Schweitzer             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_PAUSE_SCRUB, nullptr, 0, nullptr, 0);
403c2c66affSColin Finck 
4047b718d36SPierre Schweitzer         if (!NT_SUCCESS(Status))
4057b718d36SPierre Schweitzer             throw ntstatus_error(Status);
4067b718d36SPierre Schweitzer     } else
4077b718d36SPierre Schweitzer         throw last_error(GetLastError());
408c2c66affSColin Finck }
409c2c66affSColin Finck 
StopScrub(HWND hwndDlg)410c2c66affSColin Finck void BtrfsScrub::StopScrub(HWND hwndDlg) {
4117b718d36SPierre Schweitzer     win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
4127b718d36SPierre Schweitzer                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
413c2c66affSColin Finck 
414c2c66affSColin Finck     if (h != INVALID_HANDLE_VALUE) {
415c2c66affSColin Finck         NTSTATUS Status;
416c2c66affSColin Finck         IO_STATUS_BLOCK iosb;
417c2c66affSColin Finck 
4187b718d36SPierre Schweitzer         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_STOP_SCRUB, nullptr, 0, nullptr, 0);
419c2c66affSColin Finck 
4207b718d36SPierre Schweitzer         if (!NT_SUCCESS(Status))
4217b718d36SPierre Schweitzer             throw ntstatus_error(Status);
4227b718d36SPierre Schweitzer     } else
4237b718d36SPierre Schweitzer         throw last_error(GetLastError());
424c2c66affSColin Finck }
425c2c66affSColin Finck 
ScrubDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)426c2c66affSColin Finck INT_PTR CALLBACK BtrfsScrub::ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
4277b718d36SPierre Schweitzer     try {
428c2c66affSColin Finck         switch (uMsg) {
429c2c66affSColin Finck             case WM_INITDIALOG:
4307b718d36SPierre Schweitzer                 RefreshScrubDlg(hwndDlg, true);
4317b718d36SPierre Schweitzer                 SetTimer(hwndDlg, 1, 1000, nullptr);
432c2c66affSColin Finck             break;
433c2c66affSColin Finck 
434c2c66affSColin Finck             case WM_COMMAND:
435c2c66affSColin Finck                 switch (HIWORD(wParam)) {
436c2c66affSColin Finck                     case BN_CLICKED:
437c2c66affSColin Finck                         switch (LOWORD(wParam)) {
438c2c66affSColin Finck                             case IDOK:
439c2c66affSColin Finck                             case IDCANCEL:
440c2c66affSColin Finck                                 EndDialog(hwndDlg, 0);
4417b718d36SPierre Schweitzer                             return true;
442c2c66affSColin Finck 
443c2c66affSColin Finck                             case IDC_START_SCRUB:
444c2c66affSColin Finck                                 StartScrub(hwndDlg);
4457b718d36SPierre Schweitzer                             return true;
446c2c66affSColin Finck 
447c2c66affSColin Finck                             case IDC_PAUSE_SCRUB:
448c2c66affSColin Finck                                 PauseScrub(hwndDlg);
4497b718d36SPierre Schweitzer                             return true;
450c2c66affSColin Finck 
451c2c66affSColin Finck                             case IDC_CANCEL_SCRUB:
452c2c66affSColin Finck                                 StopScrub(hwndDlg);
4537b718d36SPierre Schweitzer                             return true;
454c2c66affSColin Finck                         }
455c2c66affSColin Finck                     break;
456c2c66affSColin Finck                 }
457c2c66affSColin Finck             break;
458c2c66affSColin Finck 
459c2c66affSColin Finck             case WM_TIMER:
4607b718d36SPierre Schweitzer                 RefreshScrubDlg(hwndDlg, false);
461c2c66affSColin Finck             break;
462c2c66affSColin Finck         }
4637b718d36SPierre Schweitzer     } catch (const exception& e) {
4647b718d36SPierre Schweitzer         error_message(hwndDlg, e.what());
4657b718d36SPierre Schweitzer     }
466c2c66affSColin Finck 
4677b718d36SPierre Schweitzer     return false;
468c2c66affSColin Finck }
469c2c66affSColin Finck 
stub_ScrubDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)470c2c66affSColin Finck static INT_PTR CALLBACK stub_ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
471c2c66affSColin Finck     BtrfsScrub* bs;
472c2c66affSColin Finck 
473c2c66affSColin Finck     if (uMsg == WM_INITDIALOG) {
474c2c66affSColin Finck         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
475c2c66affSColin Finck         bs = (BtrfsScrub*)lParam;
476c2c66affSColin Finck     } else {
477c2c66affSColin Finck         bs = (BtrfsScrub*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
478c2c66affSColin Finck     }
479c2c66affSColin Finck 
480c2c66affSColin Finck     if (bs)
481c2c66affSColin Finck         return bs->ScrubDlgProc(hwndDlg, uMsg, wParam, lParam);
482c2c66affSColin Finck     else
4837b718d36SPierre Schweitzer         return false;
484c2c66affSColin Finck }
485c2c66affSColin Finck 
ShowScrubW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)486*06042735SVincent Franchomme extern "C" void CALLBACK ShowScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
4877b718d36SPierre Schweitzer     try {
4887b718d36SPierre Schweitzer         win_handle token;
489c2c66affSColin Finck         TOKEN_PRIVILEGES tp;
490c2c66affSColin Finck         LUID luid;
491c2c66affSColin Finck 
4927b718d36SPierre Schweitzer         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
4937b718d36SPierre Schweitzer             throw last_error(GetLastError());
494c2c66affSColin Finck 
4957b718d36SPierre Schweitzer         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
4967b718d36SPierre Schweitzer             throw last_error(GetLastError());
497c2c66affSColin Finck 
498c2c66affSColin Finck         tp.PrivilegeCount = 1;
499c2c66affSColin Finck         tp.Privileges[0].Luid = luid;
500c2c66affSColin Finck         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
501c2c66affSColin Finck 
5027b718d36SPierre Schweitzer         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
5037b718d36SPierre Schweitzer             throw last_error(GetLastError());
504c2c66affSColin Finck 
505c2c66affSColin Finck         set_dpi_aware();
506c2c66affSColin Finck 
5077b718d36SPierre Schweitzer         BtrfsScrub scrub(lpszCmdLine);
508c2c66affSColin Finck 
5097b718d36SPierre Schweitzer         DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SCRUB), hwnd, stub_ScrubDlgProc, (LPARAM)&scrub);
5107b718d36SPierre Schweitzer     } catch (const exception& e) {
5117b718d36SPierre Schweitzer         error_message(hwnd, e.what());
5127b718d36SPierre Schweitzer     }
513c2c66affSColin Finck }
514c2c66affSColin Finck 
StartScrubW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)515*06042735SVincent Franchomme extern "C" void CALLBACK StartScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
5167b718d36SPierre Schweitzer     vector<wstring> args;
517c2c66affSColin Finck 
5187b718d36SPierre Schweitzer     command_line_to_args(lpszCmdLine, args);
519c2c66affSColin Finck 
5207b718d36SPierre Schweitzer     if (args.size() >= 1) {
521c2c66affSColin Finck         LUID luid;
522c2c66affSColin Finck         TOKEN_PRIVILEGES tp;
523c2c66affSColin Finck 
5247b718d36SPierre Schweitzer         {
5257b718d36SPierre Schweitzer             win_handle token;
526c2c66affSColin Finck 
5277b718d36SPierre Schweitzer             if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
5287b718d36SPierre Schweitzer                 return;
5297b718d36SPierre Schweitzer 
5307b718d36SPierre Schweitzer             if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
5317b718d36SPierre Schweitzer                 return;
532c2c66affSColin Finck 
533c2c66affSColin Finck             tp.PrivilegeCount = 1;
534c2c66affSColin Finck             tp.Privileges[0].Luid = luid;
535c2c66affSColin Finck             tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
536c2c66affSColin Finck 
5377b718d36SPierre Schweitzer             if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
5387b718d36SPierre Schweitzer                 return;
5392da53310SPierre Schweitzer         }
540c2c66affSColin Finck 
5417b718d36SPierre Schweitzer         win_handle h = CreateFileW(args[0].c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
5427b718d36SPierre Schweitzer                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
543c2c66affSColin Finck         if (h != INVALID_HANDLE_VALUE) {
544c2c66affSColin Finck             IO_STATUS_BLOCK iosb;
545c2c66affSColin Finck 
5467b718d36SPierre Schweitzer             NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_START_SCRUB, nullptr, 0, nullptr, 0);
547c2c66affSColin Finck         }
548c2c66affSColin Finck     }
549c2c66affSColin Finck }
550c2c66affSColin Finck 
StopScrubW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)551*06042735SVincent Franchomme extern "C" void CALLBACK StopScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
5527b718d36SPierre Schweitzer     vector<wstring> args;
553c2c66affSColin Finck 
5547b718d36SPierre Schweitzer     command_line_to_args(lpszCmdLine, args);
555c2c66affSColin Finck 
5567b718d36SPierre Schweitzer     if (args.size() >= 1) {
557c2c66affSColin Finck         LUID luid;
558c2c66affSColin Finck         TOKEN_PRIVILEGES tp;
559c2c66affSColin Finck 
5607b718d36SPierre Schweitzer         {
5617b718d36SPierre Schweitzer             win_handle token;
562c2c66affSColin Finck 
5637b718d36SPierre Schweitzer             if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
5647b718d36SPierre Schweitzer                 return;
5657b718d36SPierre Schweitzer 
5667b718d36SPierre Schweitzer             if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
5677b718d36SPierre Schweitzer                 return;
568c2c66affSColin Finck 
569c2c66affSColin Finck             tp.PrivilegeCount = 1;
570c2c66affSColin Finck             tp.Privileges[0].Luid = luid;
571c2c66affSColin Finck             tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
572c2c66affSColin Finck 
5737b718d36SPierre Schweitzer             if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
5747b718d36SPierre Schweitzer                 return;
5757b718d36SPierre Schweitzer         }
576c2c66affSColin Finck 
5777b718d36SPierre Schweitzer         win_handle h = CreateFileW(args[0].c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
5787b718d36SPierre Schweitzer                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
579c2c66affSColin Finck         if (h != INVALID_HANDLE_VALUE) {
580c2c66affSColin Finck             IO_STATUS_BLOCK iosb;
581c2c66affSColin Finck 
5827b718d36SPierre Schweitzer             NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_STOP_SCRUB, nullptr, 0, nullptr, 0);
583c2c66affSColin Finck         }
584c2c66affSColin Finck     }
585c2c66affSColin Finck }
586