1 /* Copyright (c) Mark Harmstone 2017
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 "send.h"
20 #include "resource.h"
21 #include <stddef.h>
22 #include <shlobj.h>
23 #ifdef __REACTOS__
24 #undef DeleteFile
25 #endif
26 #include <iostream>
27
28 #define SEND_BUFFER_LEN 1048576
29
Thread()30 DWORD BtrfsSend::Thread() {
31 try {
32 NTSTATUS Status;
33 IO_STATUS_BLOCK iosb;
34 btrfs_send_subvol* bss;
35 btrfs_send_header header;
36 btrfs_send_command end;
37 ULONG i;
38
39 buf = (char*)malloc(SEND_BUFFER_LEN);
40
41 try {
42 dirh = CreateFileW(subvol.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
43 if (dirh == INVALID_HANDLE_VALUE)
44 throw string_error(IDS_SEND_CANT_OPEN_DIR, subvol.c_str(), GetLastError(), format_message(GetLastError()).c_str());
45
46 try {
47 size_t bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE));
48 bss = (btrfs_send_subvol*)malloc(bss_size);
49 memset(bss, 0, bss_size);
50
51 if (incremental) {
52 WCHAR parent[MAX_PATH];
53 HANDLE parenth;
54
55 parent[0] = 0;
56
57 GetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent, sizeof(parent) / sizeof(WCHAR));
58
59 parenth = CreateFileW(parent, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
60 if (parenth == INVALID_HANDLE_VALUE)
61 throw string_error(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str());
62
63 bss->parent = parenth;
64 } else
65 bss->parent = nullptr;
66
67 bss->num_clones = (ULONG)clones.size();
68
69 for (i = 0; i < bss->num_clones; i++) {
70 HANDLE h;
71
72 h = CreateFileW(clones[i].c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
73 if (h == INVALID_HANDLE_VALUE) {
74 auto le = GetLastError();
75 ULONG j;
76
77 for (j = 0; j < i; j++) {
78 CloseHandle(bss->clones[j]);
79 }
80
81 if (bss->parent) CloseHandle(bss->parent);
82
83 throw string_error(IDS_SEND_CANT_OPEN_DIR, clones[i].c_str(), le, format_message(le).c_str());
84 }
85
86 bss->clones[i] = h;
87 }
88
89 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, (ULONG)bss_size, nullptr, 0);
90
91 for (i = 0; i < bss->num_clones; i++) {
92 CloseHandle(bss->clones[i]);
93 }
94
95 if (!NT_SUCCESS(Status)) {
96 if (Status == (NTSTATUS)STATUS_INVALID_PARAMETER) {
97 BY_HANDLE_FILE_INFORMATION fileinfo;
98 if (!GetFileInformationByHandle(dirh, &fileinfo)) {
99 auto le = GetLastError();
100 if (bss->parent) CloseHandle(bss->parent);
101 throw string_error(IDS_SEND_GET_FILE_INFO_FAILED, le, format_message(le).c_str());
102 }
103
104 if (!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
105 if (bss->parent) CloseHandle(bss->parent);
106 throw string_error(IDS_SEND_NOT_READONLY);
107 }
108
109 if (bss->parent) {
110 if (!GetFileInformationByHandle(bss->parent, &fileinfo)) {
111 auto le = GetLastError();
112 CloseHandle(bss->parent);
113 throw string_error(IDS_SEND_GET_FILE_INFO_FAILED, le, format_message(le).c_str());
114 }
115
116 if (!(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
117 CloseHandle(bss->parent);
118 throw string_error(IDS_SEND_PARENT_NOT_READONLY);
119 }
120 }
121 }
122
123 if (bss->parent) CloseHandle(bss->parent);
124 throw string_error(IDS_SEND_FSCTL_BTRFS_SEND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
125 }
126
127 if (bss->parent) CloseHandle(bss->parent);
128
129 stream = CreateFileW(file, FILE_WRITE_DATA | DELETE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
130 if (stream == INVALID_HANDLE_VALUE)
131 throw string_error(IDS_SEND_CANT_OPEN_FILE, file, GetLastError(), format_message(GetLastError()).c_str());
132
133 try {
134 memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic));
135 header.version = 1;
136
137 if (!WriteFile(stream, &header, sizeof(header), nullptr, nullptr))
138 throw string_error(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
139
140 do {
141 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, nullptr, 0, buf, SEND_BUFFER_LEN);
142
143 if (NT_SUCCESS(Status)) {
144 if (!WriteFile(stream, buf, (DWORD)iosb.Information, nullptr, nullptr))
145 throw string_error(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
146 }
147 } while (NT_SUCCESS(Status));
148
149 if (Status != STATUS_END_OF_FILE)
150 throw string_error(IDS_SEND_FSCTL_BTRFS_READ_SEND_BUFFER_FAILED, Status, format_ntstatus(Status).c_str());
151
152 end.length = 0;
153 end.cmd = BTRFS_SEND_CMD_END;
154 end.csum = 0x9dc96c50;
155
156 if (!WriteFile(stream, &end, sizeof(end), nullptr, nullptr))
157 throw string_error(IDS_SEND_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
158
159 SetEndOfFile(stream);
160 } catch (...) {
161 FILE_DISPOSITION_INFO fdi;
162
163 fdi.DeleteFile = true;
164
165 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation);
166
167 CloseHandle(stream);
168 stream = INVALID_HANDLE_VALUE;
169
170 if (!NT_SUCCESS(Status))
171 throw ntstatus_error(Status);
172
173 throw;
174 }
175
176 CloseHandle(stream);
177 stream = INVALID_HANDLE_VALUE;
178 } catch (...) {
179 CloseHandle(dirh);
180 dirh = INVALID_HANDLE_VALUE;
181
182 throw;
183 }
184
185 CloseHandle(dirh);
186 dirh = INVALID_HANDLE_VALUE;
187 } catch (...) {
188 free(buf);
189 buf = nullptr;
190
191 started = false;
192
193 SetDlgItemTextW(hwnd, IDCANCEL, closetext);
194 EnableWindow(GetDlgItem(hwnd, IDOK), true);
195 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true);
196 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true);
197
198 throw;
199 }
200 } catch (const exception& e) {
201 auto msg = utf8_to_utf16(e.what());
202
203 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, msg.c_str());
204 return 0;
205 }
206
207
208 free(buf);
209 buf = nullptr;
210
211 started = false;
212
213 SetDlgItemTextW(hwnd, IDCANCEL, closetext);
214 EnableWindow(GetDlgItem(hwnd, IDOK), true);
215 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true);
216 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true);
217
218 wstring success;
219
220 load_string(module, IDS_SEND_SUCCESS, success);
221
222 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, success.c_str());
223
224 return 0;
225 }
226
send_thread(LPVOID lpParameter)227 static DWORD WINAPI send_thread(LPVOID lpParameter) {
228 BtrfsSend* bs = (BtrfsSend*)lpParameter;
229
230 return bs->Thread();
231 }
232
StartSend(HWND hwnd)233 void BtrfsSend::StartSend(HWND hwnd) {
234 wstring s;
235 HWND cl;
236
237 if (started)
238 return;
239
240 GetDlgItemTextW(hwnd, IDC_STREAM_DEST, file, sizeof(file) / sizeof(WCHAR));
241
242 if (file[0] == 0)
243 return;
244
245 if (incremental) {
246 WCHAR parent[MAX_PATH];
247
248 GetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent, sizeof(parent) / sizeof(WCHAR));
249
250 if (parent[0] == 0)
251 return;
252 }
253
254 started = true;
255
256 wstring writing;
257
258 load_string(module, IDS_SEND_WRITING, writing);
259
260 SetDlgItemTextW(hwnd, IDC_SEND_STATUS, writing.c_str());
261
262 load_string(module, IDS_SEND_CANCEL, s);
263 SetDlgItemTextW(hwnd, IDCANCEL, s.c_str());
264
265 EnableWindow(GetDlgItem(hwnd, IDOK), false);
266 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), false);
267 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), false);
268
269 clones.clear();
270
271 cl = GetDlgItem(hwnd, IDC_CLONE_LIST);
272 auto num_clones = SendMessageW(cl, LB_GETCOUNT, 0, 0);
273
274 if (num_clones != LB_ERR) {
275 for (unsigned int i = 0; i < (unsigned int)num_clones; i++) {
276 WCHAR* t;
277
278 auto len = SendMessageW(cl, LB_GETTEXTLEN, i, 0);
279 t = (WCHAR*)malloc((len + 1) * sizeof(WCHAR));
280
281 SendMessageW(cl, LB_GETTEXT, i, (LPARAM)t);
282
283 clones.push_back(t);
284
285 free(t);
286 }
287 }
288
289 thread = CreateThread(nullptr, 0, send_thread, this, 0, nullptr);
290
291 if (!thread)
292 throw last_error(GetLastError());
293 }
294
Browse(HWND hwnd)295 void BtrfsSend::Browse(HWND hwnd) {
296 OPENFILENAMEW ofn;
297
298 file[0] = 0;
299
300 memset(&ofn, 0, sizeof(OPENFILENAMEW));
301 ofn.lStructSize = sizeof(OPENFILENAMEW);
302 ofn.hwndOwner = hwnd;
303 ofn.hInstance = module;
304 ofn.lpstrFile = file;
305 ofn.nMaxFile = sizeof(file) / sizeof(WCHAR);
306 ofn.Flags = OFN_EXPLORER;
307
308 if (!GetSaveFileNameW(&ofn))
309 return;
310
311 SetDlgItemTextW(hwnd, IDC_STREAM_DEST, file);
312 }
313
BrowseParent(HWND hwnd)314 void BtrfsSend::BrowseParent(HWND hwnd) {
315 BROWSEINFOW bi;
316 PIDLIST_ABSOLUTE root, pidl;
317 HRESULT hr;
318 WCHAR parent[MAX_PATH], volpathw[MAX_PATH];
319 NTSTATUS Status;
320 IO_STATUS_BLOCK iosb;
321 btrfs_get_file_ids bgfi;
322
323 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1))
324 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
325
326 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0);
327 if (FAILED(hr))
328 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED);
329
330 memset(&bi, 0, sizeof(BROWSEINFOW));
331
332 bi.hwndOwner = hwnd;
333 bi.pidlRoot = root;
334 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON;
335
336 pidl = SHBrowseForFolderW(&bi);
337
338 if (!pidl)
339 return;
340
341 if (!SHGetPathFromIDListW(pidl, parent))
342 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED);
343
344 {
345 win_handle h = CreateFileW(parent, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
346 if (h == INVALID_HANDLE_VALUE)
347 throw string_error(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str());
348
349 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids));
350 if (!NT_SUCCESS(Status))
351 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str());
352 }
353
354 if (bgfi.inode != 0x100 || bgfi.top)
355 throw string_error(IDS_NOT_SUBVOL);
356
357 SetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent);
358 }
359
AddClone(HWND hwnd)360 void BtrfsSend::AddClone(HWND hwnd) {
361 BROWSEINFOW bi;
362 PIDLIST_ABSOLUTE root, pidl;
363 HRESULT hr;
364 WCHAR path[MAX_PATH], volpathw[MAX_PATH];
365 NTSTATUS Status;
366 IO_STATUS_BLOCK iosb;
367 btrfs_get_file_ids bgfi;
368
369 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1))
370 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
371
372 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0);
373 if (FAILED(hr))
374 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED);
375
376 memset(&bi, 0, sizeof(BROWSEINFOW));
377
378 bi.hwndOwner = hwnd;
379 bi.pidlRoot = root;
380 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON;
381
382 pidl = SHBrowseForFolderW(&bi);
383
384 if (!pidl)
385 return;
386
387 if (!SHGetPathFromIDListW(pidl, path))
388 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED);
389
390 {
391 win_handle h = CreateFileW(path, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
392 if (h == INVALID_HANDLE_VALUE)
393 throw string_error(IDS_SEND_CANT_OPEN_DIR, path, GetLastError(), format_message(GetLastError()).c_str());
394
395 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids));
396 if (!NT_SUCCESS(Status))
397 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str());
398 }
399
400 if (bgfi.inode != 0x100 || bgfi.top)
401 throw string_error(IDS_NOT_SUBVOL);
402
403 SendMessageW(GetDlgItem(hwnd, IDC_CLONE_LIST), LB_ADDSTRING, 0, (LPARAM)path);
404 }
405
RemoveClone(HWND hwnd)406 void BtrfsSend::RemoveClone(HWND hwnd) {
407 LRESULT sel;
408 HWND cl = GetDlgItem(hwnd, IDC_CLONE_LIST);
409
410 sel = SendMessageW(cl, LB_GETCURSEL, 0, 0);
411
412 if (sel == LB_ERR)
413 return;
414
415 SendMessageW(cl, LB_DELETESTRING, sel, 0);
416
417 if (SendMessageW(cl, LB_GETCURSEL, 0, 0) == LB_ERR)
418 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), false);
419 }
420
SendDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)421 INT_PTR BtrfsSend::SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
422 try {
423 switch (uMsg) {
424 case WM_INITDIALOG:
425 this->hwnd = hwndDlg;
426
427 GetDlgItemTextW(hwndDlg, IDCANCEL, closetext, sizeof(closetext) / sizeof(WCHAR));
428 break;
429
430 case WM_COMMAND:
431 switch (HIWORD(wParam)) {
432 case BN_CLICKED:
433 switch (LOWORD(wParam)) {
434 case IDOK:
435 StartSend(hwndDlg);
436 return true;
437
438 case IDCANCEL:
439 if (started) {
440 TerminateThread(thread, 0);
441
442 if (stream != INVALID_HANDLE_VALUE) {
443 NTSTATUS Status;
444 FILE_DISPOSITION_INFO fdi;
445 IO_STATUS_BLOCK iosb;
446
447 fdi.DeleteFile = true;
448
449 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation);
450
451 CloseHandle(stream);
452
453 if (!NT_SUCCESS(Status))
454 throw ntstatus_error(Status);
455 }
456
457 if (dirh != INVALID_HANDLE_VALUE)
458 CloseHandle(dirh);
459
460 started = false;
461
462 SetDlgItemTextW(hwndDlg, IDCANCEL, closetext);
463
464 EnableWindow(GetDlgItem(hwnd, IDOK), true);
465 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true);
466 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true);
467 } else
468 EndDialog(hwndDlg, 1);
469 return true;
470
471 case IDC_BROWSE:
472 Browse(hwndDlg);
473 return true;
474
475 case IDC_INCREMENTAL:
476 incremental = IsDlgButtonChecked(hwndDlg, LOWORD(wParam));
477
478 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_SUBVOL), incremental);
479 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_BROWSE), incremental);
480 return true;
481
482 case IDC_PARENT_BROWSE:
483 BrowseParent(hwndDlg);
484 return true;
485
486 case IDC_CLONE_ADD:
487 AddClone(hwndDlg);
488 return true;
489
490 case IDC_CLONE_REMOVE:
491 RemoveClone(hwndDlg);
492 return true;
493 }
494 break;
495
496 case LBN_SELCHANGE:
497 switch (LOWORD(wParam)) {
498 case IDC_CLONE_LIST:
499 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), true);
500 return true;
501 }
502 break;
503 }
504 break;
505 }
506 } catch (const exception& e) {
507 error_message(hwnd, e.what());
508 }
509
510 return false;
511 }
512
stub_SendDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)513 static INT_PTR CALLBACK stub_SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
514 BtrfsSend* bs;
515
516 if (uMsg == WM_INITDIALOG) {
517 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
518 bs = (BtrfsSend*)lParam;
519 } else
520 bs = (BtrfsSend*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
521
522 if (bs)
523 return bs->SendDlgProc(hwndDlg, uMsg, wParam, lParam);
524 else
525 return false;
526 }
527
Open(HWND hwnd,LPWSTR path)528 void BtrfsSend::Open(HWND hwnd, LPWSTR path) {
529 subvol = path;
530
531 if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SEND_SUBVOL), hwnd, stub_SendDlgProc, (LPARAM)this) <= 0)
532 throw last_error(GetLastError());
533 }
534
SendSubvolGUIW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)535 extern "C" void CALLBACK SendSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
536 try {
537 win_handle token;
538 TOKEN_PRIVILEGES tp;
539 LUID luid;
540
541 set_dpi_aware();
542
543 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
544 throw last_error(GetLastError());
545
546 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
547 throw last_error(GetLastError());
548
549 tp.PrivilegeCount = 1;
550 tp.Privileges[0].Luid = luid;
551 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
552
553 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
554 throw last_error(GetLastError());
555
556 BtrfsSend bs;
557
558 bs.Open(hwnd, lpszCmdLine);
559 } catch (const exception& e) {
560 error_message(hwnd, e.what());
561 }
562 }
563
send_subvol(const wstring & subvol,const wstring & file,const wstring & parent,const vector<wstring> & clones)564 static void send_subvol(const wstring& subvol, const wstring& file, const wstring& parent, const vector<wstring>& clones) {
565 char* buf;
566 win_handle dirh, stream;
567 ULONG i;
568 btrfs_send_subvol* bss;
569 IO_STATUS_BLOCK iosb;
570 NTSTATUS Status;
571 btrfs_send_header header;
572 btrfs_send_command end;
573
574 buf = (char*)malloc(SEND_BUFFER_LEN);
575
576 try {
577 dirh = CreateFileW(subvol.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
578 if (dirh == INVALID_HANDLE_VALUE)
579 throw last_error(GetLastError());
580
581 stream = CreateFileW(file.c_str(), FILE_WRITE_DATA | DELETE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
582 if (stream == INVALID_HANDLE_VALUE)
583 throw last_error(GetLastError());
584
585 try {
586 size_t bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE));
587 bss = (btrfs_send_subvol*)malloc(bss_size);
588 memset(bss, 0, bss_size);
589
590 if (parent != L"") {
591 HANDLE parenth;
592
593 parenth = CreateFileW(parent.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
594 if (parenth == INVALID_HANDLE_VALUE)
595 throw last_error(GetLastError());
596
597 bss->parent = parenth;
598 } else
599 bss->parent = nullptr;
600
601 bss->num_clones = (ULONG)clones.size();
602
603 for (i = 0; i < bss->num_clones; i++) {
604 HANDLE h;
605
606 h = CreateFileW(clones[i].c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
607 if (h == INVALID_HANDLE_VALUE) {
608 auto le = GetLastError();
609 ULONG j;
610
611 for (j = 0; j < i; j++) {
612 CloseHandle(bss->clones[j]);
613 }
614
615 if (bss->parent) CloseHandle(bss->parent);
616
617 throw last_error(le);
618 }
619
620 bss->clones[i] = h;
621 }
622
623 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, (ULONG)bss_size, nullptr, 0);
624
625 for (i = 0; i < bss->num_clones; i++) {
626 CloseHandle(bss->clones[i]);
627 }
628
629 if (bss->parent) CloseHandle(bss->parent);
630
631 if (!NT_SUCCESS(Status))
632 throw ntstatus_error(Status);
633
634 memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic));
635 header.version = 1;
636
637 if (!WriteFile(stream, &header, sizeof(header), nullptr, nullptr))
638 throw last_error(GetLastError());
639
640 do {
641 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, nullptr, 0, buf, SEND_BUFFER_LEN);
642
643 if (NT_SUCCESS(Status))
644 WriteFile(stream, buf, (DWORD)iosb.Information, nullptr, nullptr);
645 } while (NT_SUCCESS(Status));
646
647 if (Status != STATUS_END_OF_FILE)
648 throw ntstatus_error(Status);
649
650 end.length = 0;
651 end.cmd = BTRFS_SEND_CMD_END;
652 end.csum = 0x9dc96c50;
653
654 if (!WriteFile(stream, &end, sizeof(end), nullptr, nullptr))
655 throw last_error(GetLastError());
656
657 SetEndOfFile(stream);
658 } catch (...) {
659 FILE_DISPOSITION_INFO fdi;
660
661 fdi.DeleteFile = true;
662
663 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation);
664 if (!NT_SUCCESS(Status))
665 throw ntstatus_error(Status);
666
667 throw;
668 }
669 } catch (...) {
670 free(buf);
671 throw;
672 }
673
674 free(buf);
675 }
676
SendSubvolW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)677 extern "C" void CALLBACK SendSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
678 vector<wstring> args;
679 wstring subvol = L"", parent = L"", file = L"";
680 vector<wstring> clones;
681
682 command_line_to_args(lpszCmdLine, args);
683
684 if (args.size() >= 2) {
685 TOKEN_PRIVILEGES tp;
686 LUID luid;
687
688 {
689 win_handle token;
690
691 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
692 return;
693
694 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
695 return;
696
697 tp.PrivilegeCount = 1;
698 tp.Privileges[0].Luid = luid;
699 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
700
701 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
702 return;
703 }
704
705 for (unsigned int i = 0; i < args.size(); i++) {
706 if (args[i][0] == '-') {
707 if (args[i][2] == 0 && i < args.size() - 1) {
708 if (args[i][1] == 'p') {
709 parent = args[i+1];
710 i++;
711 } else if (args[i][1] == 'c') {
712 clones.push_back(args[i+1]);
713 i++;
714 }
715 }
716 } else {
717 if (subvol == L"")
718 subvol = args[i];
719 else if (file == L"")
720 file = args[i];
721 }
722 }
723
724 if (subvol != L"" && file != L"") {
725 try {
726 send_subvol(subvol, file, parent, clones);
727 } catch (const exception& e) {
728 cerr << "Error: " << e.what() << endl;
729 }
730 }
731 }
732 }
733