[SHELLBTRFS] Upgrade to 1.5
[reactos.git] / dll / shellext / shellbtrfs / send.cpp
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
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
227 static DWORD WINAPI send_thread(LPVOID lpParameter) {
228 BtrfsSend* bs = (BtrfsSend*)lpParameter;
229
230 return bs->Thread();
231 }
232
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
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
307 if (!GetSaveFileNameW(&ofn))
308 return;
309
310 SetDlgItemTextW(hwnd, IDC_STREAM_DEST, file);
311 }
312
313 void BtrfsSend::BrowseParent(HWND hwnd) {
314 BROWSEINFOW bi;
315 PIDLIST_ABSOLUTE root, pidl;
316 HRESULT hr;
317 WCHAR parent[MAX_PATH], volpathw[MAX_PATH];
318 NTSTATUS Status;
319 IO_STATUS_BLOCK iosb;
320 btrfs_get_file_ids bgfi;
321
322 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1))
323 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
324
325 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0);
326 if (FAILED(hr))
327 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED);
328
329 memset(&bi, 0, sizeof(BROWSEINFOW));
330
331 bi.hwndOwner = hwnd;
332 bi.pidlRoot = root;
333 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON;
334
335 pidl = SHBrowseForFolderW(&bi);
336
337 if (!pidl)
338 return;
339
340 if (!SHGetPathFromIDListW(pidl, parent))
341 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED);
342
343 {
344 win_handle h = CreateFileW(parent, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
345 if (h == INVALID_HANDLE_VALUE)
346 throw string_error(IDS_SEND_CANT_OPEN_DIR, parent, GetLastError(), format_message(GetLastError()).c_str());
347
348 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids));
349 if (!NT_SUCCESS(Status))
350 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str());
351 }
352
353 if (bgfi.inode != 0x100 || bgfi.top)
354 throw string_error(IDS_NOT_SUBVOL);
355
356 SetDlgItemTextW(hwnd, IDC_PARENT_SUBVOL, parent);
357 }
358
359 void BtrfsSend::AddClone(HWND hwnd) {
360 BROWSEINFOW bi;
361 PIDLIST_ABSOLUTE root, pidl;
362 HRESULT hr;
363 WCHAR path[MAX_PATH], volpathw[MAX_PATH];
364 NTSTATUS Status;
365 IO_STATUS_BLOCK iosb;
366 btrfs_get_file_ids bgfi;
367
368 if (!GetVolumePathNameW(subvol.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1))
369 throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
370
371 hr = SHParseDisplayName(volpathw, 0, &root, 0, 0);
372 if (FAILED(hr))
373 throw string_error(IDS_SHPARSEDISPLAYNAME_FAILED);
374
375 memset(&bi, 0, sizeof(BROWSEINFOW));
376
377 bi.hwndOwner = hwnd;
378 bi.pidlRoot = root;
379 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_NONEWFOLDERBUTTON;
380
381 pidl = SHBrowseForFolderW(&bi);
382
383 if (!pidl)
384 return;
385
386 if (!SHGetPathFromIDListW(pidl, path))
387 throw string_error(IDS_SHGETPATHFROMIDLIST_FAILED);
388
389 {
390 win_handle h = CreateFileW(path, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
391 if (h == INVALID_HANDLE_VALUE)
392 throw string_error(IDS_SEND_CANT_OPEN_DIR, path, GetLastError(), format_message(GetLastError()).c_str());
393
394 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_FILE_IDS, nullptr, 0, &bgfi, sizeof(btrfs_get_file_ids));
395 if (!NT_SUCCESS(Status))
396 throw string_error(IDS_GET_FILE_IDS_FAILED, Status, format_ntstatus(Status).c_str());
397 }
398
399 if (bgfi.inode != 0x100 || bgfi.top)
400 throw string_error(IDS_NOT_SUBVOL);
401
402 SendMessageW(GetDlgItem(hwnd, IDC_CLONE_LIST), LB_ADDSTRING, 0, (LPARAM)path);
403 }
404
405 void BtrfsSend::RemoveClone(HWND hwnd) {
406 LRESULT sel;
407 HWND cl = GetDlgItem(hwnd, IDC_CLONE_LIST);
408
409 sel = SendMessageW(cl, LB_GETCURSEL, 0, 0);
410
411 if (sel == LB_ERR)
412 return;
413
414 SendMessageW(cl, LB_DELETESTRING, sel, 0);
415
416 if (SendMessageW(cl, LB_GETCURSEL, 0, 0) == LB_ERR)
417 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), false);
418 }
419
420 INT_PTR BtrfsSend::SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
421 try {
422 switch (uMsg) {
423 case WM_INITDIALOG:
424 this->hwnd = hwndDlg;
425
426 GetDlgItemTextW(hwndDlg, IDCANCEL, closetext, sizeof(closetext) / sizeof(WCHAR));
427 break;
428
429 case WM_COMMAND:
430 switch (HIWORD(wParam)) {
431 case BN_CLICKED:
432 switch (LOWORD(wParam)) {
433 case IDOK:
434 StartSend(hwndDlg);
435 return true;
436
437 case IDCANCEL:
438 if (started) {
439 TerminateThread(thread, 0);
440
441 if (stream != INVALID_HANDLE_VALUE) {
442 NTSTATUS Status;
443 FILE_DISPOSITION_INFO fdi;
444 IO_STATUS_BLOCK iosb;
445
446 fdi.DeleteFile = true;
447
448 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation);
449
450 CloseHandle(stream);
451
452 if (!NT_SUCCESS(Status))
453 throw ntstatus_error(Status);
454 }
455
456 if (dirh != INVALID_HANDLE_VALUE)
457 CloseHandle(dirh);
458
459 started = false;
460
461 SetDlgItemTextW(hwndDlg, IDCANCEL, closetext);
462
463 EnableWindow(GetDlgItem(hwnd, IDOK), true);
464 EnableWindow(GetDlgItem(hwnd, IDC_STREAM_DEST), true);
465 EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), true);
466 } else
467 EndDialog(hwndDlg, 1);
468 return true;
469
470 case IDC_BROWSE:
471 Browse(hwndDlg);
472 return true;
473
474 case IDC_INCREMENTAL:
475 incremental = IsDlgButtonChecked(hwndDlg, LOWORD(wParam));
476
477 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_SUBVOL), incremental);
478 EnableWindow(GetDlgItem(hwnd, IDC_PARENT_BROWSE), incremental);
479 return true;
480
481 case IDC_PARENT_BROWSE:
482 BrowseParent(hwndDlg);
483 return true;
484
485 case IDC_CLONE_ADD:
486 AddClone(hwndDlg);
487 return true;
488
489 case IDC_CLONE_REMOVE:
490 RemoveClone(hwndDlg);
491 return true;
492 }
493 break;
494
495 case LBN_SELCHANGE:
496 switch (LOWORD(wParam)) {
497 case IDC_CLONE_LIST:
498 EnableWindow(GetDlgItem(hwnd, IDC_CLONE_REMOVE), true);
499 return true;
500 }
501 break;
502 }
503 break;
504 }
505 } catch (const exception& e) {
506 error_message(hwnd, e.what());
507 }
508
509 return false;
510 }
511
512 static INT_PTR CALLBACK stub_SendDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
513 BtrfsSend* bs;
514
515 if (uMsg == WM_INITDIALOG) {
516 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
517 bs = (BtrfsSend*)lParam;
518 } else
519 bs = (BtrfsSend*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
520
521 if (bs)
522 return bs->SendDlgProc(hwndDlg, uMsg, wParam, lParam);
523 else
524 return false;
525 }
526
527 void BtrfsSend::Open(HWND hwnd, LPWSTR path) {
528 subvol = path;
529
530 if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SEND_SUBVOL), hwnd, stub_SendDlgProc, (LPARAM)this) <= 0)
531 throw last_error(GetLastError());
532 }
533
534 #ifdef __REACTOS__
535 extern "C" {
536 #endif
537
538 void CALLBACK SendSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
539 try {
540 win_handle token;
541 TOKEN_PRIVILEGES tp;
542 LUID luid;
543
544 set_dpi_aware();
545
546 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
547 throw last_error(GetLastError());
548
549 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
550 throw last_error(GetLastError());
551
552 tp.PrivilegeCount = 1;
553 tp.Privileges[0].Luid = luid;
554 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
555
556 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
557 throw last_error(GetLastError());
558
559 BtrfsSend bs;
560
561 bs.Open(hwnd, lpszCmdLine);
562 } catch (const exception& e) {
563 error_message(hwnd, e.what());
564 }
565 }
566
567 #ifdef __REACTOS__
568 } /* extern "C" */
569 #endif
570
571 static void send_subvol(const wstring& subvol, const wstring& file, const wstring& parent, const vector<wstring>& clones) {
572 char* buf;
573 win_handle dirh, stream;
574 ULONG i;
575 btrfs_send_subvol* bss;
576 IO_STATUS_BLOCK iosb;
577 NTSTATUS Status;
578 btrfs_send_header header;
579 btrfs_send_command end;
580
581 buf = (char*)malloc(SEND_BUFFER_LEN);
582
583 try {
584 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);
585 if (dirh == INVALID_HANDLE_VALUE)
586 throw last_error(GetLastError());
587
588 stream = CreateFileW(file.c_str(), FILE_WRITE_DATA | DELETE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
589 if (stream == INVALID_HANDLE_VALUE)
590 throw last_error(GetLastError());
591
592 try {
593 size_t bss_size = offsetof(btrfs_send_subvol, clones[0]) + (clones.size() * sizeof(HANDLE));
594 bss = (btrfs_send_subvol*)malloc(bss_size);
595 memset(bss, 0, bss_size);
596
597 if (parent != L"") {
598 HANDLE parenth;
599
600 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);
601 if (parenth == INVALID_HANDLE_VALUE)
602 throw last_error(GetLastError());
603
604 bss->parent = parenth;
605 } else
606 bss->parent = nullptr;
607
608 bss->num_clones = (ULONG)clones.size();
609
610 for (i = 0; i < bss->num_clones; i++) {
611 HANDLE h;
612
613 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);
614 if (h == INVALID_HANDLE_VALUE) {
615 auto le = GetLastError();
616 ULONG j;
617
618 for (j = 0; j < i; j++) {
619 CloseHandle(bss->clones[j]);
620 }
621
622 if (bss->parent) CloseHandle(bss->parent);
623
624 throw last_error(le);
625 }
626
627 bss->clones[i] = h;
628 }
629
630 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SEND_SUBVOL, bss, (ULONG)bss_size, nullptr, 0);
631
632 for (i = 0; i < bss->num_clones; i++) {
633 CloseHandle(bss->clones[i]);
634 }
635
636 if (bss->parent) CloseHandle(bss->parent);
637
638 if (!NT_SUCCESS(Status))
639 throw ntstatus_error(Status);
640
641 memcpy(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic));
642 header.version = 1;
643
644 if (!WriteFile(stream, &header, sizeof(header), nullptr, nullptr))
645 throw last_error(GetLastError());
646
647 do {
648 Status = NtFsControlFile(dirh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_READ_SEND_BUFFER, nullptr, 0, buf, SEND_BUFFER_LEN);
649
650 if (NT_SUCCESS(Status))
651 WriteFile(stream, buf, (DWORD)iosb.Information, nullptr, nullptr);
652 } while (NT_SUCCESS(Status));
653
654 if (Status != STATUS_END_OF_FILE)
655 throw ntstatus_error(Status);
656
657 end.length = 0;
658 end.cmd = BTRFS_SEND_CMD_END;
659 end.csum = 0x9dc96c50;
660
661 if (!WriteFile(stream, &end, sizeof(end), nullptr, nullptr))
662 throw last_error(GetLastError());
663
664 SetEndOfFile(stream);
665 } catch (...) {
666 FILE_DISPOSITION_INFO fdi;
667
668 fdi.DeleteFile = true;
669
670 Status = NtSetInformationFile(stream, &iosb, &fdi, sizeof(FILE_DISPOSITION_INFO), FileDispositionInformation);
671 if (!NT_SUCCESS(Status))
672 throw ntstatus_error(Status);
673
674 throw;
675 }
676 } catch (...) {
677 free(buf);
678 throw;
679 }
680
681 free(buf);
682 }
683
684 #ifdef __REACTOS__
685 extern "C" {
686 #endif
687
688 void CALLBACK SendSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
689 vector<wstring> args;
690 wstring subvol = L"", parent = L"", file = L"";
691 vector<wstring> clones;
692
693 command_line_to_args(lpszCmdLine, args);
694
695 if (args.size() >= 2) {
696 TOKEN_PRIVILEGES tp;
697 LUID luid;
698
699 {
700 win_handle token;
701
702 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
703 return;
704
705 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid))
706 return;
707
708 tp.PrivilegeCount = 1;
709 tp.Privileges[0].Luid = luid;
710 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
711
712 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
713 return;
714 }
715
716 for (unsigned int i = 0; i < args.size(); i++) {
717 if (args[i][0] == '-') {
718 if (args[i][2] == 0 && i < args.size() - 1) {
719 if (args[i][1] == 'p') {
720 parent = args[i+1];
721 i++;
722 } else if (args[i][1] == 'c') {
723 clones.push_back(args[i+1]);
724 i++;
725 }
726 }
727 } else {
728 if (subvol == L"")
729 subvol = args[i];
730 else if (file == L"")
731 file = args[i];
732 }
733 }
734
735 if (subvol != L"" && file != L"") {
736 try {
737 send_subvol(subvol, file, parent, clones);
738 } catch (const exception& e) {
739 cerr << "Error: " << e.what() << endl;
740 }
741 }
742 }
743 }
744
745 #ifdef __REACTOS__
746 } /* extern "C" */
747 #endif