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