xref: /reactos/dll/shellext/shellbtrfs/scrub.cpp (revision 09dde2cf)
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 
41 #define NO_SHLWAPI_STRFCNS
42 #include <shlwapi.h>
43 #include <uxtheme.h>
44 
45 void BtrfsScrub::UpdateTextBox(HWND hwndDlg, btrfs_query_scrub* bqs) {
46     btrfs_query_scrub* bqs2 = nullptr;
47     bool alloc_bqs2 = false;
48     NTSTATUS Status;
49     wstring s, t, u;
50     WCHAR dt[255], tm[255];
51     FILETIME filetime;
52     SYSTEMTIME systime;
53     uint64_t recoverable_errors = 0, unrecoverable_errors = 0;
54 
55     try {
56         if (bqs->num_errors > 0) {
57             win_handle h;
58             IO_STATUS_BLOCK iosb;
59             ULONG len;
60 
61             h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
62                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
63             if (h == INVALID_HANDLE_VALUE)
64                 throw last_error(GetLastError());
65 
66             len = 0;
67 
68             try {
69                 do {
70                     len += 1024;
71 
72                     if (bqs2) {
73                         free(bqs2);
74                         bqs2 = nullptr;
75                     }
76 
77                     bqs2 = (btrfs_query_scrub*)malloc(len);
78 
79                     Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, bqs2, len);
80 
81                     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
82                         throw ntstatus_error(Status);
83                 } while (Status == STATUS_BUFFER_OVERFLOW);
84             } catch (...) {
85                 if (bqs2)
86                     free(bqs2);
87 
88                 throw;
89             }
90 
91             alloc_bqs2 = true;
92         } else
93             bqs2 = bqs;
94 
95         // "scrub started"
96         if (bqs2->start_time.QuadPart > 0) {
97             filetime.dwLowDateTime = bqs2->start_time.LowPart;
98             filetime.dwHighDateTime = bqs2->start_time.HighPart;
99 
100             if (!FileTimeToSystemTime(&filetime, &systime))
101                 throw last_error(GetLastError());
102 
103             if (!SystemTimeToTzSpecificLocalTime(nullptr, &systime, &systime))
104                 throw last_error(GetLastError());
105 
106             if (!load_string(module, IDS_SCRUB_MSG_STARTED, t))
107                 throw last_error(GetLastError());
108 
109             if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, nullptr, dt, sizeof(dt) / sizeof(WCHAR)))
110                 throw last_error(GetLastError());
111 
112             if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, nullptr, tm, sizeof(tm) / sizeof(WCHAR)))
113                 throw last_error(GetLastError());
114 
115             wstring_sprintf(u, t, dt, tm);
116 
117             s += u;
118             s += L"\r\n";
119         }
120 
121         // errors
122         if (bqs2->num_errors > 0) {
123             btrfs_scrub_error* bse = &bqs2->errors;
124 
125             do {
126                 if (bse->recovered)
127                     recoverable_errors++;
128                 else
129                     unrecoverable_errors++;
130 
131                 if (bse->parity) {
132                     if (!load_string(module, IDS_SCRUB_MSG_RECOVERABLE_PARITY, t))
133                         throw last_error(GetLastError());
134 
135                     wstring_sprintf(u, t, bse->address, bse->device);
136                 } else if (bse->is_metadata) {
137                     int message;
138 
139                     if (bse->recovered)
140                         message = IDS_SCRUB_MSG_RECOVERABLE_METADATA;
141                     else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0)
142                         message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA;
143                     else
144                         message = IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM;
145 
146                     if (!load_string(module, message, t))
147                         throw last_error(GetLastError());
148 
149                     if (bse->recovered)
150                         wstring_sprintf(u, t, bse->address, bse->device);
151                     else if (bse->metadata.firstitem.obj_id == 0 && bse->metadata.firstitem.obj_type == 0 && bse->metadata.firstitem.offset == 0)
152                         wstring_sprintf(u, t, bse->address, bse->device, bse->metadata.root, bse->metadata.level);
153                     else
154                         wstring_sprintf(u, t, bse->address, bse->device, bse->metadata.root, bse->metadata.level, bse->metadata.firstitem.obj_id,
155                                         bse->metadata.firstitem.obj_type, bse->metadata.firstitem.offset);
156                 } else {
157                     int message;
158 
159                     if (bse->recovered)
160                         message = IDS_SCRUB_MSG_RECOVERABLE_DATA;
161                     else if (bse->data.subvol != 0)
162                         message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL;
163                     else
164                         message = IDS_SCRUB_MSG_UNRECOVERABLE_DATA;
165 
166                     if (!load_string(module, message, t))
167                         throw last_error(GetLastError());
168 
169                     if (bse->recovered)
170                         wstring_sprintf(u, t, bse->address, bse->device);
171                     else if (bse->data.subvol != 0)
172                         wstring_sprintf(u, t, bse->address, bse->device, bse->data.subvol,
173                             bse->data.filename_length / sizeof(WCHAR), bse->data.filename, bse->data.offset);
174                     else
175                         wstring_sprintf(u, t, bse->address, bse->device, bse->data.filename_length / sizeof(WCHAR),
176                             bse->data.filename, bse->data.offset);
177                 }
178 
179                 s += u;
180                 s += L"\r\n";
181 
182                 if (bse->next_entry == 0)
183                     break;
184                 else
185                     bse = (btrfs_scrub_error*)((uint8_t*)bse + bse->next_entry);
186             } while (true);
187         }
188 
189         if (bqs2->finish_time.QuadPart > 0) {
190             wstring d1, d2;
191             float speed;
192 
193             // "scrub finished"
194 
195             filetime.dwLowDateTime = bqs2->finish_time.LowPart;
196             filetime.dwHighDateTime = bqs2->finish_time.HighPart;
197 
198             if (!FileTimeToSystemTime(&filetime, &systime))
199                 throw last_error(GetLastError());
200 
201             if (!SystemTimeToTzSpecificLocalTime(nullptr, &systime, &systime))
202                 throw last_error(GetLastError());
203 
204             if (!load_string(module, IDS_SCRUB_MSG_FINISHED, t))
205                 throw last_error(GetLastError());
206 
207             if (!GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, nullptr, dt, sizeof(dt) / sizeof(WCHAR)))
208                 throw last_error(GetLastError());
209 
210             if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, nullptr, tm, sizeof(tm) / sizeof(WCHAR)))
211                 throw last_error(GetLastError());
212 
213             wstring_sprintf(u, t, dt, tm);
214 
215             s += u;
216             s += L"\r\n";
217 
218             // summary
219 
220             if (!load_string(module, IDS_SCRUB_MSG_SUMMARY, t))
221                 throw last_error(GetLastError());
222 
223             format_size(bqs2->data_scrubbed, d1, false);
224 
225             speed = (float)bqs2->data_scrubbed / ((float)bqs2->duration / 10000000.0f);
226 
227             format_size((uint64_t)speed, d2, false);
228 
229             wstring_sprintf(u, t, d1.c_str(), bqs2->duration / 10000000, d2.c_str());
230 
231             s += u;
232             s += L"\r\n";
233 
234             // recoverable errors
235 
236             if (!load_string(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_RECOVERABLE, t))
237                 throw last_error(GetLastError());
238 
239             wstring_sprintf(u, t, recoverable_errors);
240 
241             s += u;
242             s += L"\r\n";
243 
244             // unrecoverable errors
245 
246             if (!load_string(module, IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE, t))
247                 throw last_error(GetLastError());
248 
249             wstring_sprintf(u, t, unrecoverable_errors);
250 
251             s += u;
252             s += L"\r\n";
253         }
254 
255         SetWindowTextW(GetDlgItem(hwndDlg, IDC_SCRUB_INFO), s.c_str());
256     } catch (...) {
257         if (alloc_bqs2)
258             free(bqs2);
259 
260         throw;
261     }
262 
263     if (alloc_bqs2)
264         free(bqs2);
265 }
266 
267 void BtrfsScrub::RefreshScrubDlg(HWND hwndDlg, bool first_time) {
268     btrfs_query_scrub bqs;
269 
270     {
271         win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
272                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
273         if (h != INVALID_HANDLE_VALUE) {
274             NTSTATUS Status;
275             IO_STATUS_BLOCK iosb;
276 
277             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, &bqs, sizeof(btrfs_query_scrub));
278 
279             if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
280                 throw ntstatus_error(Status);
281         } else
282             throw last_error(GetLastError());
283     }
284 
285     if (first_time || status != bqs.status || chunks_left != bqs.chunks_left) {
286         wstring s;
287 
288         if (bqs.status == BTRFS_SCRUB_STOPPED) {
289             EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), true);
290             EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), false);
291             EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), false);
292 
293             if (bqs.error != STATUS_SUCCESS) {
294                 wstring t;
295 
296                 if (!load_string(module, IDS_SCRUB_FAILED, t))
297                     throw last_error(GetLastError());
298 
299                 wstring_sprintf(s, t, bqs.error);
300             } else {
301                 if (!load_string(module, bqs.total_chunks == 0 ? IDS_NO_SCRUB : IDS_SCRUB_FINISHED, s))
302                     throw last_error(GetLastError());
303             }
304         } else {
305             wstring t;
306             float pc;
307 
308             EnableWindow(GetDlgItem(hwndDlg, IDC_START_SCRUB), false);
309             EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_SCRUB), true);
310             EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_SCRUB), true);
311 
312             if (!load_string(module, bqs.status == BTRFS_SCRUB_PAUSED ? IDS_SCRUB_PAUSED : IDS_SCRUB_RUNNING, t))
313                 throw last_error(GetLastError());
314 
315             pc = ((float)(bqs.total_chunks - bqs.chunks_left) / (float)bqs.total_chunks) * 100.0f;
316 
317             wstring_sprintf(s, t, bqs.total_chunks - bqs.chunks_left, bqs.total_chunks, pc);
318         }
319 
320         SetDlgItemTextW(hwndDlg, IDC_SCRUB_STATUS, s.c_str());
321 
322         if (first_time || status != bqs.status) {
323             EnableWindow(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), bqs.status != BTRFS_SCRUB_STOPPED);
324 
325             if (bqs.status != BTRFS_SCRUB_STOPPED) {
326                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)bqs.total_chunks);
327                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
328 
329                 if (bqs.status == BTRFS_SCRUB_PAUSED)
330                     SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_PAUSED, 0);
331                 else
332                     SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
333             } else {
334                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETRANGE32, 0, 0);
335                 SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, 0, 0);
336             }
337 
338             chunks_left = bqs.chunks_left;
339         }
340     }
341 
342     if (bqs.status != BTRFS_SCRUB_STOPPED && chunks_left != bqs.chunks_left) {
343         SendMessageW(GetDlgItem(hwndDlg, IDC_SCRUB_PROGRESS), PBM_SETPOS, (WPARAM)(bqs.total_chunks - bqs.chunks_left), 0);
344         chunks_left = bqs.chunks_left;
345     }
346 
347     if (first_time || status != bqs.status || num_errors != bqs.num_errors) {
348         UpdateTextBox(hwndDlg, &bqs);
349 
350         num_errors = bqs.num_errors;
351     }
352 
353     status = bqs.status;
354 }
355 
356 void BtrfsScrub::StartScrub(HWND hwndDlg) {
357     win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
358                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
359 
360     if (h != INVALID_HANDLE_VALUE) {
361         NTSTATUS Status;
362         IO_STATUS_BLOCK iosb;
363 
364         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_START_SCRUB, nullptr, 0, nullptr, 0);
365 
366         if (Status == STATUS_DEVICE_NOT_READY) {
367             btrfs_query_balance bqb;
368             NTSTATUS Status2;
369 
370             Status2 = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb, sizeof(btrfs_query_balance));
371 
372             if (NT_SUCCESS(Status2) && bqb.status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))
373                 throw string_error(IDS_SCRUB_BALANCE_RUNNING);
374         }
375 
376         if (!NT_SUCCESS(Status))
377             throw ntstatus_error(Status);
378 
379         RefreshScrubDlg(hwndDlg, true);
380     } else
381         throw last_error(GetLastError());
382 }
383 
384 void BtrfsScrub::PauseScrub(HWND hwndDlg) {
385     btrfs_query_scrub bqs;
386 
387     win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
388                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
389 
390     if (h != INVALID_HANDLE_VALUE) {
391         NTSTATUS Status;
392         IO_STATUS_BLOCK iosb;
393 
394         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, &bqs, sizeof(btrfs_query_scrub));
395 
396         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
397             throw ntstatus_error(Status);
398 
399         if (bqs.status == BTRFS_SCRUB_PAUSED)
400             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESUME_SCRUB, nullptr, 0, nullptr, 0);
401         else
402             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_PAUSE_SCRUB, nullptr, 0, nullptr, 0);
403 
404         if (!NT_SUCCESS(Status))
405             throw ntstatus_error(Status);
406     } else
407         throw last_error(GetLastError());
408 }
409 
410 void BtrfsScrub::StopScrub(HWND hwndDlg) {
411     win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
412                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
413 
414     if (h != INVALID_HANDLE_VALUE) {
415         NTSTATUS Status;
416         IO_STATUS_BLOCK iosb;
417 
418         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_STOP_SCRUB, nullptr, 0, nullptr, 0);
419 
420         if (!NT_SUCCESS(Status))
421             throw ntstatus_error(Status);
422     } else
423         throw last_error(GetLastError());
424 }
425 
426 INT_PTR CALLBACK BtrfsScrub::ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
427     try {
428         switch (uMsg) {
429             case WM_INITDIALOG:
430                 RefreshScrubDlg(hwndDlg, true);
431                 SetTimer(hwndDlg, 1, 1000, nullptr);
432             break;
433 
434             case WM_COMMAND:
435                 switch (HIWORD(wParam)) {
436                     case BN_CLICKED:
437                         switch (LOWORD(wParam)) {
438                             case IDOK:
439                             case IDCANCEL:
440                                 EndDialog(hwndDlg, 0);
441                             return true;
442 
443                             case IDC_START_SCRUB:
444                                 StartScrub(hwndDlg);
445                             return true;
446 
447                             case IDC_PAUSE_SCRUB:
448                                 PauseScrub(hwndDlg);
449                             return true;
450 
451                             case IDC_CANCEL_SCRUB:
452                                 StopScrub(hwndDlg);
453                             return true;
454                         }
455                     break;
456                 }
457             break;
458 
459             case WM_TIMER:
460                 RefreshScrubDlg(hwndDlg, false);
461             break;
462         }
463     } catch (const exception& e) {
464         error_message(hwndDlg, e.what());
465     }
466 
467     return false;
468 }
469 
470 static INT_PTR CALLBACK stub_ScrubDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
471     BtrfsScrub* bs;
472 
473     if (uMsg == WM_INITDIALOG) {
474         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
475         bs = (BtrfsScrub*)lParam;
476     } else {
477         bs = (BtrfsScrub*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
478     }
479 
480     if (bs)
481         return bs->ScrubDlgProc(hwndDlg, uMsg, wParam, lParam);
482     else
483         return false;
484 }
485 
486 extern "C" void CALLBACK ShowScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
487     try {
488         win_handle token;
489         TOKEN_PRIVILEGES tp;
490         LUID luid;
491 
492         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
493             throw last_error(GetLastError());
494 
495         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
496             throw last_error(GetLastError());
497 
498         tp.PrivilegeCount = 1;
499         tp.Privileges[0].Luid = luid;
500         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
501 
502         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
503             throw last_error(GetLastError());
504 
505         set_dpi_aware();
506 
507         BtrfsScrub scrub(lpszCmdLine);
508 
509         DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SCRUB), hwnd, stub_ScrubDlgProc, (LPARAM)&scrub);
510     } catch (const exception& e) {
511         error_message(hwnd, e.what());
512     }
513 }
514 
515 extern "C" void CALLBACK StartScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
516     vector<wstring> args;
517 
518     command_line_to_args(lpszCmdLine, args);
519 
520     if (args.size() >= 1) {
521         LUID luid;
522         TOKEN_PRIVILEGES tp;
523 
524         {
525             win_handle token;
526 
527             if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
528                 return;
529 
530             if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
531                 return;
532 
533             tp.PrivilegeCount = 1;
534             tp.Privileges[0].Luid = luid;
535             tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
536 
537             if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
538                 return;
539         }
540 
541         win_handle h = CreateFileW(args[0].c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
542                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
543         if (h != INVALID_HANDLE_VALUE) {
544             IO_STATUS_BLOCK iosb;
545 
546             NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_START_SCRUB, nullptr, 0, nullptr, 0);
547         }
548     }
549 }
550 
551 extern "C" void CALLBACK StopScrubW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
552     vector<wstring> args;
553 
554     command_line_to_args(lpszCmdLine, args);
555 
556     if (args.size() >= 1) {
557         LUID luid;
558         TOKEN_PRIVILEGES tp;
559 
560         {
561             win_handle token;
562 
563             if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
564                 return;
565 
566             if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
567                 return;
568 
569             tp.PrivilegeCount = 1;
570             tp.Privileges[0].Luid = luid;
571             tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
572 
573             if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
574                 return;
575         }
576 
577         win_handle h = CreateFileW(args[0].c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
578                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
579         if (h != INVALID_HANDLE_VALUE) {
580             IO_STATUS_BLOCK iosb;
581 
582             NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_STOP_SCRUB, nullptr, 0, nullptr, 0);
583         }
584     }
585 }
586