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