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