xref: /reactos/dll/shellext/shellbtrfs/send.cpp (revision 4561998a)
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
743