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