1 /* Copyright (c) Mark Harmstone 2017
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
22 #include "../btrfsioctl.h"
24 #include "btrfsioctl.h"
33 #define WIN32_NO_STATUS
37 #include <ndk/iofuncs.h>
38 #include <ndk/iotypes.h>
42 #define NO_SHLWAPI_STRFCNS
46 void BtrfsScrub::UpdateTextBox(HWND hwndDlg
, btrfs_query_scrub
* bqs
) {
47 btrfs_query_scrub
* bqs2
= NULL
;
48 BOOL alloc_bqs2
= FALSE
;
51 WCHAR t
[255], u
[255], dt
[255], tm
[255];
54 UINT64 recoverable_errors
= 0, unrecoverable_errors
= 0;
56 if (bqs
->num_errors
> 0) {
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());
76 bqs2
= (btrfs_query_scrub
*)malloc(len
);
78 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_QUERY_SCRUB
, NULL
, 0, bqs2
, len
);
80 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
81 ShowNtStatusError(hwndDlg
, Status
);
86 } while (Status
== STATUS_BUFFER_OVERFLOW
);
97 if (bqs2
->start_time
.QuadPart
> 0) {
98 filetime
.dwLowDateTime
= bqs2
->start_time
.LowPart
;
99 filetime
.dwHighDateTime
= bqs2
->start_time
.HighPart
;
101 if (!FileTimeToSystemTime(&filetime
, &systime
)) {
102 ShowError(hwndDlg
, GetLastError());
106 if (!SystemTimeToTzSpecificLocalTime(NULL
, &systime
, &systime
)) {
107 ShowError(hwndDlg
, GetLastError());
111 if (!LoadStringW(module
, IDS_SCRUB_MSG_STARTED
, t
, sizeof(t
) / sizeof(WCHAR
))) {
112 ShowError(hwndDlg
, GetLastError());
116 if (!GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &systime
, NULL
, dt
, sizeof(dt
) / sizeof(WCHAR
))) {
117 ShowError(hwndDlg
, GetLastError());
121 if (!GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &systime
, NULL
, tm
, sizeof(tm
) / sizeof(WCHAR
))) {
122 ShowError(hwndDlg
, GetLastError());
126 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, dt
, tm
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
134 if (bqs2
->num_errors
> 0) {
135 btrfs_scrub_error
* bse
= &bqs2
->errors
;
139 recoverable_errors
++;
141 unrecoverable_errors
++;
144 if (!LoadStringW(module
, IDS_SCRUB_MSG_RECOVERABLE_PARITY
, t
, sizeof(t
) / sizeof(WCHAR
))) {
145 ShowError(hwndDlg
, GetLastError());
149 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, bse
->address
, bse
->device
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
151 } else if (bse
->is_metadata
) {
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
;
159 message
= IDS_SCRUB_MSG_UNRECOVERABLE_METADATA_FIRSTITEM
;
161 if (!LoadStringW(module
, message
, t
, sizeof(t
) / sizeof(WCHAR
))) {
162 ShowError(hwndDlg
, GetLastError());
166 if (bse
->recovered
) {
167 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, bse
->address
, bse
->device
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
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
)
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
)
183 message
= IDS_SCRUB_MSG_RECOVERABLE_DATA
;
184 else if (bse
->data
.subvol
!= 0)
185 message
= IDS_SCRUB_MSG_UNRECOVERABLE_DATA_SUBVOL
;
187 message
= IDS_SCRUB_MSG_UNRECOVERABLE_DATA
;
189 if (!LoadStringW(module
, message
, t
, sizeof(t
) / sizeof(WCHAR
))) {
190 ShowError(hwndDlg
, GetLastError());
194 if (bse
->recovered
) {
195 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, bse
->address
, bse
->device
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
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
)
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
)
211 if (bse
->next_entry
== 0)
214 bse
= (btrfs_scrub_error
*)((UINT8
*)bse
+ bse
->next_entry
);
218 if (bqs2
->finish_time
.QuadPart
> 0) {
219 WCHAR d1
[255], d2
[255];
224 filetime
.dwLowDateTime
= bqs2
->finish_time
.LowPart
;
225 filetime
.dwHighDateTime
= bqs2
->finish_time
.HighPart
;
227 if (!FileTimeToSystemTime(&filetime
, &systime
)) {
228 ShowError(hwndDlg
, GetLastError());
232 if (!SystemTimeToTzSpecificLocalTime(NULL
, &systime
, &systime
)) {
233 ShowError(hwndDlg
, GetLastError());
237 if (!LoadStringW(module
, IDS_SCRUB_MSG_FINISHED
, t
, sizeof(t
) / sizeof(WCHAR
))) {
238 ShowError(hwndDlg
, GetLastError());
242 if (!GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &systime
, NULL
, dt
, sizeof(dt
) / sizeof(WCHAR
))) {
243 ShowError(hwndDlg
, GetLastError());
247 if (!GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &systime
, NULL
, tm
, sizeof(tm
) / sizeof(WCHAR
))) {
248 ShowError(hwndDlg
, GetLastError());
252 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, dt
, tm
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
260 if (!LoadStringW(module
, IDS_SCRUB_MSG_SUMMARY
, t
, sizeof(t
) / sizeof(WCHAR
))) {
261 ShowError(hwndDlg
, GetLastError());
265 format_size(bqs2
->data_scrubbed
, d1
, sizeof(d1
) / sizeof(WCHAR
), FALSE
);
267 speed
= (float)bqs2
->data_scrubbed
/ ((float)bqs2
->duration
/ 10000000.0f
);
269 format_size((UINT64
)speed
, d2
, sizeof(d2
) / sizeof(WCHAR
), FALSE
);
271 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, d1
, bqs2
->duration
/ 10000000, d2
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
277 // recoverable errors
279 if (!LoadStringW(module
, IDS_SCRUB_MSG_SUMMARY_ERRORS_RECOVERABLE
, t
, sizeof(t
) / sizeof(WCHAR
))) {
280 ShowError(hwndDlg
, GetLastError());
284 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, recoverable_errors
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
290 // unrecoverable errors
292 if (!LoadStringW(module
, IDS_SCRUB_MSG_SUMMARY_ERRORS_UNRECOVERABLE
, t
, sizeof(t
) / sizeof(WCHAR
))) {
293 ShowError(hwndDlg
, GetLastError());
297 if (StringCchPrintfW(u
, sizeof(u
) / sizeof(WCHAR
), t
, unrecoverable_errors
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
304 SetWindowTextW(GetDlgItem(hwndDlg
, IDC_SCRUB_INFO
), s
.c_str());
311 void BtrfsScrub::RefreshScrubDlg(HWND hwndDlg
, BOOL first_time
) {
313 btrfs_query_scrub bqs
;
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
) {
319 IO_STATUS_BLOCK iosb
;
321 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_QUERY_SCRUB
, NULL
, 0, &bqs
, sizeof(btrfs_query_scrub
));
323 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
324 ShowNtStatusError(hwndDlg
, Status
);
331 ShowError(hwndDlg
, GetLastError());
335 if (first_time
|| status
!= bqs
.status
|| chunks_left
!= bqs
.chunks_left
) {
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
);
343 if (bqs
.error
!= STATUS_SUCCESS
) {
346 if (!LoadStringW(module
, IDS_SCRUB_FAILED
, t
, sizeof(t
) / sizeof(WCHAR
))) {
347 ShowError(hwndDlg
, GetLastError());
351 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), t
, bqs
.error
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
354 if (!LoadStringW(module
, bqs
.total_chunks
== 0 ? IDS_NO_SCRUB
: IDS_SCRUB_FINISHED
, s
, sizeof(s
) / sizeof(WCHAR
))) {
355 ShowError(hwndDlg
, GetLastError());
363 EnableWindow(GetDlgItem(hwndDlg
, IDC_START_SCRUB
), FALSE
);
364 EnableWindow(GetDlgItem(hwndDlg
, IDC_PAUSE_SCRUB
), TRUE
);
365 EnableWindow(GetDlgItem(hwndDlg
, IDC_CANCEL_SCRUB
), TRUE
);
367 if (!LoadStringW(module
, bqs
.status
== BTRFS_SCRUB_PAUSED
? IDS_SCRUB_PAUSED
: IDS_SCRUB_RUNNING
, t
, sizeof(t
) / sizeof(WCHAR
))) {
368 ShowError(hwndDlg
, GetLastError());
372 pc
= ((float)(bqs
.total_chunks
- bqs
.chunks_left
) / (float)bqs
.total_chunks
) * 100.0f
;
374 if (StringCchPrintfW(s
, sizeof(s
) / sizeof(WCHAR
), t
, bqs
.total_chunks
- bqs
.chunks_left
, bqs
.total_chunks
, pc
) == STRSAFE_E_INSUFFICIENT_BUFFER
)
378 SetDlgItemTextW(hwndDlg
, IDC_SCRUB_STATUS
, s
);
380 if (first_time
|| status
!= bqs
.status
) {
381 EnableWindow(GetDlgItem(hwndDlg
, IDC_SCRUB_PROGRESS
), bqs
.status
!= BTRFS_SCRUB_STOPPED
);
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);
387 if (bqs
.status
== BTRFS_SCRUB_PAUSED
)
388 SendMessageW(GetDlgItem(hwndDlg
, IDC_SCRUB_PROGRESS
), PBM_SETSTATE
, PBST_PAUSED
, 0);
390 SendMessageW(GetDlgItem(hwndDlg
, IDC_SCRUB_PROGRESS
), PBM_SETSTATE
, PBST_NORMAL
, 0);
392 SendMessageW(GetDlgItem(hwndDlg
, IDC_SCRUB_PROGRESS
), PBM_SETRANGE32
, 0, 0);
393 SendMessageW(GetDlgItem(hwndDlg
, IDC_SCRUB_PROGRESS
), PBM_SETPOS
, 0, 0);
396 chunks_left
= bqs
.chunks_left
;
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
;
405 if (first_time
|| status
!= bqs
.status
|| num_errors
!= bqs
.num_errors
) {
406 UpdateTextBox(hwndDlg
, &bqs
);
408 num_errors
= bqs
.num_errors
;
414 void BtrfsScrub::StartScrub(HWND hwndDlg
) {
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
) {
421 IO_STATUS_BLOCK iosb
;
423 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_START_SCRUB
, NULL
, 0, NULL
, 0);
425 if (Status
== STATUS_DEVICE_NOT_READY
) {
426 btrfs_query_balance bqb
;
429 Status2
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_QUERY_BALANCE
, NULL
, 0, &bqb
, sizeof(btrfs_query_balance
));
431 if (NT_SUCCESS(Status2
) && bqb
.status
& (BTRFS_BALANCE_RUNNING
| BTRFS_BALANCE_PAUSED
)) {
432 ShowStringError(hwndDlg
, IDS_SCRUB_BALANCE_RUNNING
);
438 if (!NT_SUCCESS(Status
)) {
439 ShowNtStatusError(hwndDlg
, Status
);
444 RefreshScrubDlg(hwndDlg
, TRUE
);
448 ShowError(hwndDlg
, GetLastError());
453 void BtrfsScrub::PauseScrub(HWND hwndDlg
) {
455 btrfs_query_scrub bqs
;
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
) {
461 IO_STATUS_BLOCK iosb
;
463 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_QUERY_SCRUB
, NULL
, 0, &bqs
, sizeof(btrfs_query_scrub
));
465 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
466 ShowNtStatusError(hwndDlg
, Status
);
471 if (bqs
.status
== BTRFS_SCRUB_PAUSED
)
472 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_RESUME_SCRUB
, NULL
, 0, NULL
, 0);
474 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_PAUSE_SCRUB
, NULL
, 0, NULL
, 0);
476 if (!NT_SUCCESS(Status
)) {
477 ShowNtStatusError(hwndDlg
, Status
);
484 ShowError(hwndDlg
, GetLastError());
489 void BtrfsScrub::StopScrub(HWND hwndDlg
) {
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
) {
496 IO_STATUS_BLOCK iosb
;
498 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_STOP_SCRUB
, NULL
, 0, NULL
, 0);
500 if (!NT_SUCCESS(Status
)) {
501 ShowNtStatusError(hwndDlg
, Status
);
508 ShowError(hwndDlg
, GetLastError());
513 INT_PTR CALLBACK
BtrfsScrub::ScrubDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
516 RefreshScrubDlg(hwndDlg
, TRUE
);
517 SetTimer(hwndDlg
, 1, 1000, NULL
);
521 switch (HIWORD(wParam
)) {
523 switch (LOWORD(wParam
)) {
526 EndDialog(hwndDlg
, 0);
529 case IDC_START_SCRUB
:
533 case IDC_PAUSE_SCRUB
:
537 case IDC_CANCEL_SCRUB
:
546 RefreshScrubDlg(hwndDlg
, FALSE
);
553 static INT_PTR CALLBACK
stub_ScrubDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
556 if (uMsg
== WM_INITDIALOG
) {
557 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
558 bs
= (BtrfsScrub
*)lParam
;
560 bs
= (BtrfsScrub
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
564 return bs
->ScrubDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
569 void CALLBACK
ShowScrubW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
575 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
)) {
576 ShowError(hwnd
, GetLastError());
580 if (!LookupPrivilegeValueW(NULL
, L
"SeManageVolumePrivilege", &luid
)) {
581 ShowError(hwnd
, GetLastError());
585 tp
.PrivilegeCount
= 1;
586 tp
.Privileges
[0].Luid
= luid
;
587 tp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
589 if (!AdjustTokenPrivileges(token
, FALSE
, &tp
, sizeof(TOKEN_PRIVILEGES
), NULL
, NULL
)) {
590 ShowError(hwnd
, GetLastError());
596 scrub
= new BtrfsScrub(lpszCmdLine
);
598 DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_SCRUB
), hwnd
, stub_ScrubDlgProc
, (LPARAM
)scrub
);
606 void CALLBACK
StartScrubW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
610 args
= CommandLineToArgvW(lpszCmdLine
, &num_args
);
620 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
))
623 if (!LookupPrivilegeValueW(NULL
, L
"SeManageVolumePrivilege", &luid
))
626 tp
.PrivilegeCount
= 1;
627 tp
.Privileges
[0].Luid
= luid
;
628 tp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
630 if (!AdjustTokenPrivileges(token
, FALSE
, &tp
, sizeof(TOKEN_PRIVILEGES
), NULL
, NULL
))
635 h
= CreateFileW(args
[0], FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
636 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, NULL
);
637 if (h
!= INVALID_HANDLE_VALUE
) {
638 IO_STATUS_BLOCK iosb
;
640 NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_START_SCRUB
, NULL
, 0, NULL
, 0);
650 void CALLBACK
StopScrubW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
654 args
= CommandLineToArgvW(lpszCmdLine
, &num_args
);
664 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
))
667 if (!LookupPrivilegeValueW(NULL
, L
"SeManageVolumePrivilege", &luid
))
670 tp
.PrivilegeCount
= 1;
671 tp
.Privileges
[0].Luid
= luid
;
672 tp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
674 if (!AdjustTokenPrivileges(token
, FALSE
, &tp
, sizeof(TOKEN_PRIVILEGES
), NULL
, NULL
))
679 h
= CreateFileW(args
[0], FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
680 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
, NULL
);
681 if (h
!= INVALID_HANDLE_VALUE
) {
682 IO_STATUS_BLOCK iosb
;
684 NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_STOP_SCRUB
, NULL
, 0, NULL
, 0);