xref: /reactos/dll/shellext/shellbtrfs/balance.cpp (revision 5bb277a5)
1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "shellext.h"
19 #include "balance.h"
20 #include "resource.h"
21 #ifndef __REACTOS__
22 #include "../btrfsioctl.h"
23 #else
24 #include "btrfsioctl.h"
25 #endif
26 #include <shlobj.h>
27 #include <uxtheme.h>
28 #include <stdio.h>
29 #ifndef __REACTOS__
30 #include <strsafe.h>
31 #include <winternl.h>
32 #else
33 #define WIN32_NO_STATUS
34 #include <windef.h>
35 #include <winbase.h>
36 #include <strsafe.h>
37 #include <ndk/iofuncs.h>
38 #include <ndk/iotypes.h>
39 #endif
40 
41 #define NO_SHLWAPI_STRFCNS
42 #include <shlwapi.h>
43 #include <uxtheme.h>
44 
45 static uint64_t convtypes2[] = { BLOCK_FLAG_SINGLE, BLOCK_FLAG_DUPLICATE, BLOCK_FLAG_RAID0, BLOCK_FLAG_RAID1, BLOCK_FLAG_RAID5, BLOCK_FLAG_RAID6, BLOCK_FLAG_RAID10 };
46 
47 static WCHAR hex_digit(uint8_t u) {
48     if (u >= 0xa && u <= 0xf)
49         return (uint8_t)(u - 0xa + 'a');
50     else
51         return (uint8_t)(u + '0');
52 }
53 
54 static void serialize(void* data, ULONG len, WCHAR* s) {
55     uint8_t* d;
56 
57     d = (uint8_t*)data;
58 
59     while (true) {
60         *s = hex_digit((uint8_t)(*d >> 4)); s++;
61         *s = hex_digit(*d & 0xf); s++;
62 
63         d++;
64         len--;
65 
66         if (len == 0) {
67             *s = 0;
68             return;
69         }
70     }
71 }
72 
73 void BtrfsBalance::StartBalance(HWND hwndDlg) {
74     wstring t;
75     WCHAR modfn[MAX_PATH], u[600];
76     SHELLEXECUTEINFOW sei;
77     btrfs_start_balance bsb;
78 
79     GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR));
80 
81 #ifndef __REACTOS__
82     t = L"\""s + modfn + L"\",StartBalance "s + fn + L" "s;
83 #else
84     t = wstring(L"\"") + modfn + wstring(L"\",StartBalance ") + fn + wstring(L" ");
85 #endif
86 
87     RtlCopyMemory(&bsb.opts[0], &data_opts, sizeof(btrfs_balance_opts));
88     RtlCopyMemory(&bsb.opts[1], &metadata_opts, sizeof(btrfs_balance_opts));
89     RtlCopyMemory(&bsb.opts[2], &system_opts, sizeof(btrfs_balance_opts));
90 
91     if (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED)
92         bsb.opts[0].flags |= BTRFS_BALANCE_OPTS_ENABLED;
93     else
94         bsb.opts[0].flags &= ~BTRFS_BALANCE_OPTS_ENABLED;
95 
96     if (IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED)
97         bsb.opts[1].flags |= BTRFS_BALANCE_OPTS_ENABLED;
98     else
99         bsb.opts[1].flags &= ~BTRFS_BALANCE_OPTS_ENABLED;
100 
101     if (IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED)
102         bsb.opts[2].flags |= BTRFS_BALANCE_OPTS_ENABLED;
103     else
104         bsb.opts[2].flags &= ~BTRFS_BALANCE_OPTS_ENABLED;
105 
106     serialize(&bsb, sizeof(btrfs_start_balance), u);
107 
108     t += u;
109 
110     RtlZeroMemory(&sei, sizeof(sei));
111 
112     sei.cbSize = sizeof(sei);
113     sei.hwnd = hwndDlg;
114     sei.lpVerb = L"runas";
115     sei.lpFile = L"rundll32.exe";
116     sei.lpParameters = t.c_str();
117     sei.nShow = SW_SHOW;
118     sei.fMask = SEE_MASK_NOCLOSEPROCESS;
119 
120     if (!ShellExecuteExW(&sei))
121         throw last_error(GetLastError());
122 
123     cancelling = false;
124     removing = false;
125     shrinking = false;
126     balance_status = BTRFS_BALANCE_RUNNING;
127 
128     EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), true);
129     EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), true);
130     EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), true);
131     EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), false);
132     EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), false);
133     EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), false);
134     EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false);
135     EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false);
136     EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false);
137 
138     EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), false);
139 
140     WaitForSingleObject(sei.hProcess, INFINITE);
141     CloseHandle(sei.hProcess);
142 }
143 
144 void BtrfsBalance::PauseBalance(HWND hwndDlg) {
145     WCHAR modfn[MAX_PATH];
146     wstring t;
147     SHELLEXECUTEINFOW sei;
148 
149     GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR));
150 
151 #ifndef __REACTOS__
152     t = L"\""s + modfn + L"\",PauseBalance " + fn;
153 #else
154     t = wstring(L"\"") + modfn + wstring(L"\",PauseBalance ") + fn;
155 #endif
156 
157     RtlZeroMemory(&sei, sizeof(sei));
158 
159     sei.cbSize = sizeof(sei);
160     sei.hwnd = hwndDlg;
161     sei.lpVerb = L"runas";
162     sei.lpFile = L"rundll32.exe";
163     sei.lpParameters = t.c_str();
164     sei.nShow = SW_SHOW;
165     sei.fMask = SEE_MASK_NOCLOSEPROCESS;
166 
167     if (!ShellExecuteExW(&sei))
168         throw last_error(GetLastError());
169 
170     WaitForSingleObject(sei.hProcess, INFINITE);
171     CloseHandle(sei.hProcess);
172 }
173 
174 void BtrfsBalance::StopBalance(HWND hwndDlg) {
175     WCHAR modfn[MAX_PATH];
176     wstring t;
177     SHELLEXECUTEINFOW sei;
178 
179     GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR));
180 
181 #ifndef __REACTOS__
182     t = L"\""s + modfn + L"\",StopBalance " + fn;
183 #else
184     t = wstring(L"\"") + modfn + wstring(L"\",StopBalance ") + fn;
185 #endif
186 
187     RtlZeroMemory(&sei, sizeof(sei));
188 
189     sei.cbSize = sizeof(sei);
190     sei.hwnd = hwndDlg;
191     sei.lpVerb = L"runas";
192     sei.lpFile = L"rundll32.exe";
193     sei.lpParameters = t.c_str();
194     sei.nShow = SW_SHOW;
195     sei.fMask = SEE_MASK_NOCLOSEPROCESS;
196 
197     if (!ShellExecuteExW(&sei))
198         throw last_error(GetLastError());
199 
200     cancelling = true;
201 
202     WaitForSingleObject(sei.hProcess, INFINITE);
203     CloseHandle(sei.hProcess);
204 }
205 
206 void BtrfsBalance::RefreshBalanceDlg(HWND hwndDlg, bool first) {
207     bool balancing = false;
208     wstring s, t;
209 
210     {
211         win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
212                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
213 
214         if (h != INVALID_HANDLE_VALUE) {
215             NTSTATUS Status;
216             IO_STATUS_BLOCK iosb;
217 
218             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb, sizeof(btrfs_query_balance));
219 
220             if (!NT_SUCCESS(Status))
221                 throw ntstatus_error(Status);
222         } else
223             throw last_error(GetLastError());
224     }
225 
226     if (cancelling)
227         bqb.status = BTRFS_BALANCE_STOPPED;
228 
229     balancing = bqb.status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED);
230 
231     if (!balancing) {
232         if (first || balance_status != BTRFS_BALANCE_STOPPED) {
233             int resid;
234 
235             EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), false);
236             EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), false);
237             SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
238             EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), false);
239             EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), true);
240             EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), true);
241             EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), true);
242 
243             if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED)) {
244                 CheckDlgButton(hwndDlg, IDC_DATA, BST_UNCHECKED);
245                 CheckDlgButton(hwndDlg, IDC_METADATA, BST_UNCHECKED);
246                 CheckDlgButton(hwndDlg, IDC_SYSTEM, BST_UNCHECKED);
247 
248                 SendMessage(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETPOS, 0, 0);
249             }
250 
251             EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ? true : false);
252             EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED ? true : false);
253             EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED ? true : false);
254 
255             if (bqb.status & BTRFS_BALANCE_ERROR) {
256                 if (removing)
257                     resid = IDS_BALANCE_FAILED_REMOVAL;
258                 else if (shrinking)
259                     resid = IDS_BALANCE_FAILED_SHRINK;
260                 else
261                     resid = IDS_BALANCE_FAILED;
262 
263                 if (!load_string(module, resid, s))
264                     throw last_error(GetLastError());
265 
266                 wstring_sprintf(t, s, bqb.error, format_ntstatus(bqb.error).c_str());
267 
268                 SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, t.c_str());
269             } else {
270                 if (cancelling)
271                     resid = removing ? IDS_BALANCE_CANCELLED_REMOVAL : (shrinking ? IDS_BALANCE_CANCELLED_SHRINK : IDS_BALANCE_CANCELLED);
272                 else if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))
273                     resid = removing ? IDS_BALANCE_COMPLETE_REMOVAL : (shrinking ? IDS_BALANCE_COMPLETE_SHRINK : IDS_BALANCE_COMPLETE);
274                 else
275                     resid = IDS_NO_BALANCE;
276 
277                 if (!load_string(module, resid, s))
278                     throw last_error(GetLastError());
279 
280                 SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, s.c_str());
281             }
282 
283             EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
284                          IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false);
285 
286             balance_status = bqb.status;
287             cancelling = false;
288         }
289 
290         return;
291     }
292 
293     if (first || !(balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))) {
294         EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), true);
295         EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), true);
296         EnableWindow(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), true);
297         EnableWindow(GetDlgItem(hwndDlg, IDC_DATA), false);
298         EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA), false);
299         EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM), false);
300 
301         CheckDlgButton(hwndDlg, IDC_DATA, bqb.data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED);
302         CheckDlgButton(hwndDlg, IDC_METADATA, bqb.metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED);
303         CheckDlgButton(hwndDlg, IDC_SYSTEM, bqb.system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? BST_CHECKED : BST_UNCHECKED);
304 
305         EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), bqb.data_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false);
306         EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), bqb.metadata_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false);
307         EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), bqb.system_opts.flags & BTRFS_BALANCE_OPTS_ENABLED ? true : false);
308 
309         EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), false);
310     }
311 
312     SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)bqb.total_chunks);
313     SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETPOS, (WPARAM)(bqb.total_chunks - bqb.chunks_left), 0);
314 
315     if (bqb.status & BTRFS_BALANCE_PAUSED && balance_status != bqb.status)
316         SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_PAUSED, 0);
317     else if (!(bqb.status & BTRFS_BALANCE_PAUSED) && balance_status & BTRFS_BALANCE_PAUSED)
318         SendMessageW(GetDlgItem(hwndDlg, IDC_BALANCE_PROGRESS), PBM_SETSTATE, PBST_NORMAL, 0);
319 
320     balance_status = bqb.status;
321 
322     if (bqb.status & BTRFS_BALANCE_REMOVAL) {
323         if (!load_string(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED_REMOVAL : IDS_BALANCE_RUNNING_REMOVAL, s))
324             throw last_error(GetLastError());
325 
326         wstring_sprintf(t, s, bqb.data_opts.devid, bqb.total_chunks - bqb.chunks_left, bqb.total_chunks,
327                         (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks);
328 
329         removing = true;
330         shrinking = false;
331     } else if (bqb.status & BTRFS_BALANCE_SHRINKING) {
332         if (!load_string(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED_SHRINK : IDS_BALANCE_RUNNING_SHRINK, s))
333             throw last_error(GetLastError());
334 
335         wstring_sprintf(t, s, bqb.data_opts.devid, bqb.total_chunks - bqb.chunks_left, bqb.total_chunks,
336                         (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks);
337 
338         removing = false;
339         shrinking = true;
340     } else {
341         if (!load_string(module, balance_status & BTRFS_BALANCE_PAUSED ? IDS_BALANCE_PAUSED : IDS_BALANCE_RUNNING, s))
342             throw last_error(GetLastError());
343 
344         wstring_sprintf(t, s, bqb.total_chunks - bqb.chunks_left, bqb.total_chunks,
345                         (float)(bqb.total_chunks - bqb.chunks_left) * 100.0f / (float)bqb.total_chunks);
346 
347         removing = false;
348         shrinking = false;
349     }
350 
351     SetDlgItemTextW(hwndDlg, IDC_BALANCE_STATUS, t.c_str());
352 }
353 
354 void BtrfsBalance::SaveBalanceOpts(HWND hwndDlg) {
355     btrfs_balance_opts* opts;
356 
357     switch (opts_type) {
358         case 1:
359             opts = &data_opts;
360         break;
361 
362         case 2:
363             opts = &metadata_opts;
364         break;
365 
366         case 3:
367             opts = &system_opts;
368         break;
369 
370         default:
371             return;
372     }
373 
374     RtlZeroMemory(opts, sizeof(btrfs_balance_opts));
375 
376     if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES) == BST_CHECKED) {
377         opts->flags |= BTRFS_BALANCE_OPTS_PROFILES;
378 
379         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_SINGLE) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_SINGLE;
380         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_DUP) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_DUPLICATE;
381         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID0) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID0;
382         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID1) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID1;
383         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID10) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID10;
384         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID5) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID5;
385         if (IsDlgButtonChecked(hwndDlg, IDC_PROFILES_RAID6) == BST_CHECKED) opts->profiles |= BLOCK_FLAG_RAID6;
386     }
387 
388     if (IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED) {
389         int sel;
390 
391         opts->flags |= BTRFS_BALANCE_OPTS_DEVID;
392 
393         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         int sel;
497 
498         opts->flags |= BTRFS_BALANCE_OPTS_CONVERT;
499 
500         sel = SendMessageW(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), CB_GETCURSEL, 0, 0);
501 
502         if (sel == CB_ERR || (unsigned int)sel >= sizeof(convtypes2) / sizeof(convtypes2[0]))
503             opts->flags &= ~BTRFS_BALANCE_OPTS_CONVERT;
504         else {
505             opts->convert = convtypes2[sel];
506 
507             if (IsDlgButtonChecked(hwndDlg, IDC_SOFT) == BST_CHECKED) opts->flags |= BTRFS_BALANCE_OPTS_SOFT;
508         }
509     }
510 
511     EndDialog(hwndDlg, 0);
512 }
513 
514 INT_PTR CALLBACK BtrfsBalance::BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
515     try {
516         switch (uMsg) {
517             case WM_INITDIALOG:
518             {
519                 HWND devcb, convcb;
520                 btrfs_device* bd;
521                 btrfs_balance_opts* opts;
522                 static int convtypes[] = { IDS_SINGLE2, IDS_DUP, IDS_RAID0, IDS_RAID1, IDS_RAID5, IDS_RAID6, IDS_RAID10, 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                     SendMessage(devcb, CB_ADDSTRING, 0, (LPARAM)t.c_str());
572 
573                     if (opts->devid == bd->dev_id)
574                         SendMessage(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                     SendMessage(convcb, CB_ADDSTRING, 0, (LPARAM)s.c_str());
598 
599                     if (opts->convert == convtypes2[i])
600                         SendMessage(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 == 5)
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 
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), balance_started ? false : true);
631 
632                 // usage
633 
634                 CheckDlgButton(hwndDlg, IDC_USAGE, opts->flags & BTRFS_BALANCE_OPTS_USAGE ? BST_CHECKED : BST_UNCHECKED);
635 
636                 s = to_wstring(opts->usage_start);
637                 SetDlgItemTextW(hwndDlg, IDC_USAGE_START, s.c_str());
638                 SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), UDM_SETRANGE32, 0, 100);
639 
640                 s = to_wstring(opts->usage_end);
641                 SetDlgItemTextW(hwndDlg, IDC_USAGE_END, s.c_str());
642                 SendMessageW(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), UDM_SETRANGE32, 0, 100);
643 
644                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
645                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
646                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
647                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_USAGE ? true : false);
648                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE), balance_started ? false : true);
649 
650                 // devid
651 
652                 if (num_devices < 2 || balance_started)
653                     EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID), false);
654 
655                 CheckDlgButton(hwndDlg, IDC_DEVID, opts->flags & BTRFS_BALANCE_OPTS_DEVID ? BST_CHECKED : BST_UNCHECKED);
656                 EnableWindow(devcb, (opts->flags & BTRFS_BALANCE_OPTS_DEVID && num_devices >= 2 && !balance_started) ? true : false);
657 
658                 // drange
659 
660                 CheckDlgButton(hwndDlg, IDC_DRANGE, opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? BST_CHECKED : BST_UNCHECKED);
661 
662                 s = to_wstring(opts->drange_start);
663                 SetDlgItemTextW(hwndDlg, IDC_DRANGE_START, s.c_str());
664 
665                 s = to_wstring(opts->drange_end);
666                 SetDlgItemTextW(hwndDlg, IDC_DRANGE_END, s.c_str());
667 
668                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? true : false);
669                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_DRANGE ? true : false);
670                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE), balance_started ? false : true);
671 
672                 // vrange
673 
674                 CheckDlgButton(hwndDlg, IDC_VRANGE, opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? BST_CHECKED : BST_UNCHECKED);
675 
676                 s = to_wstring(opts->vrange_start);
677                 SetDlgItemTextW(hwndDlg, IDC_VRANGE_START, s.c_str());
678 
679                 s = to_wstring(opts->vrange_end);
680                 SetDlgItemTextW(hwndDlg, IDC_VRANGE_END, s.c_str());
681 
682                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? true : false);
683                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_VRANGE ? true : false);
684                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE), balance_started ? false : true);
685 
686                 // limit
687 
688                 CheckDlgButton(hwndDlg, IDC_LIMIT, opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? BST_CHECKED : BST_UNCHECKED);
689 
690                 s = to_wstring(opts->limit_start);
691                 SetDlgItemTextW(hwndDlg, IDC_LIMIT_START, s.c_str());
692                 SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff);
693 
694                 s = to_wstring(opts->limit_end);
695                 SetDlgItemTextW(hwndDlg, IDC_LIMIT_END, s.c_str());
696                 SendMessageW(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), UDM_SETRANGE32, 0, 0x7fffffff);
697 
698                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
699                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
700                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
701                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_LIMIT ? true : false);
702                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT), balance_started ? false : true);
703 
704                 // stripes
705 
706                 CheckDlgButton(hwndDlg, IDC_STRIPES, opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? BST_CHECKED : BST_UNCHECKED);
707 
708                 s = to_wstring(opts->stripes_start);
709                 SetDlgItemTextW(hwndDlg, IDC_STRIPES_START, s.c_str());
710                 SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), UDM_SETRANGE32, 0, 0xffff);
711 
712                 s = to_wstring(opts->stripes_end);
713                 SetDlgItemTextW(hwndDlg, IDC_STRIPES_END, s.c_str());
714                 SendMessageW(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), UDM_SETRANGE32, 0, 0xffff);
715 
716                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
717                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
718                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
719                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_STRIPES ? true : false);
720                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES), balance_started ? false : true);
721 
722                 // convert
723 
724                 CheckDlgButton(hwndDlg, IDC_CONVERT, opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? BST_CHECKED : BST_UNCHECKED);
725                 CheckDlgButton(hwndDlg, IDC_SOFT, opts->flags & BTRFS_BALANCE_OPTS_SOFT ? BST_CHECKED : BST_UNCHECKED);
726 
727                 EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? true : false);
728                 EnableWindow(convcb, !balance_started && opts->flags & BTRFS_BALANCE_OPTS_CONVERT ? true : false);
729                 EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT), balance_started ? false : true);
730 
731                 break;
732             }
733 
734             case WM_COMMAND:
735                 switch (HIWORD(wParam)) {
736                     case BN_CLICKED:
737                         switch (LOWORD(wParam)) {
738                             case IDOK:
739                                 if (balance_status & (BTRFS_BALANCE_RUNNING | BTRFS_BALANCE_PAUSED))
740                                     EndDialog(hwndDlg, 0);
741                                 else
742                                     SaveBalanceOpts(hwndDlg);
743                             return true;
744 
745                             case IDCANCEL:
746                                 EndDialog(hwndDlg, 0);
747                             return true;
748 
749                             case IDC_PROFILES: {
750                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_PROFILES) == BST_CHECKED ? true : false;
751 
752                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_SINGLE), enabled);
753                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_DUP), enabled);
754                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID0), enabled);
755                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID1), enabled);
756                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID10), enabled);
757                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID5), enabled);
758                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILES_RAID6), enabled);
759                                 break;
760                             }
761 
762                             case IDC_USAGE: {
763                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_USAGE) == BST_CHECKED ? true : false;
764 
765                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START), enabled);
766                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_START_SPINNER), enabled);
767                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END), enabled);
768                                 EnableWindow(GetDlgItem(hwndDlg, IDC_USAGE_END_SPINNER), enabled);
769                                 break;
770                             }
771 
772                             case IDC_DEVID: {
773                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_DEVID) == BST_CHECKED ? true : false;
774 
775                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DEVID_COMBO), enabled);
776                                 break;
777                             }
778 
779                             case IDC_DRANGE: {
780                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_DRANGE) == BST_CHECKED ? true : false;
781 
782                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_START), enabled);
783                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DRANGE_END), enabled);
784                                 break;
785                             }
786 
787                             case IDC_VRANGE: {
788                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_VRANGE) == BST_CHECKED ? true : false;
789 
790                                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_START), enabled);
791                                 EnableWindow(GetDlgItem(hwndDlg, IDC_VRANGE_END), enabled);
792                                 break;
793                             }
794 
795                             case IDC_LIMIT: {
796                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_LIMIT) == BST_CHECKED ? true : false;
797 
798                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START), enabled);
799                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_START_SPINNER), enabled);
800                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END), enabled);
801                                 EnableWindow(GetDlgItem(hwndDlg, IDC_LIMIT_END_SPINNER), enabled);
802                                 break;
803                             }
804 
805                             case IDC_STRIPES: {
806                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_STRIPES) == BST_CHECKED ? true : false;
807 
808                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START), enabled);
809                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_START_SPINNER), enabled);
810                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END), enabled);
811                                 EnableWindow(GetDlgItem(hwndDlg, IDC_STRIPES_END_SPINNER), enabled);
812                                 break;
813                             }
814 
815                             case IDC_CONVERT: {
816                                 bool enabled = IsDlgButtonChecked(hwndDlg, IDC_CONVERT) == BST_CHECKED ? true : false;
817 
818                                 EnableWindow(GetDlgItem(hwndDlg, IDC_CONVERT_COMBO), enabled);
819                                 EnableWindow(GetDlgItem(hwndDlg, IDC_SOFT), enabled);
820                                 break;
821                             }
822                         }
823                     break;
824                 }
825             break;
826         }
827     } catch (const exception& e) {
828         error_message(hwndDlg, e.what());
829     }
830 
831     return false;
832 }
833 
834 static INT_PTR CALLBACK stub_BalanceOptsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
835     BtrfsBalance* bb;
836 
837     if (uMsg == WM_INITDIALOG) {
838         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
839         bb = (BtrfsBalance*)lParam;
840     } else {
841         bb = (BtrfsBalance*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
842     }
843 
844     if (bb)
845         return bb->BalanceOptsDlgProc(hwndDlg, uMsg, wParam, lParam);
846     else
847         return false;
848 }
849 
850 void BtrfsBalance::ShowBalanceOptions(HWND hwndDlg, uint8_t type) {
851     opts_type = type;
852     DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE_OPTIONS), hwndDlg, stub_BalanceOptsDlgProc, (LPARAM)this);
853 }
854 
855 INT_PTR CALLBACK BtrfsBalance::BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
856     try {
857         switch (uMsg) {
858             case WM_INITDIALOG:
859             {
860                 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
861 
862                 RtlZeroMemory(&data_opts, sizeof(btrfs_balance_opts));
863                 RtlZeroMemory(&metadata_opts, sizeof(btrfs_balance_opts));
864                 RtlZeroMemory(&system_opts, sizeof(btrfs_balance_opts));
865 
866                 removing = called_from_RemoveDevice;
867                 shrinking = called_from_ShrinkDevice;
868                 balance_status = (removing || shrinking) ? BTRFS_BALANCE_RUNNING : BTRFS_BALANCE_STOPPED;
869                 cancelling = false;
870                 RefreshBalanceDlg(hwndDlg, true);
871 
872                 if (readonly) {
873                     EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), false);
874                     EnableWindow(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), false);
875                     EnableWindow(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), false);
876                 }
877 
878                 SendMessageW(GetDlgItem(hwndDlg, IDC_START_BALANCE), BCM_SETSHIELD, 0, true);
879                 SendMessageW(GetDlgItem(hwndDlg, IDC_PAUSE_BALANCE), BCM_SETSHIELD, 0, true);
880                 SendMessageW(GetDlgItem(hwndDlg, IDC_CANCEL_BALANCE), BCM_SETSHIELD, 0, true);
881 
882                 SetTimer(hwndDlg, 1, 1000, nullptr);
883 
884                 break;
885             }
886 
887             case WM_COMMAND:
888                 switch (HIWORD(wParam)) {
889                     case BN_CLICKED:
890                         switch (LOWORD(wParam)) {
891                             case IDOK:
892                             case IDCANCEL:
893                                 KillTimer(hwndDlg, 1);
894                                 EndDialog(hwndDlg, 0);
895                             return true;
896 
897                             case IDC_DATA:
898                                 EnableWindow(GetDlgItem(hwndDlg, IDC_DATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ? true : false);
899 
900                                 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
901                                 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false);
902                             return true;
903 
904                             case IDC_METADATA:
905                                 EnableWindow(GetDlgItem(hwndDlg, IDC_METADATA_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED ? true : false);
906 
907                                 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
908                                 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false);
909                             return true;
910 
911                             case IDC_SYSTEM:
912                                 EnableWindow(GetDlgItem(hwndDlg, IDC_SYSTEM_OPTIONS), IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED ? true : false);
913 
914                                 EnableWindow(GetDlgItem(hwndDlg, IDC_START_BALANCE), !readonly && (IsDlgButtonChecked(hwndDlg, IDC_DATA) == BST_CHECKED ||
915                                 IsDlgButtonChecked(hwndDlg, IDC_METADATA) == BST_CHECKED || IsDlgButtonChecked(hwndDlg, IDC_SYSTEM) == BST_CHECKED) ? true: false);
916                             return true;
917 
918                             case IDC_DATA_OPTIONS:
919                                 ShowBalanceOptions(hwndDlg, 1);
920                             return true;
921 
922                             case IDC_METADATA_OPTIONS:
923                                 ShowBalanceOptions(hwndDlg, 2);
924                             return true;
925 
926                             case IDC_SYSTEM_OPTIONS:
927                                 ShowBalanceOptions(hwndDlg, 3);
928                             return true;
929 
930                             case IDC_START_BALANCE:
931                                 StartBalance(hwndDlg);
932                             return true;
933 
934                             case IDC_PAUSE_BALANCE:
935                                 PauseBalance(hwndDlg);
936                                 RefreshBalanceDlg(hwndDlg, false);
937                             return true;
938 
939                             case IDC_CANCEL_BALANCE:
940                                 StopBalance(hwndDlg);
941                                 RefreshBalanceDlg(hwndDlg, false);
942                             return true;
943                         }
944                     break;
945                 }
946             break;
947 
948             case WM_TIMER:
949                 RefreshBalanceDlg(hwndDlg, false);
950                 break;
951         }
952     } catch (const exception& e) {
953         error_message(hwndDlg, e.what());
954     }
955 
956     return false;
957 }
958 
959 static INT_PTR CALLBACK stub_BalanceDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
960     BtrfsBalance* bb;
961 
962     if (uMsg == WM_INITDIALOG) {
963         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
964         bb = (BtrfsBalance*)lParam;
965     } else {
966         bb = (BtrfsBalance*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
967     }
968 
969     if (bb)
970         return bb->BalanceDlgProc(hwndDlg, uMsg, wParam, lParam);
971     else
972         return false;
973 }
974 
975 void BtrfsBalance::ShowBalance(HWND hwndDlg) {
976     btrfs_device* bd;
977 
978     if (devices) {
979         free(devices);
980         devices = nullptr;
981     }
982 
983     {
984         win_handle h = CreateFileW(fn.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
985                                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
986 
987         if (h != INVALID_HANDLE_VALUE) {
988             NTSTATUS Status;
989             IO_STATUS_BLOCK iosb;
990             ULONG devsize, i;
991 
992             i = 0;
993             devsize = 1024;
994 
995             devices = (btrfs_device*)malloc(devsize);
996 
997             while (true) {
998                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_DEVICES, nullptr, 0, devices, devsize);
999                 if (Status == STATUS_BUFFER_OVERFLOW) {
1000                     if (i < 8) {
1001                         devsize += 1024;
1002 
1003                         free(devices);
1004                         devices = (btrfs_device*)malloc(devsize);
1005 
1006                         i++;
1007                     } else
1008                         return;
1009                 } else
1010                     break;
1011             }
1012 
1013             if (!NT_SUCCESS(Status))
1014                 throw ntstatus_error(Status);
1015         } else
1016             throw last_error(GetLastError());
1017     }
1018 
1019     readonly = true;
1020     bd = devices;
1021 
1022     while (true) {
1023         if (!bd->readonly) {
1024             readonly = false;
1025             break;
1026         }
1027 
1028         if (bd->next_entry > 0)
1029             bd = (btrfs_device*)((uint8_t*)bd + bd->next_entry);
1030         else
1031             break;
1032     }
1033 
1034     DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_BALANCE), hwndDlg, stub_BalanceDlgProc, (LPARAM)this);
1035 }
1036 
1037 static uint8_t from_hex_digit(WCHAR c) {
1038     if (c >= 'a' && c <= 'f')
1039         return (uint8_t)(c - 'a' + 0xa);
1040     else if (c >= 'A' && c <= 'F')
1041         return (uint8_t)(c - 'A' + 0xa);
1042     else
1043         return (uint8_t)(c - '0');
1044 }
1045 
1046 static void unserialize(void* data, ULONG len, WCHAR* s) {
1047     uint8_t* d;
1048 
1049     d = (uint8_t*)data;
1050 
1051     while (s[0] != 0 && s[1] != 0 && len > 0) {
1052         *d = (uint8_t)(from_hex_digit(s[0]) << 4) | from_hex_digit(s[1]);
1053 
1054         s += 2;
1055         d++;
1056         len--;
1057     }
1058 }
1059 
1060 #ifdef __REACTOS__
1061 extern "C" {
1062 #endif
1063 
1064 void CALLBACK StartBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1065     try {
1066         WCHAR *s, *vol, *block;
1067         win_handle h, token;
1068         btrfs_start_balance bsb;
1069         TOKEN_PRIVILEGES tp;
1070         LUID luid;
1071 
1072         s = wcsstr(lpszCmdLine, L" ");
1073         if (!s)
1074             return;
1075 
1076         s[0] = 0;
1077 
1078         vol = lpszCmdLine;
1079         block = &s[1];
1080 
1081         RtlZeroMemory(&bsb, sizeof(btrfs_start_balance));
1082         unserialize(&bsb, sizeof(btrfs_start_balance), block);
1083 
1084         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1085             throw last_error(GetLastError());
1086 
1087         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
1088             throw last_error(GetLastError());
1089 
1090         tp.PrivilegeCount = 1;
1091         tp.Privileges[0].Luid = luid;
1092         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1093 
1094         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
1095             throw last_error(GetLastError());
1096 
1097         h = CreateFileW(vol, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1098                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1099 
1100         if (h != INVALID_HANDLE_VALUE) {
1101             NTSTATUS Status;
1102             IO_STATUS_BLOCK iosb;
1103 
1104             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_START_BALANCE, &bsb, sizeof(btrfs_start_balance), nullptr, 0);
1105 
1106             if (Status == STATUS_DEVICE_NOT_READY) {
1107                 btrfs_query_scrub bqs;
1108                 NTSTATUS Status2;
1109 
1110                 Status2 = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_SCRUB, nullptr, 0, &bqs, sizeof(btrfs_query_scrub));
1111 
1112                 if ((NT_SUCCESS(Status2) || Status2 == STATUS_BUFFER_OVERFLOW) && bqs.status != BTRFS_SCRUB_STOPPED)
1113                     throw string_error(IDS_BALANCE_SCRUB_RUNNING);
1114             }
1115 
1116             if (!NT_SUCCESS(Status))
1117                 throw ntstatus_error(Status);
1118         } else
1119             throw last_error(GetLastError());
1120     } catch (const exception& e) {
1121         error_message(hwnd, e.what());
1122     }
1123 }
1124 
1125 void CALLBACK PauseBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1126     try {
1127         win_handle h, token;
1128         TOKEN_PRIVILEGES tp;
1129         LUID luid;
1130 
1131         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1132             throw last_error(GetLastError());
1133 
1134         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
1135             throw last_error(GetLastError());
1136 
1137         tp.PrivilegeCount = 1;
1138         tp.Privileges[0].Luid = luid;
1139         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1140 
1141         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
1142             throw last_error(GetLastError());
1143 
1144         h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1145                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1146 
1147         if (h != INVALID_HANDLE_VALUE) {
1148             NTSTATUS Status;
1149             IO_STATUS_BLOCK iosb;
1150             btrfs_query_balance bqb2;
1151 
1152             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb2, sizeof(btrfs_query_balance));
1153             if (!NT_SUCCESS(Status))
1154                 throw ntstatus_error(Status);
1155 
1156             if (bqb2.status & BTRFS_BALANCE_PAUSED)
1157                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESUME_BALANCE, nullptr, 0, nullptr, 0);
1158             else if (bqb2.status & BTRFS_BALANCE_RUNNING)
1159                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_PAUSE_BALANCE, nullptr, 0, nullptr, 0);
1160             else
1161                 return;
1162 
1163             if (!NT_SUCCESS(Status))
1164                 throw ntstatus_error(Status);
1165         } else
1166             throw last_error(GetLastError());
1167     } catch (const exception& e) {
1168         error_message(hwnd, e.what());
1169     }
1170 }
1171 
1172 void CALLBACK StopBalanceW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1173     try {
1174         win_handle h, token;
1175         TOKEN_PRIVILEGES tp;
1176         LUID luid;
1177 
1178         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1179             throw last_error(GetLastError());
1180 
1181         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
1182             throw last_error(GetLastError());
1183 
1184         tp.PrivilegeCount = 1;
1185         tp.Privileges[0].Luid = luid;
1186         tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1187 
1188         if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
1189             throw last_error(GetLastError());
1190 
1191         h = CreateFileW(lpszCmdLine, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1192                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1193 
1194         if (h != INVALID_HANDLE_VALUE) {
1195             NTSTATUS Status;
1196             IO_STATUS_BLOCK iosb;
1197             btrfs_query_balance bqb2;
1198 
1199             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_QUERY_BALANCE, nullptr, 0, &bqb2, sizeof(btrfs_query_balance));
1200             if (!NT_SUCCESS(Status))
1201                 throw ntstatus_error(Status);
1202 
1203             if (bqb2.status & BTRFS_BALANCE_PAUSED || bqb2.status & BTRFS_BALANCE_RUNNING)
1204                 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_STOP_BALANCE, nullptr, 0, nullptr, 0);
1205             else
1206                 return;
1207 
1208             if (!NT_SUCCESS(Status))
1209                 throw ntstatus_error(Status);
1210         } else
1211             throw last_error(GetLastError());
1212     } catch (const exception& e) {
1213         error_message(hwnd, e.what());
1214     }
1215 }
1216 
1217 #ifdef __REACTOS__
1218 } /* extern "C" */
1219 #endif
1220