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