xref: /reactos/dll/shellext/shellbtrfs/balance.cpp (revision 8a978a17)
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         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1C3) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1C3;
387         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1C4) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1C4;
388     }
389 
390     if (IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED) {
391         opts->flags |= BTRFS_BALANCE_OPTS_DEVID;
392 
393         auto sel = SendMessageW(GetDlgItem(hwndDlg, IDC_DEVID_COMBO), CB_GETCURSEL, 0, 0);
394 
395         if (sel == CB_ERR)
396             opts->flags &= ~BTRFS_BALANCE_OPTS_DEVID;
397         else {
398             btrfs_device* bd = devices;
399             int i = 0;
400 
401             while (true) {
402                 if (i == sel) {
403                     opts->devid = bd->dev_id;
404                     break;
405                 }
406 
407                 i++;
408 
409                 if (bd->next_entry > 0)
410                     bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry);
411                 else
412                     break;
413             }
414 
415             if (opts->devid == 0)
416                 opts->flags &= ~BTRFS_BALANCE_OPTS_DEVID;
417         }
418     }
419 
420     if (IsDlgButtonChecked(hwndDlg, IDC_DRANGE) == BST_CHECKED) {
421         WCHAR s[255];
422 
423         opts->flags |= BTRFS_BALANCE_OPTS_DRANGE;
424 
425         GetWindowTextW(GetDlgItem(hwndDlg, IDC_DRANGE_START), s, sizeof(s) / sizeof(WCHAR));
426         opts->drange_start = _wtoi64(s);
427 
428         GetWindowTextW(GetDlgItem(hwndDlg, IDC_DRANGE_END), s, sizeof(s) / sizeof(WCHAR));
429         opts->drange_end = _wtoi64(s);
430 
431         if (opts->drange_end < opts->drange_start)
432             throw string_error(IDS_DRANGE_END_BEFORE_START);
433     }
434 
435     if (IsDlgButtonChecked(hwndDlg, IDC_VRANGE) == BST_CHECKED) {
436         WCHAR s[255];
437 
438         opts->flags |= BTRFS_BALANCE_OPTS_VRANGE;
439 
440         GetWindowTextW(GetDlgItem(hwndDlg, IDC_VRANGE_START), s, sizeof(s) / sizeof(WCHAR));
441         opts->vrange_start = _wtoi64(s);
442 
443         GetWindowTextW(GetDlgItem(hwndDlg, IDC_VRANGE_END), s, sizeof(s) / sizeof(WCHAR));
444         opts->vrange_end = _wtoi64(s);
445 
446         if (opts->vrange_end < opts->vrange_start)
447             throw string_error(IDS_VRANGE_END_BEFORE_START);
448     }
449 
450     if (IsDlgButtonChecked(hwndDlg, IDC_LIMIT) == BST_CHECKED) {
451         WCHAR s[255];
452 
453         opts->flags |= BTRFS_BALANCE_OPTS_LIMIT;
454 
455         GetWindowTextW(GetDlgItem(hwndDlg, IDC_LIMIT_START), s, sizeof(s) / sizeof(WCHAR));
456         opts->limit_start = _wtoi64(s);
457 
458         GetWindowTextW(GetDlgItem(hwndDlg, IDC_LIMIT_END), s, sizeof(s) / sizeof(WCHAR));
459         opts->limit_end = _wtoi64(s);
460 
461         if (opts->limit_end < opts->limit_start)
462             throw string_error(IDS_LIMIT_END_BEFORE_START);
463     }
464 
465     if (IsDlgButtonChecked(hwndDlg, IDC_STRIPES) == BST_CHECKED) {
466         WCHAR s[255];
467 
468         opts->flags |= BTRFS_BALANCE_OPTS_STRIPES;
469 
470         GetWindowTextW(GetDlgItem(hwndDlg, IDC_STRIPES_START), s, sizeof(s) / sizeof(WCHAR));
471         opts->stripes_start = (uint8_t)_wtoi(s);
472 
473         GetWindowTextW(GetDlgItem(hwndDlg, IDC_STRIPES_END), s, sizeof(s) / sizeof(WCHAR));
474         opts->stripes_end = (uint8_t)_wtoi(s);
475 
476         if (opts->stripes_end < opts->stripes_start)
477             throw string_error(IDS_STRIPES_END_BEFORE_START);
478     }
479 
480     if (IsDlgButtonChecked(hwndDlg, IDC_USAGE) == BST_CHECKED) {
481         WCHAR s[255];
482 
483         opts->flags |= BTRFS_BALANCE_OPTS_USAGE;
484 
485         GetWindowTextW(GetDlgItem(hwndDlg, IDC_USAGE_START), s, sizeof(s) / sizeof(WCHAR));
486         opts->usage_start = (uint8_t)_wtoi(s);
487 
488         GetWindowTextW(GetDlgItem(hwndDlg, IDC_USAGE_END), s, sizeof(s) / sizeof(WCHAR));
489         opts->usage_end = (uint8_t)_wtoi(s);
490 
491         if (opts->usage_end < opts->usage_start)
492             throw string_error(IDS_USAGE_END_BEFORE_START);
493     }
494 
495     if (IsDlgButtonChecked(hwndDlg, IDC_CONVERT) == BST_CHECKED) {
496         opts->flags |= BTRFS_BALANCE_OPTS_CONVERT;
497 
498         auto sel = SendMessageW(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), CB_GETCURSEL, 0, 0);
499 
500         if (sel == CB_ERR || (unsigned int)sel >= sizeof(convtypes2) / sizeof(convtypes2[0]))
501             opts->flags &= ~BTRFS_BALANCE_OPTS_CONVERT;
502         else {
503             opts->convert = convtypes2[sel];
504 
505             if (IsDlgButtonChecked(hwndDlg, IDC_SOFT) == BST_CHECKED) opts->flags |= BTRFS_BALANCE_OPTS_SOFT;
506         }
507     }
508 
509     EndDialog(hwndDlg, 0);
510 }
511 
512 INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
513     try {
514         switch (uMsg) {
515             case WM_INITDIALOG:
516             {
517                 HWND devcb, convcb;
518                 btrfs_device* bd;
519                 btrfs_balance_opts* opts;
520                 static int convtypes[] = { IDS_SINGLE2, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID5, IDS_RAID1C3, IDS_RAID6, IDS_RAID10, IDS_RAID1C4, 0 };
521                 int i, num_devices = 0, num_writeable_devices = 0;
522                 wstring s, u;
523                 bool balance_started = balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED);
524 
525                 switch (opts_type) {
526                     case 1:
527                         opts = balance_started ? &bqb.data_opts : &data_opts;
528                     break;
529 
530                     case 2:
531                         opts = balance_started ? &bqb.metadata_opts : &metadata_opts;
532                     break;
533 
534                     case 3:
535                         opts = balance_started ? &bqb.system_opts : &system_opts;
536                     break;
537 
538                     default:
539                         return true;
540                 }
541 
542                 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
543 
544                 devcb = GetDlgItem(hwndDlg, IDC_DEVID_COMBO);
545 
546                 if (!load_string(module, IDS_DEVID_LIST, u))
547                     throw last_error(GetLastError());
548 
549                 bd = devices;
550                 while (true) {
551                     wstring t, v;
552 
553                     if (bd->device_number == 0xffffffff)
554                         s = wstring(bd->name, bd->namelen);
555                     else if (bd->partition_number == 0) {
556                         if (!load_string(module, IDS_DISK_NUM, v))
557                             throw last_error(GetLastError());
558 
559                         wstring_sprintf(s, v, bd->device_number);
560                     } else {
561                         if (!load_string(module, IDS_DISK_PART_NUM, v))
562                             throw last_error(GetLastError());
563 
564                         wstring_sprintf(s, v, bd->device_number, bd->partition_number);
565                     }
566 
567                     wstring_sprintf(t, u, bd->dev_id, s.c_str());
568 
569                     SendMessage(devcb, CB_ADDSTRING, 0, (LPARAM)t.c_str());
570 
571                     if (opts->devid == bd->dev_id)
572                         SendMessage(devcb, CB_SETCURSEL, num_devices, 0);
573 
574                     num_devices++;
575 
576                     if (!bd->readonly)
577                         num_writeable_devices++;
578 
579                     if (bd->next_entry > 0)
580                         bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry);
581                     else
582                         break;
583                 }
584 
585                 convcb = GetDlgItem(hwndDlg, IDC_CONVERT_COMBO);
586 
587                 if (num_writeable_devices == 0)
588                     num_writeable_devices = num_devices;
589 
590                 i = 0;
591                 while (convtypes[i] != 0) {
592                     if (!load_string(module, convtypes[i], s))
593                         throw last_error(GetLastError());
594 
595                     SendMessage(convcb, CB_ADDSTRING, 0, (LPARAM)s.c_str());
596 
597                     if (opts->convert == convtypes2[i])
598                         SendMessage(convcb, CB_SETCURSEL, i, 0);
599 
600                     i++;
601 
602                     if (num_writeable_devices < 2 && i == 2)
603                         break;
604                     else if (num_writeable_devices < 3 && i == 4)
605                         break;
606                     else if (num_writeable_devices < 4 && i == 6)
607                         break;
608                 }
609 
610                 // profiles
611 
612                 CheckDlgButton(hwndDlg, IDC_PROFILES, opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? BST_CHECKED : BST_UNCHECKED);
613                 CheckDlgButton(hwndDlg, IDC_PROFILES_SINGLE, opts->profiles & BLOCK_FLAG_SINGLE ? BST_CHECKED : BST_UNCHECKED);
614                 CheckDlgButton(hwndDlg, IDC_PROFILES_DUP, opts->profiles & BLOCK_FLAG_DUPLICATE ? BST_CHECKED : BST_UNCHECKED);
615                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID0, opts->profiles & BLOCK_FLAG_RAID0 ? BST_CHECKED : BST_UNCHECKED);
616                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1, opts->profiles & BLOCK_FLAG_RAID1 ? BST_CHECKED : BST_UNCHECKED);
617                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID10, opts->profiles & BLOCK_FLAG_RAID10 ? BST_CHECKED : BST_UNCHECKED);
618                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID5, opts->profiles & BLOCK_FLAG_RAID5 ? BST_CHECKED : BST_UNCHECKED);
619                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID6, opts->profiles & BLOCK_FLAG_RAID6 ? BST_CHECKED : BST_UNCHECKED);
620                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1C3, opts->profiles & BLOCK_FLAG_RAID1C3 ? BST_CHECKED : BST_UNCHECKED);
621                 CheckDlgButton(hwndDlg, IDC_PROFILES_RAID1C4, opts->profiles & BLOCK_FLAG_RAID1C4 ? BST_CHECKED : BST_UNCHECKED);
622 
623                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_SINGLE), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
624                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_DUP), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
625                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID0), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
626                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
627                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
628                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
629                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
630                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C3), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
631                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C4), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_PROFILES ? true : false);
632                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES), balance_started ? false : true);
633 
634                 // usage
635 
636                 CheckDlgButton(hwndDlg, IDC_USAGE, opts->flags & BTRFS_BALANCE_OPTS_USAGE ? BST_CHECKED : BST_UNCHECKED);
637 
638                 s = to_wstring(opts->usage_start);
639                 SetDlgItemTextW(hwndDlg, IDC_USAGE_START, s.c_str());
640                 SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), UDM_SETRANGE32, 0, 100);
641 
642                 s = to_wstring(opts->usage_end);
643                 SetDlgItemTextW(hwndDlg, IDC_USAGE_END, s.c_str());
644                 SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), UDM_SETRANGE32, 0, 100);
645 
646                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
647                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
648                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
649                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
650                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE), balance_started ? false : true);
651 
652                 // devid
653 
654                 if (num_devices < 2 || balance_started)
655                     EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID), false);
656 
657                 CheckDlgButton(hwndDlg, IDC_DEVID, opts->flags & BTRFS_BALANCE_OPTS_DEVID ? BST_CHECKED : BST_UNCHECKED);
658                 EnableWindow(devcb, (opts->flags & BTRFS_BALANCE_OPTS_DEVID && num_devices >= 2 && !balance_started) ? true : false);
659 
660                 // drange
661 
662                 CheckDlgButton(hwndDlg, IDC_DRANGE, opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? BST_CHECKED : BST_UNCHECKED);
663 
664                 s = to_wstring(opts->drange_start);
665                 SetDlgItemTextW(hwndDlg, IDC_DRANGE_START, s.c_str());
666 
667                 s = to_wstring(opts->drange_end);
668                 SetDlgItemTextW(hwndDlg, IDC_DRANGE_END, s.c_str());
669 
670                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? true : false);
671                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? true : false);
672                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE), balance_started ? false : true);
673 
674                 // vrange
675 
676                 CheckDlgButton(hwndDlg, IDC_VRANGE, opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? BST_CHECKED : BST_UNCHECKED);
677 
678                 s = to_wstring(opts->vrange_start);
679                 SetDlgItemTextW(hwndDlg, IDC_VRANGE_START, s.c_str());
680 
681                 s = to_wstring(opts->vrange_end);
682                 SetDlgItemTextW(hwndDlg, IDC_VRANGE_END, s.c_str());
683 
684                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? true : false);
685                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? true : false);
686                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE), balance_started ? false : true);
687 
688                 // limit
689 
690                 CheckDlgButton(hwndDlg, IDC_LIMIT, opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? BST_CHECKED : BST_UNCHECKED);
691 
692                 s = to_wstring(opts->limit_start);
693                 SetDlgItemTextW(hwndDlg, IDC_LIMIT_START, s.c_str());
694                 SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff);
695 
696                 s = to_wstring(opts->limit_end);
697                 SetDlgItemTextW(hwndDlg, IDC_LIMIT_END, s.c_str());
698                 SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff);
699 
700                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
701                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
702                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
703                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
704                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT), balance_started ? false : true);
705 
706                 // stripes
707 
708                 CheckDlgButton(hwndDlg, IDC_STRIPES, opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? BST_CHECKED : BST_UNCHECKED);
709 
710                 s = to_wstring(opts->stripes_start);
711                 SetDlgItemTextW(hwndDlg, IDC_STRIPES_START, s.c_str());
712                 SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), UDM_SETRANGE32, 0, 0xffff);
713 
714                 s = to_wstring(opts->stripes_end);
715                 SetDlgItemTextW(hwndDlg, IDC_STRIPES_END, s.c_str());
716                 SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), UDM_SETRANGE32, 0, 0xffff);
717 
718                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
719                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
720                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
721                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
722                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES), balance_started ? false : true);
723 
724                 // convert
725 
726                 CheckDlgButton(hwndDlg, IDC_CONVERT, opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? BST_CHECKED : BST_UNCHECKED);
727                 CheckDlgButton(hwndDlg, IDC_SOFT, opts->flags & BTRFS_BALANCE_OPTS_SOFT ? BST_CHECKED : BST_UNCHECKED);
728 
729                 EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? true : false);
730                 EnableWindow(convcb, !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? true : false);
731                 EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT), balance_started ? false : true);
732 
733                 break;
734             }
735 
736             case WM_COMMAND:
737                 switch (HIWORD(wParam)) {
738                     case BN_CLICKED:
739                         switch (LOWORD(wParam)) {
740                             case IDOK:
741                                 if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))
742                                     EndDialog(hwndDlg, 0);
743                                 else
744                                     SaveBalanceOpts(hwndDlg);
745                             return true;
746 
747                             case IDCANCEL:
748                                 EndDialog(hwndDlg, 0);
749                             return true;
750 
751                             case IDC_PROFILES: {
752                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_PROFILES) == BST_CHECKED ? true : false;
753 
754                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_SINGLE), enabled);
755                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_DUP), enabled);
756                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID0), enabled);
757                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1), enabled);
758                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), enabled);
759                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), enabled);
760                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), enabled);
761                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C3), enabled);
762                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1C4), enabled);
763                                 break;
764                             }
765 
766                             case IDC_USAGE: {
767                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_USAGE) == BST_CHECKED ? true : false;
768 
769                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), enabled);
770                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), enabled);
771                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), enabled);
772                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), enabled);
773                                 break;
774                             }
775 
776                             case IDC_DEVID: {
777                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED ? true : false;
778 
779                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID_COMBO), enabled);
780                                 break;
781                             }
782 
783                             case IDC_DRANGE: {
784                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_DRANGE) == BST_CHECKED ? true : false;
785 
786                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), enabled);
787                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), enabled);
788                                 break;
789                             }
790 
791                             case IDC_VRANGE: {
792                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_VRANGE) == BST_CHECKED ? true : false;
793 
794                                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), enabled);
795                                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), enabled);
796                                 break;
797                             }
798 
799                             case IDC_LIMIT: {
800                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_LIMIT) == BST_CHECKED ? true : false;
801 
802                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), enabled);
803                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), enabled);
804                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), enabled);
805                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), enabled);
806                                 break;
807                             }
808 
809                             case IDC_STRIPES: {
810                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_STRIPES) == BST_CHECKED ? true : false;
811 
812                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), enabled);
813                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), enabled);
814                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), enabled);
815                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), enabled);
816                                 break;
817                             }
818 
819                             case IDC_CONVERT: {
820                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_CONVERT) == BST_CHECKED ? true : false;
821 
822                                 EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), enabled);
823                                 EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), enabled);
824                                 break;
825                             }
826                         }
827                     break;
828                 }
829             break;
830         }
831     } catch (const exception& e) {
832         error_message(hwndDlg, e.what());
833     }
834 
835     return false;
836 }
837 
838 static INT_PTR CALLBACK stub_BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
839     BtrfsBalance* bb;
840 
841     if (uMsg == WM_INITDIALOG) {
842         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
843         bb = (BtrfsBalance*)lParam;
844     } else {
845         bb = (BtrfsBalance*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
846     }
847 
848     if (bb)
849         return bb->BalanceOptsDlgProc(hwndDlg, uMsg, wParam, lParam);
850     else
851         return false;
852 }
853 
854 void BtrfsBalance::ShowBalanceOptions(HWND hwndDlg, uint8_t type) {
855     opts_type = type;
856     DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE_OPTIONS), hwndDlg, stub_BalanceOptsDlgProc, (LPARAM)this);
857 }
858 
859 INT_PTR CALLBACK BtrfsBalance::BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
860     try {
861         switch (uMsg) {
862             case WM_INITDIALOG:
863             {
864                 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
865 
866                 RtlZeroMemory(&data_opts, sizeof(btrfs_balance_opts));
867                 RtlZeroMemory(&metadata_opts, sizeof(btrfs_balance_opts));
868                 RtlZeroMemory(&system_opts, sizeof(btrfs_balance_opts));
869 
870                 removing = called_from_RemoveDevice;
871                 shrinking = called_from_ShrinkDevice;
872                 balance_status = (removing || shrinking) ? BTRFS_BALANCE_RUNNING : BTRFS_BALANCE_STOPPED;
873                 cancelling = false;
874                 RefreshBalanceDlg(hwndDlg, true);
875 
876                 if (readonly) {
877                     EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), false);
878                     EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), false);
879                     EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), false);
880                 }
881 
882                 SendMessageW(GetDlgItem(hwndDlg, IDC_START_BALANCE), BCM_SETSHIELD, 0, true);
883                 SendMessageW(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), BCM_SETSHIELD, 0, true);
884                 SendMessageW(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), BCM_SETSHIELD, 0, true);
885 
886                 SetTimer(hwndDlg, 1, 1000, nullptr);
887 
888                 break;
889             }
890 
891             case WM_COMMAND:
892                 switch (HIWORD(wParam)) {
893                     case BN_CLICKED:
894                         switch (LOWORD(wParam)) {
895                             case IDOK:
896                             case IDCANCEL:
897                                 KillTimer(hwndDlg, 1);
898                                 EndDialog(hwndDlg, 0);
899                             return true;
900 
901                             case IDC_DATA:
902                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ? true : false);
903 
904                                 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
905                                 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false);
906                             return true;
907 
908                             case IDC_METADATA:
909                                 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED ? true : false);
910 
911                                 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
912                                 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false);
913                             return true;
914 
915                             case IDC_SYSTEM:
916                                 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED ? true : false);
917 
918                                 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
919                                 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false);
920                             return true;
921 
922                             case IDC_DATA_OPTIONS:
923                                 ShowBalanceOptions(hwndDlg, 1);
924                             return true;
925 
926                             case IDC_METADATA_OPTIONS:
927                                 ShowBalanceOptions(hwndDlg, 2);
928                             return true;
929 
930                             case IDC_SYSTEM_OPTIONS:
931                                 ShowBalanceOptions(hwndDlg, 3);
932                             return true;
933 
934                             case IDC_START_BALANCE:
935                                 StartBalance(hwndDlg);
936                             return true;
937 
938                             case IDC_PAUSE_BALANCE:
939                                 PauseBalance(hwndDlg);
940                                 RefreshBalanceDlg(hwndDlg, false);
941                             return true;
942 
943                             case IDC_CANCEL_BALANCE:
944                                 StopBalance(hwndDlg);
945                                 RefreshBalanceDlg(hwndDlg, false);
946                             return true;
947                         }
948                     break;
949                 }
950             break;
951 
952             case WM_TIMER:
953                 RefreshBalanceDlg(hwndDlg, false);
954                 break;
955         }
956     } catch (const exception& e) {
957         error_message(hwndDlg, e.what());
958     }
959 
960     return false;
961 }
962 
963 static INT_PTR CALLBACK stub_BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
964     BtrfsBalance* bb;
965 
966     if (uMsg == WM_INITDIALOG) {
967         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
968         bb = (BtrfsBalance*)lParam;
969     } else {
970         bb = (BtrfsBalance*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
971     }
972 
973     if (bb)
974         return bb->BalanceDlgProc(hwndDlg, uMsg, wParam, lParam);
975     else
976         return false;
977 }
978 
979 void BtrfsBalance::ShowBalance(HWND hwndDlg) {
980     btrfs_device* bd;
981 
982     if (devices) {
983         free(devices);
984         devices = nullptr;
985     }
986 
987     {
988         win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
989                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
990 
991         if (h != INVALID_HANDLE_VALUE) {
992             NTSTATUS Status;
993             IO_STATUS_BLOCK iosb;
994             ULONG devsize, i;
995 
996             i = 0;
997             devsize = 1024;
998 
999             devices = (btrfs_device*)malloc(devsize);
1000 
1001             while (true) {
1002                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize);
1003                 if (Status == STATUS_BUFFER_OVERFLOW) {
1004                     if (i < 8) {
1005                         devsize += 1024;
1006 
1007                         free(devices);
1008                         devices = (btrfs_device*)malloc(devsize);
1009 
1010                         i++;
1011                     } else
1012                         return;
1013                 } else
1014                     break;
1015             }
1016 
1017             if (!NT_SUCCESS(Status))
1018                 throw ntstatus_error(Status);
1019         } else
1020             throw last_error(GetLastError());
1021     }
1022 
1023     readonly = true;
1024     bd = devices;
1025 
1026     while (true) {
1027         if (!bd->readonly) {
1028             readonly = false;
1029             break;
1030         }
1031 
1032         if (bd->next_entry > 0)
1033             bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry);
1034         else
1035             break;
1036     }
1037 
1038     DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE), hwndDlg, stub_BalanceDlgProc, (LPARAM)this);
1039 }
1040 
1041 static uint8_t from_hex_digit(WCHAR c) {
1042     if (c >= 'a' && c <= 'f')
1043         return (uint8_t)(c - 'a' + 0xa);
1044     else if (c >= 'A' && c <= 'F')
1045         return (uint8_t)(c - 'A' + 0xa);
1046     else
1047         return (uint8_t)(c - '0');
1048 }
1049 
1050 static void unserialize(void* data, ULONG len, WCHAR* s) {
1051     uint8_t* d;
1052 
1053     d = (uint8_t*)data;
1054 
1055     while (s[0] != 0 && s[1] != 0 && len > 0) {
1056         *d = (uint8_t)(from_hex_digit(s[0]) << 4) | from_hex_digit(s[1]);
1057 
1058         s += 2;
1059         d++;
1060         len--;
1061     }
1062 }
1063 
1064 #ifdef __REACTOS__
1065 extern "C" {
1066 #endif
1067 
1068 void CALLBACK StartBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1069     try {
1070         WCHAR *s, *vol, *block;
1071         win_handle h, token;
1072         btrfs_start_balance bsb;
1073         TOKEN_PRIVILEGES tp;
1074         LUID luid;
1075 
1076         s = wcsstr(lpszCmdLine, L" ");
1077         if (!s)
1078             return;
1079 
1080         s[0] = 0;
1081 
1082         vol = lpszCmdLine;
1083         block = &s[1];
1084 
1085         RtlZeroMemory(&bsb, sizeof(btrfs_start_balance));
1086         unserialize(&bsb, sizeof(btrfs_start_balance), block);
1087 
1088         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1089             throw last_error(GetLastError());
1090 
1091         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
1092             throw last_error(GetLastError());
1093 
1094         tp.PrivilegeCount = 1;
1095         tp.Privileges[0].Luid = luid;
1096         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1097 
1098         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
1099             throw last_error(GetLastError());
1100 
1101         h = CreateFileW(vol, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1102                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1103 
1104         if (h != INVALID_HANDLE_VALUE) {
1105             NTSTATUS Status;
1106             IO_STATUS_BLOCK iosb;
1107 
1108             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_START_BALANCE, &bsb, sizeof(btrfs_start_balance), nullptr, 0);
1109 
1110             if (Status == STATUS_DEVICE_NOT_READY) {
1111                 btrfs_query_scrub bqs;
1112                 NTSTATUS Status2;
1113 
1114                 Status2 = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, &bqs, sizeof(btrfs_query_scrub));
1115 
1116                 if ((NT_SUCCESS(Status2) || Status2 == STATUS_BUFFER_OVERFLOW) && bqs.status != BTRFS_SCRUB_STOPPED)
1117                     throw string_error(IDS_BALANCE_SCRUB_RUNNING);
1118             }
1119 
1120             if (!NT_SUCCESS(Status))
1121                 throw ntstatus_error(Status);
1122         } else
1123             throw last_error(GetLastError());
1124     } catch (const exception& e) {
1125         error_message(hwnd, e.what());
1126     }
1127 }
1128 
1129 void CALLBACK PauseBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1130     try {
1131         win_handle h, token;
1132         TOKEN_PRIVILEGES tp;
1133         LUID luid;
1134 
1135         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1136             throw last_error(GetLastError());
1137 
1138         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
1139             throw last_error(GetLastError());
1140 
1141         tp.PrivilegeCount = 1;
1142         tp.Privileges[0].Luid = luid;
1143         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1144 
1145         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
1146             throw last_error(GetLastError());
1147 
1148         h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1149                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1150 
1151         if (h != INVALID_HANDLE_VALUE) {
1152             NTSTATUS Status;
1153             IO_STATUS_BLOCK iosb;
1154             btrfs_query_balance bqb2;
1155 
1156             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb2, sizeof(btrfs_query_balance));
1157             if (!NT_SUCCESS(Status))
1158                 throw ntstatus_error(Status);
1159 
1160             if (bqb2.status & BTRFS_BALANCE_PAUSED)
1161                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESUME_BALANCE, nullptr, 0, nullptr, 0);
1162             else if (bqb2.status & BTRFS_BALANCE_RUNNING)
1163                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_PAUSE_BALANCE, nullptr, 0, nullptr, 0);
1164             else
1165                 return;
1166 
1167             if (!NT_SUCCESS(Status))
1168                 throw ntstatus_error(Status);
1169         } else
1170             throw last_error(GetLastError());
1171     } catch (const exception& e) {
1172         error_message(hwnd, e.what());
1173     }
1174 }
1175 
1176 void CALLBACK StopBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1177     try {
1178         win_handle h, token;
1179         TOKEN_PRIVILEGES tp;
1180         LUID luid;
1181 
1182         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1183             throw last_error(GetLastError());
1184 
1185         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
1186             throw last_error(GetLastError());
1187 
1188         tp.PrivilegeCount = 1;
1189         tp.Privileges[0].Luid = luid;
1190         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1191 
1192         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
1193             throw last_error(GetLastError());
1194 
1195         h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1196                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1197 
1198         if (h != INVALID_HANDLE_VALUE) {
1199             NTSTATUS Status;
1200             IO_STATUS_BLOCK iosb;
1201             btrfs_query_balance bqb2;
1202 
1203             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb2, sizeof(btrfs_query_balance));
1204             if (!NT_SUCCESS(Status))
1205                 throw ntstatus_error(Status);
1206 
1207             if (bqb2.status & BTRFS_BALANCE_PAUSED || bqb2.status & BTRFS_BALANCE_RUNNING)
1208                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_STOP_BALANCE, nullptr, 0, nullptr, 0);
1209             else
1210                 return;
1211 
1212             if (!NT_SUCCESS(Status))
1213                 throw ntstatus_error(Status);
1214         } else
1215             throw last_error(GetLastError());
1216     } catch (const exception& e) {
1217         error_message(hwnd, e.what());
1218     }
1219 }
1220 
1221 #ifdef __REACTOS__
1222 } /* extern "C" */
1223 #endif
1224