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
hex_digit(uint8_t u)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
serialize(void * data,ULONG len,WCHAR * s)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
StartBalance(HWND hwndDlg)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
PauseBalance(HWND hwndDlg)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
StopBalance(HWND hwndDlg)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
RefreshBalanceDlg(HWND hwndDlg,bool first)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
SaveBalanceOpts(HWND hwndDlg)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
BalanceOptsDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)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
stub_BalanceOptsDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)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
ShowBalanceOptions(HWND hwndDlg,uint8_t type)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
BalanceDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)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
stub_BalanceDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)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
ShowBalance(HWND hwndDlg)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
from_hex_digit(WCHAR c)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
unserialize(void * data,ULONG len,WCHAR * s)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
StartBalanceW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)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
PauseBalanceW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)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
StopBalanceW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)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