1 /* Copyright (c) Mark Harmstone 2016-17 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 "balance.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 static uint64_t convtypes2[] = { BLOCK_FLAG_SINGLE, BLOCK_FLAG_DUPLICATE, BLOCK_FLAG_RAID0, BLOCK_FLAG_RAID1, 46 BLOCK_FLAG_RAID5, BLOCK_FLAG_RAID1C3, BLOCK_FLAG_RAID6, BLOCK_FLAG_RAID10, 47 BLOCK_FLAG_RAID1C4 }; 48 49 static WCHAR hex_digit(uint8_t u) { 50 if (u >= 0xa && u <= 0xf) 51 return (uint8_t)(u - 0xa + 'a'); 52 else 53 return (uint8_t)(u + '0'); 54 } 55 56 static void serialize(void* data, ULONG len, WCHAR* s) { 57 uint8_t* d; 58 59 d = (uint8_t*)data; 60 61 while (true) { 62 *s = hex_digit((uint8_t)(*d >> 4)); s++; 63 *s = hex_digit(*d & 0xf); s++; 64 65 d++; 66 len--; 67 68 if (len == 0) { 69 *s = 0; 70 return; 71 } 72 } 73 } 74 75 void BtrfsBalance::StartBalance(HWND hwndDlg) { 76 wstring t; 77 WCHAR modfn[MAX_PATH], u[600]; 78 SHELLEXECUTEINFOW sei; 79 btrfs_start_balance bsb; 80 81 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 82 83 #ifndef __REACTOS__ 84 t = L"\""s + modfn + L"\",StartBalance "s + fn + L" "s; 85 #else 86 t = wstring(L"\"") + modfn + wstring(L"\",StartBalance ") + fn + wstring(L" "); 87 #endif 88 89 RtlCopyMemory(&bsb.opts[0], &data_opts, sizeof(btrfs_balance_opts)); 90 RtlCopyMemory(&bsb.opts[1], &metadata_opts, sizeof(btrfs_balance_opts)); 91 RtlCopyMemory(&bsb.opts[2], &system_opts, sizeof(btrfs_balance_opts)); 92 93 if (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED) 94 bsb.opts[0].flags |= BTRFS_BALANCE_OPTS_ENABLED; 95 else 96 bsb.opts[0].flags &= ~BTRFS_BALANCE_OPTS_ENABLED; 97 98 if (IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED) 99 bsb.opts[1].flags |= BTRFS_BALANCE_OPTS_ENABLED; 100 else 101 bsb.opts[1].flags &= ~BTRFS_BALANCE_OPTS_ENABLED; 102 103 if (IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) 104 bsb.opts[2].flags |= BTRFS_BALANCE_OPTS_ENABLED; 105 else 106 bsb.opts[2].flags &= ~BTRFS_BALANCE_OPTS_ENABLED; 107 108 serialize(&bsb, sizeof(btrfs_start_balance), u); 109 110 t += u; 111 112 RtlZeroMemory(&sei, sizeof(sei)); 113 114 sei.cbSize = sizeof(sei); 115 sei.hwnd = hwndDlg; 116 sei.lpVerb = L"runas"; 117 sei.lpFile = L"rundll32.exe"; 118 sei.lpParameters = t.c_str(); 119 sei.nShow = SW_SHOW; 120 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 121 122 if (!ShellExecuteExW(&sei)) 123 throw last_error(GetLastError()); 124 125 cancelling = false; 126 removing = false; 127 shrinking = false; 128 balance_status = BTRFS_BALANCE_RUNNING; 129 130 EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), true); 131 EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), true); 132 EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), true); 133 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), false); 134 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), false); 135 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), false); 136 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false); 137 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false); 138 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false); 139 140 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), false); 141 142 WaitForSingleObject(sei.hProcess, INFINITE); 143 CloseHandle(sei.hProcess); 144 } 145 146 void BtrfsBalance::PauseBalance(HWND hwndDlg) { 147 WCHAR modfn[MAX_PATH]; 148 wstring t; 149 SHELLEXECUTEINFOW sei; 150 151 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 152 153 #ifndef __REACTOS__ 154 t = L"\""s + modfn + L"\",PauseBalance " + fn; 155 #else 156 t = wstring(L"\"") + modfn + wstring(L"\",PauseBalance ") + fn; 157 #endif 158 159 RtlZeroMemory(&sei, sizeof(sei)); 160 161 sei.cbSize = sizeof(sei); 162 sei.hwnd = hwndDlg; 163 sei.lpVerb = L"runas"; 164 sei.lpFile = L"rundll32.exe"; 165 sei.lpParameters = t.c_str(); 166 sei.nShow = SW_SHOW; 167 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 168 169 if (!ShellExecuteExW(&sei)) 170 throw last_error(GetLastError()); 171 172 WaitForSingleObject(sei.hProcess, INFINITE); 173 CloseHandle(sei.hProcess); 174 } 175 176 void BtrfsBalance::StopBalance(HWND hwndDlg) { 177 WCHAR modfn[MAX_PATH]; 178 wstring t; 179 SHELLEXECUTEINFOW sei; 180 181 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR)); 182 183 #ifndef __REACTOS__ 184 t = L"\""s + modfn + L"\",StopBalance " + fn; 185 #else 186 t = wstring(L"\"") + modfn + wstring(L"\",StopBalance ") + fn; 187 #endif 188 189 RtlZeroMemory(&sei, sizeof(sei)); 190 191 sei.cbSize = sizeof(sei); 192 sei.hwnd = hwndDlg; 193 sei.lpVerb = L"runas"; 194 sei.lpFile = L"rundll32.exe"; 195 sei.lpParameters = t.c_str(); 196 sei.nShow = SW_SHOW; 197 sei.fMask = SEE_MASK_NOCLOSEPROCESS; 198 199 if (!ShellExecuteExW(&sei)) 200 throw last_error(GetLastError()); 201 202 cancelling = true; 203 204 WaitForSingleObject(sei.hProcess, INFINITE); 205 CloseHandle(sei.hProcess); 206 } 207 208 void BtrfsBalance::RefreshBalanceDlg(HWND hwndDlg, bool first) { 209 bool balancing = false; 210 wstring s, t; 211 212 { 213 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 214 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 215 216 if (h != INVALID_HANDLE_VALUE) { 217 NTSTATUS Status; 218 IO_STATUS_BLOCK iosb; 219 220 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb, sizeof(btrfs_query_balance)); 221 222 if (!NT_SUCCESS(Status)) 223 throw ntstatus_error(Status); 224 } else 225 throw last_error(GetLastError()); 226 } 227 228 if (cancelling) 229 bqb.status = BTRFS_BALANCE_STOPPED; 230 231 balancing = bqb.status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED); 232 233 if (!balancing) { 234 if (first || balance_status != BTRFS_BALANCE_STOPPED) { 235 int resid; 236 237 EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), false); 238 EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), false); 239 SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0); 240 EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), false); 241 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), true); 242 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), true); 243 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), true); 244 245 if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED)) { 246 CheckDlgButton(hwndDlg, IDC_DATA, BST_UNCHECKED); 247 CheckDlgButton(hwndDlg, IDC_METADATA, BST_UNCHECKED); 248 CheckDlgButton(hwndDlg, IDC_SYSTEM, BST_UNCHECKED); 249 250 SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETPOS, 0, 0); 251 } 252 253 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ? true : false); 254 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED ? true : false); 255 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED ? true : false); 256 257 if (bqb.status & BTRFS_BALANCE_ERROR) { 258 if (removing) 259 resid = IDS_BALANCE_FAILED_REMOVAL; 260 else if (shrinking) 261 resid = IDS_BALANCE_FAILED_SHRINK; 262 else 263 resid = IDS_BALANCE_FAILED; 264 265 if (!load_string(module, resid, s)) 266 throw last_error(GetLastError()); 267 268 wstring_sprintf(t, s, bqb.error, format_ntstatus(bqb.error).c_str()); 269 270 SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, t.c_str()); 271 } else { 272 if (cancelling) 273 resid = removing ? IDS_BALANCE_CANCELLED_REMOVAL : (shrinking ? IDS_BALANCE_CANCELLED_SHRINK : IDS_BALANCE_CANCELLED); 274 else if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED)) 275 resid = removing ? IDS_BALANCE_COMPLETE_REMOVAL : (shrinking ? IDS_BALANCE_COMPLETE_SHRINK : IDS_BALANCE_COMPLETE); 276 else 277 resid = IDS_NO_BALANCE; 278 279 if (!load_string(module, resid, s)) 280 throw last_error(GetLastError()); 281 282 SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, s.c_str()); 283 } 284 285 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED || 286 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false); 287 288 balance_status = bqb.status; 289 cancelling = false; 290 } 291 292 return; 293 } 294 295 if (first || !(balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))) { 296 EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), true); 297 EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), true); 298 EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), true); 299 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), false); 300 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), false); 301 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), false); 302 303 CheckDlgButton(hwndDlg, IDC_DATA, bqb.data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED); 304 CheckDlgButton(hwndDlg, IDC_METADATA, bqb.metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED); 305 CheckDlgButton(hwndDlg, IDC_SYSTEM, bqb.system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED); 306 307 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), bqb.data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false); 308 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), bqb.metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false); 309 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), bqb.system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false); 310 311 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), false); 312 } 313 314 SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)bqb.total_chunks); 315 SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETPOS, (WPARAM)(bqb.total_chunks - bqb.chunks_left), 0); 316 317 if (bqb.status & BTRFS_BALANCE_PAUSED && balance_status != bqb.status) 318 SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_PAUSED, 0); 319 else if (!(bqb.status & BTRFS_BALANCE_PAUSED) && balance_status & BTRFS_BALANCE_PAUSED) 320 SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0); 321 322 balance_status = bqb.status; 323 324 if (bqb.status & BTRFS_BALANCE_REMOVAL) { 325 if (!load_string(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED_REMOVAL : IDS_BALANCE_RUNNING_REMOVAL, s)) 326 throw last_error(GetLastError()); 327 328 wstring_sprintf(t, s, bqb.data_opts.devid, bqb.total_chunks - bqb.chunks_left, bqb.total_chunks, 329 (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks); 330 331 removing = true; 332 shrinking = false; 333 } else if (bqb.status & BTRFS_BALANCE_SHRINKING) { 334 if (!load_string(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED_SHRINK : IDS_BALANCE_RUNNING_SHRINK, s)) 335 throw last_error(GetLastError()); 336 337 wstring_sprintf(t, s, bqb.data_opts.devid, bqb.total_chunks - bqb.chunks_left, bqb.total_chunks, 338 (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks); 339 340 removing = false; 341 shrinking = true; 342 } else { 343 if (!load_string(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED : IDS_BALANCE_RUNNING, s)) 344 throw last_error(GetLastError()); 345 346 wstring_sprintf(t, s, bqb.total_chunks - bqb.chunks_left, bqb.total_chunks, 347 (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks); 348 349 removing = false; 350 shrinking = false; 351 } 352 353 SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, t.c_str()); 354 } 355 356 void BtrfsBalance::SaveBalanceOpts(HWND hwndDlg) { 357 btrfs_balance_opts* opts; 358 359 switch (opts_type) { 360 case 1: 361 opts = &data_opts; 362 break; 363 364 case 2: 365 opts = &metadata_opts; 366 break; 367 368 case 3: 369 opts = &system_opts; 370 break; 371 372 default: 373 return; 374 } 375 376 RtlZeroMemory(opts, sizeof(btrfs_balance_opts)); 377 378 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES) == BST_CHECKED) { 379 opts->flags |= BTRFS_BALANCE_OPTS_PROFILES; 380 381 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_SINGLE) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_SINGLE; 382 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_DUP) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_DUPLICATE; 383 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID0) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID0; 384 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1; 385 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID10) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID10; 386 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID5) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID5; 387 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID6) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID6; 388 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1C3) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1C3; 389 if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1C4) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1C4; 390 } 391 392 if (IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED) { 393 opts->flags |= BTRFS_BALANCE_OPTS_DEVID; 394 395 auto sel = SendMessageW(GetDlgItem(hwndDlg, IDC_DEVID_COMBO), CB_GETCURSEL, 0, 0); 396 397 if (sel == CB_ERR) 398 opts->flags &= ~BTRFS_BALANCE_OPTS_DEVID; 399 else { 400 btrfs_device* bd = devices; 401 int i = 0; 402 403 while (true) { 404 if (i == sel) { 405 opts->devid = bd->dev_id; 406 break; 407 } 408 409 i++; 410 411 if (bd->next_entry > 0) 412 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 413 else 414 break; 415 } 416 417 if (opts->devid == 0) 418 opts->flags &= ~BTRFS_BALANCE_OPTS_DEVID; 419 } 420 } 421 422 if (IsDlgButtonChecked(hwndDlg, IDC_DRANGE) == BST_CHECKED) { 423 WCHAR s[255]; 424 425 opts->flags |= BTRFS_BALANCE_OPTS_DRANGE; 426 427 GetWindowTextW(GetDlgItem(hwndDlg, IDC_DRANGE_START), s, sizeof(s) / sizeof(WCHAR)); 428 opts->drange_start = _wtoi64(s); 429 430 GetWindowTextW(GetDlgItem(hwndDlg, IDC_DRANGE_END), s, sizeof(s) / sizeof(WCHAR)); 431 opts->drange_end = _wtoi64(s); 432 433 if (opts->drange_end < opts->drange_start) 434 throw string_error(IDS_DRANGE_END_BEFORE_START); 435 } 436 437 if (IsDlgButtonChecked(hwndDlg, IDC_VRANGE) == BST_CHECKED) { 438 WCHAR s[255]; 439 440 opts->flags |= BTRFS_BALANCE_OPTS_VRANGE; 441 442 GetWindowTextW(GetDlgItem(hwndDlg, IDC_VRANGE_START), s, sizeof(s) / sizeof(WCHAR)); 443 opts->vrange_start = _wtoi64(s); 444 445 GetWindowTextW(GetDlgItem(hwndDlg, IDC_VRANGE_END), s, sizeof(s) / sizeof(WCHAR)); 446 opts->vrange_end = _wtoi64(s); 447 448 if (opts->vrange_end < opts->vrange_start) 449 throw string_error(IDS_VRANGE_END_BEFORE_START); 450 } 451 452 if (IsDlgButtonChecked(hwndDlg, IDC_LIMIT) == BST_CHECKED) { 453 WCHAR s[255]; 454 455 opts->flags |= BTRFS_BALANCE_OPTS_LIMIT; 456 457 GetWindowTextW(GetDlgItem(hwndDlg, IDC_LIMIT_START), s, sizeof(s) / sizeof(WCHAR)); 458 opts->limit_start = _wtoi64(s); 459 460 GetWindowTextW(GetDlgItem(hwndDlg, IDC_LIMIT_END), s, sizeof(s) / sizeof(WCHAR)); 461 opts->limit_end = _wtoi64(s); 462 463 if (opts->limit_end < opts->limit_start) 464 throw string_error(IDS_LIMIT_END_BEFORE_START); 465 } 466 467 if (IsDlgButtonChecked(hwndDlg, IDC_STRIPES) == BST_CHECKED) { 468 WCHAR s[255]; 469 470 opts->flags |= BTRFS_BALANCE_OPTS_STRIPES; 471 472 GetWindowTextW(GetDlgItem(hwndDlg, IDC_STRIPES_START), s, sizeof(s) / sizeof(WCHAR)); 473 opts->stripes_start = (uint8_t)_wtoi(s); 474 475 GetWindowTextW(GetDlgItem(hwndDlg, IDC_STRIPES_END), s, sizeof(s) / sizeof(WCHAR)); 476 opts->stripes_end = (uint8_t)_wtoi(s); 477 478 if (opts->stripes_end < opts->stripes_start) 479 throw string_error(IDS_STRIPES_END_BEFORE_START); 480 } 481 482 if (IsDlgButtonChecked(hwndDlg, IDC_USAGE) == BST_CHECKED) { 483 WCHAR s[255]; 484 485 opts->flags |= BTRFS_BALANCE_OPTS_USAGE; 486 487 GetWindowTextW(GetDlgItem(hwndDlg, IDC_USAGE_START), s, sizeof(s) / sizeof(WCHAR)); 488 opts->usage_start = (uint8_t)_wtoi(s); 489 490 GetWindowTextW(GetDlgItem(hwndDlg, IDC_USAGE_END), s, sizeof(s) / sizeof(WCHAR)); 491 opts->usage_end = (uint8_t)_wtoi(s); 492 493 if (opts->usage_end < opts->usage_start) 494 throw string_error(IDS_USAGE_END_BEFORE_START); 495 } 496 497 if (IsDlgButtonChecked(hwndDlg, IDC_CONVERT) == BST_CHECKED) { 498 opts->flags |= BTRFS_BALANCE_OPTS_CONVERT; 499 500 auto sel = SendMessageW(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), CB_GETCURSEL, 0, 0); 501 502 if (sel == CB_ERR || (unsigned int)sel >= sizeof(convtypes2) / sizeof(convtypes2[0])) 503 opts->flags &= ~BTRFS_BALANCE_OPTS_CONVERT; 504 else { 505 opts->convert = convtypes2[sel]; 506 507 if (IsDlgButtonChecked(hwndDlg, IDC_SOFT) == BST_CHECKED) opts->flags |= BTRFS_BALANCE_OPTS_SOFT; 508 } 509 } 510 511 EndDialog(hwndDlg, 0); 512 } 513 514 INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 515 try { 516 switch (uMsg) { 517 case WM_INITDIALOG: 518 { 519 HWND devcb, convcb; 520 btrfs_device* bd; 521 btrfs_balance_opts* opts; 522 static int convtypes[] = { IDS_SINGLE2, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID5, IDS_RAID1C3, IDS_RAID6, IDS_RAID10, IDS_RAID1C4, 0 }; 523 int i, num_devices = 0, num_writeable_devices = 0; 524 wstring s, u; 525 bool balance_started = balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED); 526 527 switch (opts_type) { 528 case 1: 529 opts = balance_started ? &bqb.data_opts : &data_opts; 530 break; 531 532 case 2: 533 opts = balance_started ? &bqb.metadata_opts : &metadata_opts; 534 break; 535 536 case 3: 537 opts = balance_started ? &bqb.system_opts : &system_opts; 538 break; 539 540 default: 541 return true; 542 } 543 544 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); 545 546 devcb = GetDlgItem(hwndDlg, IDC_DEVID_COMBO); 547 548 if (!load_string(module, IDS_DEVID_LIST, u)) 549 throw last_error(GetLastError()); 550 551 bd = devices; 552 while (true) { 553 wstring t, v; 554 555 if (bd->device_number == 0xffffffff) 556 s = wstring(bd->name, bd->namelen); 557 else if (bd->partition_number == 0) { 558 if (!load_string(module, IDS_DISK_NUM, v)) 559 throw last_error(GetLastError()); 560 561 wstring_sprintf(s, v, bd->device_number); 562 } else { 563 if (!load_string(module, IDS_DISK_PART_NUM, v)) 564 throw last_error(GetLastError()); 565 566 wstring_sprintf(s, v, bd->device_number, bd->partition_number); 567 } 568 569 wstring_sprintf(t, u, bd->dev_id, s.c_str()); 570 571 SendMessageW(devcb, CB_ADDSTRING, 0, (LPARAM)t.c_str()); 572 573 if (opts->devid == bd->dev_id) 574 SendMessageW(devcb, CB_SETCURSEL, num_devices, 0); 575 576 num_devices++; 577 578 if (!bd->readonly) 579 num_writeable_devices++; 580 581 if (bd->next_entry > 0) 582 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 583 else 584 break; 585 } 586 587 convcb = GetDlgItem(hwndDlg, IDC_CONVERT_COMBO); 588 589 if (num_writeable_devices == 0) 590 num_writeable_devices = num_devices; 591 592 i = 0; 593 while (convtypes[i] != 0) { 594 if (!load_string(module, convtypes[i], s)) 595 throw last_error(GetLastError()); 596 597 SendMessageW(convcb, CB_ADDSTRING, 0, (LPARAM)s.c_str()); 598 599 if (opts->convert == convtypes2[i]) 600 SendMessageW(convcb, CB_SETCURSEL, i, 0); 601 602 i++; 603 604 if (num_writeable_devices < 2 && i == 2) 605 break; 606 else if (num_writeable_devices < 3 && i == 4) 607 break; 608 else if (num_writeable_devices < 4 && i == 6) 609 break; 610 } 611 612 // profiles 613 614 CheckDlgButton(hwndDlg, IDC_PROFILES, opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? BST_CHECKED : BST_UNCHECKED); 615 CheckDlgButton(hwndDlg, IDC_PROFILES_SINGLE, opts->profiles & BLOCK_FLAG_SINGLE ? BST_CHECKED : BST_UNCHECKED); 616 CheckDlgButton(hwndDlg, IDC_PROFILES_DUP, opts->profiles & BLOCK_FLAG_DUPLICATE ? BST_CHECKED : BST_UNCHECKED); 617 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID0, opts->profiles & BLOCK_FLAG_RAID0 ? BST_CHECKED : BST_UNCHECKED); 618 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1, opts->profiles & BLOCK_FLAG_RAID1 ? BST_CHECKED : BST_UNCHECKED); 619 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID10, opts->profiles & BLOCK_FLAG_RAID10 ? BST_CHECKED : BST_UNCHECKED); 620 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID5, opts->profiles & BLOCK_FLAG_RAID5 ? BST_CHECKED : BST_UNCHECKED); 621 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID6, opts->profiles & BLOCK_FLAG_RAID6 ? BST_CHECKED : BST_UNCHECKED); 622 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1C3, opts->profiles & BLOCK_FLAG_RAID1C3 ? BST_CHECKED : BST_UNCHECKED); 623 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1C4, opts->profiles & BLOCK_FLAG_RAID1C4 ? BST_CHECKED : BST_UNCHECKED); 624 625 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_SINGLE), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 626 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_DUP), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 627 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID0), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 628 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 629 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 630 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 631 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 632 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C3), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 633 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C4), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false); 634 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES), balance_started ? false : true); 635 636 // usage 637 638 CheckDlgButton(hwndDlg, IDC_USAGE, opts->flags & BTRFS_BALANCE_OPTS_USAGE ? BST_CHECKED : BST_UNCHECKED); 639 640 s = to_wstring(opts->usage_start); 641 SetDlgItemTextW(hwndDlg, IDC_USAGE_START, s.c_str()); 642 SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), UDM_SETRANGE32, 0, 100); 643 644 s = to_wstring(opts->usage_end); 645 SetDlgItemTextW(hwndDlg, IDC_USAGE_END, s.c_str()); 646 SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), UDM_SETRANGE32, 0, 100); 647 648 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false); 649 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false); 650 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false); 651 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false); 652 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE), balance_started ? false : true); 653 654 // devid 655 656 if (num_devices < 2 || balance_started) 657 EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID), false); 658 659 CheckDlgButton(hwndDlg, IDC_DEVID, opts->flags & BTRFS_BALANCE_OPTS_DEVID ? BST_CHECKED : BST_UNCHECKED); 660 EnableWindow(devcb, (opts->flags & BTRFS_BALANCE_OPTS_DEVID && num_devices >= 2 && !balance_started) ? true : false); 661 662 // drange 663 664 CheckDlgButton(hwndDlg, IDC_DRANGE, opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? BST_CHECKED : BST_UNCHECKED); 665 666 s = to_wstring(opts->drange_start); 667 SetDlgItemTextW(hwndDlg, IDC_DRANGE_START, s.c_str()); 668 669 s = to_wstring(opts->drange_end); 670 SetDlgItemTextW(hwndDlg, IDC_DRANGE_END, s.c_str()); 671 672 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? true : false); 673 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? true : false); 674 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE), balance_started ? false : true); 675 676 // vrange 677 678 CheckDlgButton(hwndDlg, IDC_VRANGE, opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? BST_CHECKED : BST_UNCHECKED); 679 680 s = to_wstring(opts->vrange_start); 681 SetDlgItemTextW(hwndDlg, IDC_VRANGE_START, s.c_str()); 682 683 s = to_wstring(opts->vrange_end); 684 SetDlgItemTextW(hwndDlg, IDC_VRANGE_END, s.c_str()); 685 686 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? true : false); 687 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? true : false); 688 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE), balance_started ? false : true); 689 690 // limit 691 692 CheckDlgButton(hwndDlg, IDC_LIMIT, opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? BST_CHECKED : BST_UNCHECKED); 693 694 s = to_wstring(opts->limit_start); 695 SetDlgItemTextW(hwndDlg, IDC_LIMIT_START, s.c_str()); 696 SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff); 697 698 s = to_wstring(opts->limit_end); 699 SetDlgItemTextW(hwndDlg, IDC_LIMIT_END, s.c_str()); 700 SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff); 701 702 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false); 703 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false); 704 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false); 705 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false); 706 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT), balance_started ? false : true); 707 708 // stripes 709 710 CheckDlgButton(hwndDlg, IDC_STRIPES, opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? BST_CHECKED : BST_UNCHECKED); 711 712 s = to_wstring(opts->stripes_start); 713 SetDlgItemTextW(hwndDlg, IDC_STRIPES_START, s.c_str()); 714 SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), UDM_SETRANGE32, 0, 0xffff); 715 716 s = to_wstring(opts->stripes_end); 717 SetDlgItemTextW(hwndDlg, IDC_STRIPES_END, s.c_str()); 718 SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), UDM_SETRANGE32, 0, 0xffff); 719 720 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false); 721 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false); 722 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false); 723 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false); 724 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES), balance_started ? false : true); 725 726 // convert 727 728 CheckDlgButton(hwndDlg, IDC_CONVERT, opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? BST_CHECKED : BST_UNCHECKED); 729 CheckDlgButton(hwndDlg, IDC_SOFT, opts->flags & BTRFS_BALANCE_OPTS_SOFT ? BST_CHECKED : BST_UNCHECKED); 730 731 EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? true : false); 732 EnableWindow(convcb, !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? true : false); 733 EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT), balance_started ? false : true); 734 735 break; 736 } 737 738 case WM_COMMAND: 739 switch (HIWORD(wParam)) { 740 case BN_CLICKED: 741 switch (LOWORD(wParam)) { 742 case IDOK: 743 if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED)) 744 EndDialog(hwndDlg, 0); 745 else 746 SaveBalanceOpts(hwndDlg); 747 return true; 748 749 case IDCANCEL: 750 EndDialog(hwndDlg, 0); 751 return true; 752 753 case IDC_PROFILES: { 754 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_PROFILES) == BST_CHECKED ? true : false; 755 756 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_SINGLE), enabled); 757 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_DUP), enabled); 758 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID0), enabled); 759 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1), enabled); 760 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), enabled); 761 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), enabled); 762 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), enabled); 763 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C3), enabled); 764 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C4), enabled); 765 break; 766 } 767 768 case IDC_USAGE: { 769 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_USAGE) == BST_CHECKED ? true : false; 770 771 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), enabled); 772 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), enabled); 773 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), enabled); 774 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), enabled); 775 break; 776 } 777 778 case IDC_DEVID: { 779 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED ? true : false; 780 781 EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID_COMBO), enabled); 782 break; 783 } 784 785 case IDC_DRANGE: { 786 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_DRANGE) == BST_CHECKED ? true : false; 787 788 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), enabled); 789 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), enabled); 790 break; 791 } 792 793 case IDC_VRANGE: { 794 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_VRANGE) == BST_CHECKED ? true : false; 795 796 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), enabled); 797 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), enabled); 798 break; 799 } 800 801 case IDC_LIMIT: { 802 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_LIMIT) == BST_CHECKED ? true : false; 803 804 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), enabled); 805 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), enabled); 806 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), enabled); 807 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), enabled); 808 break; 809 } 810 811 case IDC_STRIPES: { 812 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_STRIPES) == BST_CHECKED ? true : false; 813 814 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), enabled); 815 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), enabled); 816 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), enabled); 817 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), enabled); 818 break; 819 } 820 821 case IDC_CONVERT: { 822 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_CONVERT) == BST_CHECKED ? true : false; 823 824 EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), enabled); 825 EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), enabled); 826 break; 827 } 828 } 829 break; 830 } 831 break; 832 } 833 } catch (const exception& e) { 834 error_message(hwndDlg, e.what()); 835 } 836 837 return false; 838 } 839 840 static INT_PTR CALLBACK stub_BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 841 BtrfsBalance* bb; 842 843 if (uMsg == WM_INITDIALOG) { 844 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 845 bb = (BtrfsBalance*)lParam; 846 } else { 847 bb = (BtrfsBalance*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 848 } 849 850 if (bb) 851 return bb->BalanceOptsDlgProc(hwndDlg, uMsg, wParam, lParam); 852 else 853 return false; 854 } 855 856 void BtrfsBalance::ShowBalanceOptions(HWND hwndDlg, uint8_t type) { 857 opts_type = type; 858 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE_OPTIONS), hwndDlg, stub_BalanceOptsDlgProc, (LPARAM)this); 859 } 860 861 INT_PTR CALLBACK BtrfsBalance::BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 862 try { 863 switch (uMsg) { 864 case WM_INITDIALOG: 865 { 866 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB); 867 868 RtlZeroMemory(&data_opts, sizeof(btrfs_balance_opts)); 869 RtlZeroMemory(&metadata_opts, sizeof(btrfs_balance_opts)); 870 RtlZeroMemory(&system_opts, sizeof(btrfs_balance_opts)); 871 872 removing = called_from_RemoveDevice; 873 shrinking = called_from_ShrinkDevice; 874 balance_status = (removing || shrinking) ? BTRFS_BALANCE_RUNNING : BTRFS_BALANCE_STOPPED; 875 cancelling = false; 876 RefreshBalanceDlg(hwndDlg, true); 877 878 if (readonly) { 879 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), false); 880 EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), false); 881 EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), false); 882 } 883 884 SendMessageW(GetDlgItem(hwndDlg, IDC_START_BALANCE), BCM_SETSHIELD, 0, true); 885 SendMessageW(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), BCM_SETSHIELD, 0, true); 886 SendMessageW(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), BCM_SETSHIELD, 0, true); 887 888 SetTimer(hwndDlg, 1, 1000, nullptr); 889 890 break; 891 } 892 893 case WM_COMMAND: 894 switch (HIWORD(wParam)) { 895 case BN_CLICKED: 896 switch (LOWORD(wParam)) { 897 case IDOK: 898 case IDCANCEL: 899 KillTimer(hwndDlg, 1); 900 EndDialog(hwndDlg, 0); 901 return true; 902 903 case IDC_DATA: 904 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ? true : false); 905 906 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED || 907 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false); 908 return true; 909 910 case IDC_METADATA: 911 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED ? true : false); 912 913 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED || 914 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false); 915 return true; 916 917 case IDC_SYSTEM: 918 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED ? true : false); 919 920 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED || 921 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false); 922 return true; 923 924 case IDC_DATA_OPTIONS: 925 ShowBalanceOptions(hwndDlg, 1); 926 return true; 927 928 case IDC_METADATA_OPTIONS: 929 ShowBalanceOptions(hwndDlg, 2); 930 return true; 931 932 case IDC_SYSTEM_OPTIONS: 933 ShowBalanceOptions(hwndDlg, 3); 934 return true; 935 936 case IDC_START_BALANCE: 937 StartBalance(hwndDlg); 938 return true; 939 940 case IDC_PAUSE_BALANCE: 941 PauseBalance(hwndDlg); 942 RefreshBalanceDlg(hwndDlg, false); 943 return true; 944 945 case IDC_CANCEL_BALANCE: 946 StopBalance(hwndDlg); 947 RefreshBalanceDlg(hwndDlg, false); 948 return true; 949 } 950 break; 951 } 952 break; 953 954 case WM_TIMER: 955 RefreshBalanceDlg(hwndDlg, false); 956 break; 957 } 958 } catch (const exception& e) { 959 error_message(hwndDlg, e.what()); 960 } 961 962 return false; 963 } 964 965 static INT_PTR CALLBACK stub_BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { 966 BtrfsBalance* bb; 967 968 if (uMsg == WM_INITDIALOG) { 969 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); 970 bb = (BtrfsBalance*)lParam; 971 } else { 972 bb = (BtrfsBalance*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA); 973 } 974 975 if (bb) 976 return bb->BalanceDlgProc(hwndDlg, uMsg, wParam, lParam); 977 else 978 return false; 979 } 980 981 void BtrfsBalance::ShowBalance(HWND hwndDlg) { 982 btrfs_device* bd; 983 984 if (devices) { 985 free(devices); 986 devices = nullptr; 987 } 988 989 { 990 win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 991 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 992 993 if (h != INVALID_HANDLE_VALUE) { 994 NTSTATUS Status; 995 IO_STATUS_BLOCK iosb; 996 ULONG devsize, i; 997 998 i = 0; 999 devsize = 1024; 1000 1001 devices = (btrfs_device*)malloc(devsize); 1002 1003 while (true) { 1004 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize); 1005 if (Status == STATUS_BUFFER_OVERFLOW) { 1006 if (i < 8) { 1007 devsize += 1024; 1008 1009 free(devices); 1010 devices = (btrfs_device*)malloc(devsize); 1011 1012 i++; 1013 } else 1014 return; 1015 } else 1016 break; 1017 } 1018 1019 if (!NT_SUCCESS(Status)) 1020 throw ntstatus_error(Status); 1021 } else 1022 throw last_error(GetLastError()); 1023 } 1024 1025 readonly = true; 1026 bd = devices; 1027 1028 while (true) { 1029 if (!bd->readonly) { 1030 readonly = false; 1031 break; 1032 } 1033 1034 if (bd->next_entry > 0) 1035 bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry); 1036 else 1037 break; 1038 } 1039 1040 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE), hwndDlg, stub_BalanceDlgProc, (LPARAM)this); 1041 } 1042 1043 static uint8_t from_hex_digit(WCHAR c) { 1044 if (c >= 'a' && c <= 'f') 1045 return (uint8_t)(c - 'a' + 0xa); 1046 else if (c >= 'A' && c <= 'F') 1047 return (uint8_t)(c - 'A' + 0xa); 1048 else 1049 return (uint8_t)(c - '0'); 1050 } 1051 1052 static void unserialize(void* data, ULONG len, WCHAR* s) { 1053 uint8_t* d; 1054 1055 d = (uint8_t*)data; 1056 1057 while (s[0] != 0 && s[1] != 0 && len > 0) { 1058 *d = (uint8_t)(from_hex_digit(s[0]) << 4) | from_hex_digit(s[1]); 1059 1060 s += 2; 1061 d++; 1062 len--; 1063 } 1064 } 1065 1066 extern "C" void CALLBACK StartBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1067 try { 1068 WCHAR *s, *vol, *block; 1069 win_handle h, token; 1070 btrfs_start_balance bsb; 1071 TOKEN_PRIVILEGES tp; 1072 LUID luid; 1073 1074 s = wcsstr(lpszCmdLine, L" "); 1075 if (!s) 1076 return; 1077 1078 s[0] = 0; 1079 1080 vol = lpszCmdLine; 1081 block = &s[1]; 1082 1083 RtlZeroMemory(&bsb, sizeof(btrfs_start_balance)); 1084 unserialize(&bsb, sizeof(btrfs_start_balance), block); 1085 1086 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 1087 throw last_error(GetLastError()); 1088 1089 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 1090 throw last_error(GetLastError()); 1091 1092 tp.PrivilegeCount = 1; 1093 tp.Privileges[0].Luid = luid; 1094 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1095 1096 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 1097 throw last_error(GetLastError()); 1098 1099 h = CreateFileW(vol, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 1100 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 1101 1102 if (h != INVALID_HANDLE_VALUE) { 1103 NTSTATUS Status; 1104 IO_STATUS_BLOCK iosb; 1105 1106 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_START_BALANCE, &bsb, sizeof(btrfs_start_balance), nullptr, 0); 1107 1108 if (Status == STATUS_DEVICE_NOT_READY) { 1109 btrfs_query_scrub bqs; 1110 NTSTATUS Status2; 1111 1112 Status2 = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, &bqs, sizeof(btrfs_query_scrub)); 1113 1114 if ((NT_SUCCESS(Status2) || Status2 == STATUS_BUFFER_OVERFLOW) && bqs.status != BTRFS_SCRUB_STOPPED) 1115 throw string_error(IDS_BALANCE_SCRUB_RUNNING); 1116 } 1117 1118 if (!NT_SUCCESS(Status)) 1119 throw ntstatus_error(Status); 1120 } else 1121 throw last_error(GetLastError()); 1122 } catch (const exception& e) { 1123 error_message(hwnd, e.what()); 1124 } 1125 } 1126 1127 extern "C" void CALLBACK PauseBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1128 try { 1129 win_handle h, token; 1130 TOKEN_PRIVILEGES tp; 1131 LUID luid; 1132 1133 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 1134 throw last_error(GetLastError()); 1135 1136 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 1137 throw last_error(GetLastError()); 1138 1139 tp.PrivilegeCount = 1; 1140 tp.Privileges[0].Luid = luid; 1141 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1142 1143 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 1144 throw last_error(GetLastError()); 1145 1146 h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 1147 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 1148 1149 if (h != INVALID_HANDLE_VALUE) { 1150 NTSTATUS Status; 1151 IO_STATUS_BLOCK iosb; 1152 btrfs_query_balance bqb2; 1153 1154 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb2, sizeof(btrfs_query_balance)); 1155 if (!NT_SUCCESS(Status)) 1156 throw ntstatus_error(Status); 1157 1158 if (bqb2.status & BTRFS_BALANCE_PAUSED) 1159 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESUME_BALANCE, nullptr, 0, nullptr, 0); 1160 else if (bqb2.status & BTRFS_BALANCE_RUNNING) 1161 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_PAUSE_BALANCE, nullptr, 0, nullptr, 0); 1162 else 1163 return; 1164 1165 if (!NT_SUCCESS(Status)) 1166 throw ntstatus_error(Status); 1167 } else 1168 throw last_error(GetLastError()); 1169 } catch (const exception& e) { 1170 error_message(hwnd, e.what()); 1171 } 1172 } 1173 1174 extern "C" void CALLBACK StopBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { 1175 try { 1176 win_handle h, token; 1177 TOKEN_PRIVILEGES tp; 1178 LUID luid; 1179 1180 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 1181 throw last_error(GetLastError()); 1182 1183 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) 1184 throw last_error(GetLastError()); 1185 1186 tp.PrivilegeCount = 1; 1187 tp.Privileges[0].Luid = luid; 1188 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1189 1190 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) 1191 throw last_error(GetLastError()); 1192 1193 h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, 1194 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); 1195 1196 if (h != INVALID_HANDLE_VALUE) { 1197 NTSTATUS Status; 1198 IO_STATUS_BLOCK iosb; 1199 btrfs_query_balance bqb2; 1200 1201 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb2, sizeof(btrfs_query_balance)); 1202 if (!NT_SUCCESS(Status)) 1203 throw ntstatus_error(Status); 1204 1205 if (bqb2.status & BTRFS_BALANCE_PAUSED || bqb2.status & BTRFS_BALANCE_RUNNING) 1206 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_STOP_BALANCE, nullptr, 0, nullptr, 0); 1207 else 1208 return; 1209 1210 if (!NT_SUCCESS(Status)) 1211 throw ntstatus_error(Status); 1212 } else 1213 throw last_error(GetLastError()); 1214 } catch (const exception& e) { 1215 error_message(hwnd, e.what()); 1216 } 1217 } 1218