xref: /reactos/dll/shellext/shellbtrfs/scrub.cpp (revision aad80191)
1 /* Copyright (c) Mark Harmstone 2017
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "shellext.h"
19 #include "scrub.h"
20 #include "resource.h"
21 #ifndef __REACTOS__
22 #include "../btrfsioctl.h"
23 #else
24 #include "btrfsioctl.h"
25 #endif
26 #include <shlobj.h>
27 #include <uxtheme.h>
28 #include <stdio.h>
29 #ifndef __REACTOS__
30 #include <strsafe.h>
31 #include <winternl.h>
32 #else
33 #define WIN32_NO_STATUS
34 #include <windef.h>
35 #include <winbase.h>
36 #include <strsafe.h>
37 #include <ndk/iofuncs.h>
38 #include <ndk/iotypes.h>
39 #endif
40 #include <string>
41 
42 #define NO_SHLWAPI_STRFCNS
43 #include <shlwapi.h>
44 #include <uxtheme.h>
45 
46 void BtrfsScrub::UpdateTextBox(HWND hwndDlg, btrfs_query_scrub* bqs) {
47     btrfs_query_scrub* bqs2 = NULL;
48     BOOL alloc_bqs2 = FALSE;
49     NTSTATUS Status;
50     std::wstring s;
51     WCHAR t[255], u[255], dt[255], tm[255];
52     FILETIME filetime;
53     SYSTEMTIME systime;
54     UINT64 recoverable_errors = 0, unrecoverable_errors = 0;
55 
56     if (bqs->num_errors > 0) {
57         HANDLE h;
58         IO_STATUS_BLOCK iosb;
59         ULONG len;
60 
61         h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
62                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
63         if (h == INVALID_HANDLE_VALUE) {
64             ShowError(hwndDlg, GetLastError());
65             return;
66         }
67 
68         len = 0;
69 
70         do {
71             len += 1024;
72 
73             if (bqs2)
74                 free(bqs2);
75 
76             bqs2 = (btrfs_query_scrub*)malloc(len);
77 
78             Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_SCRUB, NULL, 0, bqs2, len);
79 
80             if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
81                 ShowNtStatusError(hwndDlg, Status);
82                 CloseHandle(h);
83                 free(bqs2);
84                 return;
85             }
86         } while (Status == STATUS_BUFFER_OVERFLOW);
87 
88         alloc_bqs2 = TRUE;
89 
90         CloseHandle(h);
91     } else
92         bqs2 = bqs;
93 
94     s[0] = 0;
95 
96     // "scrub started"
97     if (bqs2->start_time.QuadPart > 0) {
98         filetime.dwLowDateTime = bqs2->start_time.LowPart;
99         filetime.dwHighDateTime = bqs2->start_time.HighPart;
100 
101         if (!FileTimeToSystemTime(&filetime, &systime)) {
102             ShowError(hwndDlg, GetLastError());
103             goto end;
104         }
105 
106         if (!SystemTimeToTzSpecificLocalTime(NULL, &systime, &systime)) {
107             ShowError(hwndDlg, GetLastError());
108             goto end;
109         }
110 
111         if (!LoadStringW(module, IDS_SCRUB_MSG_STARTED, t, sizeof(t) / sizeof(WCHAR))) {
112             ShowError(hwndDlg, GetLastError());
113             goto end;
114         }
115 
116         if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, dt, sizeof(dt) / sizeof(WCHAR))) {
117             ShowError(hwndDlg, GetLastError());
118             goto end;
119         }
120 
121         if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, NULL, tm, sizeof(tm) / sizeof(WCHAR))) {
122             ShowError(hwndDlg, GetLastError());
123             goto end;
124         }
125 
126         if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, dt, tm) == STRSAFE_E_INSUFFICIENT_BUFFER)
127             goto end;
128 
129         s += u;
130         s += L"\r\n";
131     }
132 
133     // errors
134     if (bqs2->num_errors > 0) {
135         btrfs_scrub_error* bse = &bqs2->errors;
136 
137         do {
138             if (bse->recovered)
139                 recoverable_errors++;
140             else
141                 unrecoverable_errors++;
142 
143             if (bse->parity) {
144                 if (!LoadStringW(module, IDS_SCRUB_MSG_RECOVERABLE_PARITY, t, sizeof(t) / sizeof(WCHAR))) {
145                     ShowError(hwndDlg, GetLastError());
146                     goto end;
147                 }
148 
149                 if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device) == STRSAFE_E_INSUFFICIENT_BUFFER)
150                     goto end;
151             } else if (bse->is_metadata) {
152                 int message;
153 
154                 if (bse->recovered)
155                     message = IDS_SCRUB_MSG_RECOVERABLE_METADATA;
156                 else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0)
157                     message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA;
158                 else
159                     message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM;
160 
161                 if (!LoadStringW(module, message, t, sizeof(t) / sizeof(WCHAR))) {
162                     ShowError(hwndDlg, GetLastError());
163                     goto end;
164                 }
165 
166                 if (bse->recovered) {
167                     if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device) == STRSAFE_E_INSUFFICIENT_BUFFER)
168                         goto end;
169                 } else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0) {
170                     if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device,
171                         bse->metadata.root, bse->metadata.level) == STRSAFE_E_INSUFFICIENT_BUFFER)
172                         goto end;
173                 } else {
174                     if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device,
175                         bse->metadata.root, bse->metadata.level, bse->metadata.firstitem.obj_id, bse->metadata.firstitem.obj_type,
176                         bse->metadata.firstitem.offset) == STRSAFE_E_INSUFFICIENT_BUFFER)
177                         goto end;
178                 }
179             } else {
180                 int message;
181 
182                 if (bse->recovered)
183                     message = IDS_SCRUB_MSG_RECOVERABLE_DATA;
184                 else if (bse->data.subvol != 0)
185                     message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL;
186                 else
187                     message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA;
188 
189                 if (!LoadStringW(module, message, t, sizeof(t) / sizeof(WCHAR))) {
190                     ShowError(hwndDlg, GetLastError());
191                     goto end;
192                 }
193 
194                 if (bse->recovered) {
195                     if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device) == STRSAFE_E_INSUFFICIENT_BUFFER)
196                         goto end;
197                 } else if (bse->data.subvol != 0) {
198                     if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device, bse->data.subvol,
199                         bse->data.filename_length / sizeof(WCHAR), bse->data.filename, bse->data.offset) == STRSAFE_E_INSUFFICIENT_BUFFER)
200                         goto end;
201                 } else {
202                     if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, bse->address, bse->device, bse->data.filename_length / sizeof(WCHAR),
203                         bse->data.filename, bse->data.offset) == STRSAFE_E_INSUFFICIENT_BUFFER)
204                         goto end;
205                 }
206             }
207 
208             s += u;
209             s += L"\r\n";
210 
211             if (bse->next_entry == 0)
212                 break;
213             else
214                 bse = (btrfs_scrub_error*)((UINT8*)bse + bse->next_entry);
215         } while (TRUE);
216     }
217 
218     if (bqs2->finish_time.QuadPart > 0) {
219         WCHAR d1[255], d2[255];
220         float speed;
221 
222         // "scrub finished"
223 
224         filetime.dwLowDateTime = bqs2->finish_time.LowPart;
225         filetime.dwHighDateTime = bqs2->finish_time.HighPart;
226 
227         if (!FileTimeToSystemTime(&filetime, &systime)) {
228             ShowError(hwndDlg, GetLastError());
229             goto end;
230         }
231 
232         if (!SystemTimeToTzSpecificLocalTime(NULL, &systime, &systime)) {
233             ShowError(hwndDlg, GetLastError());
234             goto end;
235         }
236 
237         if (!LoadStringW(module, IDS_SCRUB_MSG_FINISHED, t, sizeof(t) / sizeof(WCHAR))) {
238             ShowError(hwndDlg, GetLastError());
239             goto end;
240         }
241 
242         if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, dt, sizeof(dt) / sizeof(WCHAR))) {
243             ShowError(hwndDlg, GetLastError());
244             goto end;
245         }
246 
247         if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, NULL, tm, sizeof(tm) / sizeof(WCHAR))) {
248             ShowError(hwndDlg, GetLastError());
249             goto end;
250         }
251 
252         if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, dt, tm) == STRSAFE_E_INSUFFICIENT_BUFFER)
253             goto end;
254 
255         s += u;
256         s += L"\r\n";
257 
258         // summary
259 
260         if (!LoadStringW(module, IDS_SCRUB_MSG_SUMMARY, t, sizeof(t) / sizeof(WCHAR))) {
261             ShowError(hwndDlg, GetLastError());
262             goto end;
263         }
264 
265         format_size(bqs2->data_scrubbed, d1, sizeof(d1) / sizeof(WCHAR), FALSE);
266 
267         speed = (float)bqs2->data_scrubbed / ((float)bqs2->duration / 10000000.0f);
268 
269         format_size((UINT64)speed, d2, sizeof(d2) / sizeof(WCHAR), FALSE);
270 
271         if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, d1, bqs2->duration / 10000000, d2) == STRSAFE_E_INSUFFICIENT_BUFFER)
272             goto end;
273 
274         s += u;
275         s += L"\r\n";
276 
277         // recoverable errors
278 
279         if (!LoadStringW(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_RECOVERABLE, t, sizeof(t) / sizeof(WCHAR))) {
280             ShowError(hwndDlg, GetLastError());
281             goto end;
282         }
283 
284         if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, recoverable_errors) == STRSAFE_E_INSUFFICIENT_BUFFER)
285             goto end;
286 
287         s += u;
288         s += L"\r\n";
289 
290         // unrecoverable errors
291 
292         if (!LoadStringW(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE, t, sizeof(t) / sizeof(WCHAR))) {
293             ShowError(hwndDlg, GetLastError());
294             goto end;
295         }
296 
297         if (StringCchPrintfW(u, sizeof(u) / sizeof(WCHAR), t, unrecoverable_errors) == STRSAFE_E_INSUFFICIENT_BUFFER)
298             goto end;
299 
300         s += u;
301         s += L"\r\n";
302     }
303 
304     SetWindowTextW(GetDlgItem(hwndDlg, IDC_SCRUB_INFO), s.c_str());
305 
306 end:
307     if (alloc_bqs2)
308         free(bqs2);
309 }
310 
311 void BtrfsScrub::RefreshScrubDlg(HWND hwndDlg, BOOL first_time) {
312     HANDLE h;
313     btrfs_query_scrub bqs;
314 
315     h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
316                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
317     if (h != INVALID_HANDLE_VALUE) {
318         NTSTATUS Status;
319         IO_STATUS_BLOCK iosb;
320 
321         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_SCRUB, NULL, 0, &bqs, sizeof(btrfs_query_scrub));
322 
323         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
324             ShowNtStatusError(hwndDlg, Status);
325             CloseHandle(h);
326             return;
327         }
328 
329         CloseHandle(h);
330     } else {
331         ShowError(hwndDlg, GetLastError());
332         return;
333     }
334 
335     if (first_time || status != bqs.status || chunks_left != bqs.chunks_left) {
336         WCHAR s[255];
337 
338         if (bqs.status == BTRFS_SCRUB_STOPPED) {
339             EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), TRUE);
340             EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), FALSE);
341             EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), FALSE);
342 
343             if (bqs.error != STATUS_SUCCESS) {
344                 WCHAR t[255];
345 
346                 if (!LoadStringW(module, IDS_SCRUB_FAILED, t, sizeof(t) / sizeof(WCHAR))) {
347                     ShowError(hwndDlg, GetLastError());
348                     return;
349                 }
350 
351                 if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, bqs.error) == STRSAFE_E_INSUFFICIENT_BUFFER)
352                     return;
353             } else {
354                 if (!LoadStringW(module, bqs.total_chunks == 0 ? IDS_NO_SCRUB : IDS_SCRUB_FINISHED, s, sizeof(s) / sizeof(WCHAR))) {
355                     ShowError(hwndDlg, GetLastError());
356                     return;
357                 }
358             }
359         } else {
360             WCHAR t[255];
361             float pc;
362 
363             EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), FALSE);
364             EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), TRUE);
365             EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), TRUE);
366 
367             if (!LoadStringW(module, bqs.status == BTRFS_SCRUB_PAUSED ? IDS_SCRUB_PAUSED : IDS_SCRUB_RUNNING, t, sizeof(t) / sizeof(WCHAR))) {
368                 ShowError(hwndDlg, GetLastError());
369                 return;
370             }
371 
372             pc = ((float)(bqs.total_chunks - bqs.chunks_left) / (float)bqs.total_chunks) * 100.0f;
373 
374             if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, bqs.total_chunks - bqs.chunks_left, bqs.total_chunks, pc) == STRSAFE_E_INSUFFICIENT_BUFFER)
375                 return;
376         }
377 
378         SetDlgItemTextW(hwndDlg, IDC_SCRUB_STATUS, s);
379 
380         if (first_time || status != bqs.status) {
381             EnableWindow(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), bqs.status != BTRFS_SCRUB_STOPPED);
382 
383             if (bqs.status != BTRFS_SCRUB_STOPPED) {
384                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)bqs.total_chunks);
385                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
386 
387                 if (bqs.status == BTRFS_SCRUB_PAUSED)
388                     SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_PAUSED, 0);
389                 else
390                     SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
391             } else {
392                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, 0);
393                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, 0, 0);
394             }
395 
396             chunks_left = bqs.chunks_left;
397         }
398     }
399 
400     if (bqs.status != BTRFS_SCRUB_STOPPED && chunks_left != bqs.chunks_left) {
401         SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
402         chunks_left = bqs.chunks_left;
403     }
404 
405     if (first_time || status != bqs.status || num_errors != bqs.num_errors) {
406         UpdateTextBox(hwndDlg, &bqs);
407 
408         num_errors = bqs.num_errors;
409     }
410 
411     status = bqs.status;
412 }
413 
414 void BtrfsScrub::StartScrub(HWND hwndDlg) {
415     HANDLE h;
416 
417     h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
418                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
419     if (h != INVALID_HANDLE_VALUE) {
420         NTSTATUS Status;
421         IO_STATUS_BLOCK iosb;
422 
423         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_START_SCRUB, NULL, 0, NULL, 0);
424 
425         if (Status == STATUS_DEVICE_NOT_READY) {
426             btrfs_query_balance bqb;
427             NTSTATUS Status2;
428 
429             Status2 = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_BALANCE, NULL, 0, &bqb, sizeof(btrfs_query_balance));
430 
431             if (NT_SUCCESS(Status2) && bqb.status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED)) {
432                 ShowStringError(hwndDlg, IDS_SCRUB_BALANCE_RUNNING);
433                 CloseHandle(h);
434                 return;
435             }
436         }
437 
438         if (!NT_SUCCESS(Status)) {
439             ShowNtStatusError(hwndDlg, Status);
440             CloseHandle(h);
441             return;
442         }
443 
444         RefreshScrubDlg(hwndDlg, TRUE);
445 
446         CloseHandle(h);
447     } else {
448         ShowError(hwndDlg, GetLastError());
449         return;
450     }
451 }
452 
453 void BtrfsScrub::PauseScrub(HWND hwndDlg) {
454     HANDLE h;
455     btrfs_query_scrub bqs;
456 
457     h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
458                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
459     if (h != INVALID_HANDLE_VALUE) {
460         NTSTATUS Status;
461         IO_STATUS_BLOCK iosb;
462 
463         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_QUERY_SCRUB, NULL, 0, &bqs, sizeof(btrfs_query_scrub));
464 
465         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
466             ShowNtStatusError(hwndDlg, Status);
467             CloseHandle(h);
468             return;
469         }
470 
471         if (bqs.status == BTRFS_SCRUB_PAUSED)
472             Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESUME_SCRUB, NULL, 0, NULL, 0);
473         else
474             Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_PAUSE_SCRUB, NULL, 0, NULL, 0);
475 
476         if (!NT_SUCCESS(Status)) {
477             ShowNtStatusError(hwndDlg, Status);
478             CloseHandle(h);
479             return;
480         }
481 
482         CloseHandle(h);
483     } else {
484         ShowError(hwndDlg, GetLastError());
485         return;
486     }
487 }
488 
489 void BtrfsScrub::StopScrub(HWND hwndDlg) {
490     HANDLE h;
491 
492     h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
493                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
494     if (h != INVALID_HANDLE_VALUE) {
495         NTSTATUS Status;
496         IO_STATUS_BLOCK iosb;
497 
498         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_STOP_SCRUB, NULL, 0, NULL, 0);
499 
500         if (!NT_SUCCESS(Status)) {
501             ShowNtStatusError(hwndDlg, Status);
502             CloseHandle(h);
503             return;
504         }
505 
506         CloseHandle(h);
507     } else {
508         ShowError(hwndDlg, GetLastError());
509         return;
510     }
511 }
512 
513 INT_PTR CALLBACK BtrfsScrub::ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
514     switch (uMsg) {
515         case WM_INITDIALOG:
516             RefreshScrubDlg(hwndDlg, TRUE);
517             SetTimer(hwndDlg, 1, 1000, NULL);
518         break;
519 
520         case WM_COMMAND:
521             switch (HIWORD(wParam)) {
522                 case BN_CLICKED:
523                     switch (LOWORD(wParam)) {
524                         case IDOK:
525                         case IDCANCEL:
526                             EndDialog(hwndDlg, 0);
527                         return TRUE;
528 
529                         case IDC_START_SCRUB:
530                             StartScrub(hwndDlg);
531                         return TRUE;
532 
533                         case IDC_PAUSE_SCRUB:
534                             PauseScrub(hwndDlg);
535                         return TRUE;
536 
537                         case IDC_CANCEL_SCRUB:
538                             StopScrub(hwndDlg);
539                         return TRUE;
540                     }
541                 break;
542             }
543         break;
544 
545         case WM_TIMER:
546             RefreshScrubDlg(hwndDlg, FALSE);
547         break;
548     }
549 
550     return FALSE;
551 }
552 
553 static INT_PTR CALLBACK stub_ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
554     BtrfsScrub* bs;
555 
556     if (uMsg == WM_INITDIALOG) {
557         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
558         bs = (BtrfsScrub*)lParam;
559     } else {
560         bs = (BtrfsScrub*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
561     }
562 
563     if (bs)
564         return bs->ScrubDlgProc(hwndDlg, uMsg, wParam, lParam);
565     else
566         return FALSE;
567 }
568 
569 void CALLBACK ShowScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
570     HANDLE token;
571     TOKEN_PRIVILEGES tp;
572     LUID luid;
573     BtrfsScrub* scrub;
574 
575     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
576         ShowError(hwnd, GetLastError());
577         return;
578     }
579 
580     if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
581         ShowError(hwnd, GetLastError());
582         goto end;
583     }
584 
585     tp.PrivilegeCount = 1;
586     tp.Privileges[0].Luid = luid;
587     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
588 
589     if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
590         ShowError(hwnd, GetLastError());
591         goto end;
592     }
593 
594     set_dpi_aware();
595 
596     scrub = new BtrfsScrub(lpszCmdLine);
597 
598     DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SCRUB), hwnd, stub_ScrubDlgProc, (LPARAM)scrub);
599 
600     delete scrub;
601 
602 end:
603     CloseHandle(token);
604 }
605 
606 void CALLBACK StartScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
607     LPWSTR* args;
608     int num_args;
609 
610     args = CommandLineToArgvW(lpszCmdLine, &num_args);
611 
612     if (!args)
613         return;
614 
615     if (num_args >= 1) {
616         HANDLE h, token;
617         LUID luid;
618         TOKEN_PRIVILEGES tp;
619 
620         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
621             goto end;
622 
623         if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
624             CloseHandle(token);
625             goto end;
626         }
627 
628         tp.PrivilegeCount = 1;
629         tp.Privileges[0].Luid = luid;
630         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
631 
632         if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
633             CloseHandle(token);
634             goto end;
635         }
636 
637         CloseHandle(token);
638 
639         h = CreateFileW(args[0], FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
640                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
641         if (h != INVALID_HANDLE_VALUE) {
642             IO_STATUS_BLOCK iosb;
643 
644             NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_START_SCRUB, NULL, 0, NULL, 0);
645 
646             CloseHandle(h);
647         }
648     }
649 
650 end:
651     LocalFree(args);
652 }
653 
654 void CALLBACK StopScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
655     LPWSTR* args;
656     int num_args;
657 
658     args = CommandLineToArgvW(lpszCmdLine, &num_args);
659 
660     if (!args)
661         return;
662 
663     if (num_args >= 1) {
664         HANDLE h, token;
665         LUID luid;
666         TOKEN_PRIVILEGES tp;
667 
668         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
669             goto end;
670 
671         if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid))
672             goto end;
673 
674         tp.PrivilegeCount = 1;
675         tp.Privileges[0].Luid = luid;
676         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
677 
678         if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
679             goto end;
680 
681         CloseHandle(token);
682 
683         h = CreateFileW(args[0], FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
684                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
685         if (h != INVALID_HANDLE_VALUE) {
686             IO_STATUS_BLOCK iosb;
687 
688             NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_STOP_SCRUB, NULL, 0, NULL, 0);
689 
690             CloseHandle(h);
691         }
692     }
693 
694 end:
695     LocalFree(args);
696 }
697