Reactos
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
45void 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
267void 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
356void 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
384void 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
410void 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
426INT_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
470static 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
486extern "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
515extern "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
551extern "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}