1 /* Copyright (c) Mark Harmstone 2017
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
27 #define SEND_BUFFER_LEN 1048576
29 void BtrfsSend::ShowSendError(UINT msg
, ...) {
30 WCHAR s
[1024], t
[1024];
33 if (!LoadStringW(module
, msg
, s
, sizeof(s
) / sizeof(WCHAR
))) {
34 ShowError(hwnd
, GetLastError());
40 vswprintf(t
, sizeof(t
) / sizeof(WCHAR
), s
, ap
);
42 vsnwprintf(t
, sizeof(t
) / sizeof(WCHAR
), s
, ap
);
45 SetDlgItemTextW(hwnd
, IDC_SEND_STATUS
, t
);
50 DWORD
BtrfsSend::Thread() {
53 btrfs_send_subvol
* bss
;
54 btrfs_send_header header
;
55 btrfs_send_command end
;
59 buf
= (char*)malloc(SEND_BUFFER_LEN
);
61 dirh
= CreateFileW(subvol
.c_str(), FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
62 if (dirh
== INVALID_HANDLE_VALUE
) {
63 ShowSendError(IDS_SEND_CANT_OPEN_DIR
, subvol
.c_str(), GetLastError(), format_message(GetLastError()).c_str());
67 bss_size
= offsetof(btrfs_send_subvol
, clones
[0]) + (clones
.size() * sizeof(HANDLE
));
68 bss
= (btrfs_send_subvol
*)malloc(bss_size
);
69 memset(bss
, 0, bss_size
);
72 WCHAR parent
[MAX_PATH
];
77 GetDlgItemTextW(hwnd
, IDC_PARENT_SUBVOL
, parent
, sizeof(parent
) / sizeof(WCHAR
));
79 parenth
= CreateFileW(parent
, FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
80 if (parenth
== INVALID_HANDLE_VALUE
) {
81 ShowSendError(IDS_SEND_CANT_OPEN_DIR
, parent
, GetLastError(), format_message(GetLastError()).c_str());
85 bss
->parent
= parenth
;
89 bss
->num_clones
= clones
.size();
91 for (i
= 0; i
< bss
->num_clones
; i
++) {
94 h
= CreateFileW(clones
[i
].c_str(), FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
95 if (h
== INVALID_HANDLE_VALUE
) {
98 ShowSendError(IDS_SEND_CANT_OPEN_DIR
, clones
[i
].c_str(), GetLastError(), format_message(GetLastError()).c_str());
100 for (j
= 0; j
< i
; j
++) {
101 CloseHandle(bss
->clones
[j
]);
104 if (bss
->parent
) CloseHandle(bss
->parent
);
111 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_SEND_SUBVOL
, bss
, bss_size
, NULL
, 0);
113 for (i
= 0; i
< bss
->num_clones
; i
++) {
114 CloseHandle(bss
->clones
[i
]);
117 if (!NT_SUCCESS(Status
)) {
118 if (Status
== (NTSTATUS
)STATUS_INVALID_PARAMETER
) {
119 BY_HANDLE_FILE_INFORMATION fileinfo
;
120 if (!GetFileInformationByHandle(dirh
, &fileinfo
)) {
121 ShowSendError(IDS_SEND_GET_FILE_INFO_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
122 if (bss
->parent
) CloseHandle(bss
->parent
);
126 if (!(fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)) {
127 ShowSendError(IDS_SEND_NOT_READONLY
);
128 if (bss
->parent
) CloseHandle(bss
->parent
);
133 if (!GetFileInformationByHandle(bss
->parent
, &fileinfo
)) {
134 ShowSendError(IDS_SEND_GET_FILE_INFO_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
135 CloseHandle(bss
->parent
);
139 if (!(fileinfo
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)) {
140 ShowSendError(IDS_SEND_PARENT_NOT_READONLY
);
141 CloseHandle(bss
->parent
);
147 ShowSendError(IDS_SEND_FSCTL_BTRFS_SEND_SUBVOL_FAILED
, Status
, format_ntstatus(Status
).c_str());
148 if (bss
->parent
) CloseHandle(bss
->parent
);
152 if (bss
->parent
) CloseHandle(bss
->parent
);
154 stream
= CreateFileW(file
, FILE_WRITE_DATA
| DELETE
, 0, NULL
, OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
155 if (stream
== INVALID_HANDLE_VALUE
) {
156 ShowSendError(IDS_SEND_CANT_OPEN_FILE
, file
, GetLastError(), format_message(GetLastError()).c_str());
160 memcpy(header
.magic
, BTRFS_SEND_MAGIC
, sizeof(BTRFS_SEND_MAGIC
));
163 if (!WriteFile(stream
, &header
, sizeof(header
), NULL
, NULL
)) {
164 ShowSendError(IDS_SEND_WRITEFILE_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
169 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_READ_SEND_BUFFER
, NULL
, 0, buf
, SEND_BUFFER_LEN
);
171 if (NT_SUCCESS(Status
)) {
172 if (!WriteFile(stream
, buf
, iosb
.Information
, NULL
, NULL
))
173 ShowSendError(IDS_SEND_WRITEFILE_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
175 } while (NT_SUCCESS(Status
));
177 if (Status
!= STATUS_END_OF_FILE
) {
178 ShowSendError(IDS_SEND_FSCTL_BTRFS_READ_SEND_BUFFER_FAILED
, Status
, format_ntstatus(Status
).c_str());
183 end
.cmd
= BTRFS_SEND_CMD_END
;
184 end
.csum
= 0x9dc96c50;
186 if (!WriteFile(stream
, &end
, sizeof(end
), NULL
, NULL
)) {
187 ShowSendError(IDS_SEND_WRITEFILE_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
191 SetEndOfFile(stream
);
193 ShowSendError(IDS_SEND_SUCCESS
);
198 FILE_DISPOSITION_INFO fdi
;
200 fdi
.DeleteFile
= TRUE
;
202 SetFileInformationByHandle(stream
, FileDispositionInfo
, &fdi
, sizeof(FILE_DISPOSITION_INFO
));
206 stream
= INVALID_HANDLE_VALUE
;
210 dirh
= INVALID_HANDLE_VALUE
;
218 SetDlgItemTextW(hwnd
, IDCANCEL
, closetext
);
219 EnableWindow(GetDlgItem(hwnd
, IDOK
), TRUE
);
220 EnableWindow(GetDlgItem(hwnd
, IDC_STREAM_DEST
), TRUE
);
221 EnableWindow(GetDlgItem(hwnd
, IDC_BROWSE
), TRUE
);
226 static DWORD WINAPI
send_thread(LPVOID lpParameter
) {
227 BtrfsSend
* bs
= (BtrfsSend
*)lpParameter
;
232 void BtrfsSend::StartSend(HWND hwnd
) {
240 GetDlgItemTextW(hwnd
, IDC_STREAM_DEST
, file
, sizeof(file
) / sizeof(WCHAR
));
246 WCHAR parent
[MAX_PATH
];
248 GetDlgItemTextW(hwnd
, IDC_PARENT_SUBVOL
, parent
, sizeof(parent
) / sizeof(WCHAR
));
255 ShowSendError(IDS_SEND_WRITING
);
257 LoadStringW(module
, IDS_SEND_CANCEL
, s
, sizeof(s
) / sizeof(WCHAR
));
258 SetDlgItemTextW(hwnd
, IDCANCEL
, s
);
260 EnableWindow(GetDlgItem(hwnd
, IDOK
), FALSE
);
261 EnableWindow(GetDlgItem(hwnd
, IDC_STREAM_DEST
), FALSE
);
262 EnableWindow(GetDlgItem(hwnd
, IDC_BROWSE
), FALSE
);
266 cl
= GetDlgItem(hwnd
, IDC_CLONE_LIST
);
267 num_clones
= SendMessageW(cl
, LB_GETCOUNT
, 0, 0);
269 if ((LRESULT
)num_clones
!= LB_ERR
) {
272 for (i
= 0; i
< num_clones
; i
++) {
276 len
= SendMessageW(cl
, LB_GETTEXTLEN
, i
, 0);
277 s
= (WCHAR
*)malloc((len
+ 1) * sizeof(WCHAR
));
279 SendMessageW(cl
, LB_GETTEXT
, i
, (LPARAM
)s
);
287 thread
= CreateThread(NULL
, 0, send_thread
, this, 0, NULL
);
290 ShowError(NULL
, GetLastError());
293 void BtrfsSend::Browse(HWND hwnd
) {
298 memset(&ofn
, 0, sizeof(OPENFILENAMEW
));
299 ofn
.lStructSize
= sizeof(OPENFILENAMEW
);
300 ofn
.hwndOwner
= hwnd
;
301 ofn
.hInstance
= module
;
302 ofn
.lpstrFile
= file
;
303 ofn
.nMaxFile
= sizeof(file
) / sizeof(WCHAR
);
305 if (!GetSaveFileNameW(&ofn
))
308 SetDlgItemTextW(hwnd
, IDC_STREAM_DEST
, file
);
311 void BtrfsSend::BrowseParent(HWND hwnd
) {
313 PIDLIST_ABSOLUTE root
, pidl
;
315 WCHAR parent
[MAX_PATH
], volpathw
[MAX_PATH
];
318 IO_STATUS_BLOCK iosb
;
319 btrfs_get_file_ids bgfi
;
321 if (!GetVolumePathNameW(subvol
.c_str(), volpathw
, (sizeof(volpathw
) / sizeof(WCHAR
)) - 1)) {
322 ShowStringError(hwnd
, IDS_RECV_GETVOLUMEPATHNAME_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
326 hr
= SHParseDisplayName(volpathw
, 0, &root
, 0, 0);
328 ShowStringError(hwnd
, IDS_SHPARSEDISPLAYNAME_FAILED
);
332 memset(&bi
, 0, sizeof(BROWSEINFOW
));
336 bi
.ulFlags
= BIF_RETURNONLYFSDIRS
| BIF_USENEWUI
| BIF_NONEWFOLDERBUTTON
;
338 pidl
= SHBrowseForFolderW(&bi
);
343 if (!SHGetPathFromIDListW(pidl
, parent
)) {
344 ShowStringError(hwnd
, IDS_SHGETPATHFROMIDLIST_FAILED
);
348 h
= CreateFileW(parent
, FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
349 if (h
== INVALID_HANDLE_VALUE
) {
350 ShowStringError(hwnd
, IDS_SEND_CANT_OPEN_DIR
, parent
, GetLastError(), format_message(GetLastError()).c_str());
354 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_FILE_IDS
, NULL
, 0, &bgfi
, sizeof(btrfs_get_file_ids
));
355 if (!NT_SUCCESS(Status
)) {
356 ShowStringError(hwnd
, IDS_GET_FILE_IDS_FAILED
, Status
, format_ntstatus(Status
).c_str());
363 if (bgfi
.inode
!= 0x100 || bgfi
.top
) {
364 ShowStringError(hwnd
, IDS_NOT_SUBVOL
);
368 SetDlgItemTextW(hwnd
, IDC_PARENT_SUBVOL
, parent
);
371 void BtrfsSend::AddClone(HWND hwnd
) {
373 PIDLIST_ABSOLUTE root
, pidl
;
375 WCHAR path
[MAX_PATH
], volpathw
[MAX_PATH
];
378 IO_STATUS_BLOCK iosb
;
379 btrfs_get_file_ids bgfi
;
381 if (!GetVolumePathNameW(subvol
.c_str(), volpathw
, (sizeof(volpathw
) / sizeof(WCHAR
)) - 1)) {
382 ShowStringError(hwnd
, IDS_RECV_GETVOLUMEPATHNAME_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
386 hr
= SHParseDisplayName(volpathw
, 0, &root
, 0, 0);
388 ShowStringError(hwnd
, IDS_SHPARSEDISPLAYNAME_FAILED
);
392 memset(&bi
, 0, sizeof(BROWSEINFOW
));
396 bi
.ulFlags
= BIF_RETURNONLYFSDIRS
| BIF_USENEWUI
| BIF_NONEWFOLDERBUTTON
;
398 pidl
= SHBrowseForFolderW(&bi
);
403 if (!SHGetPathFromIDListW(pidl
, path
)) {
404 ShowStringError(hwnd
, IDS_SHGETPATHFROMIDLIST_FAILED
);
408 h
= CreateFileW(path
, FILE_TRAVERSE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
409 if (h
== INVALID_HANDLE_VALUE
) {
410 ShowStringError(hwnd
, IDS_SEND_CANT_OPEN_DIR
, path
, GetLastError(), format_message(GetLastError()).c_str());
414 Status
= NtFsControlFile(h
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_GET_FILE_IDS
, NULL
, 0, &bgfi
, sizeof(btrfs_get_file_ids
));
415 if (!NT_SUCCESS(Status
)) {
416 ShowStringError(hwnd
, IDS_GET_FILE_IDS_FAILED
, Status
, format_ntstatus(Status
).c_str());
423 if (bgfi
.inode
!= 0x100 || bgfi
.top
) {
424 ShowStringError(hwnd
, IDS_NOT_SUBVOL
);
428 SendMessageW(GetDlgItem(hwnd
, IDC_CLONE_LIST
), LB_ADDSTRING
, 0, (LPARAM
)path
);
431 void BtrfsSend::RemoveClone(HWND hwnd
) {
433 HWND cl
= GetDlgItem(hwnd
, IDC_CLONE_LIST
);
435 sel
= SendMessageW(cl
, LB_GETCURSEL
, 0, 0);
440 SendMessageW(cl
, LB_DELETESTRING
, sel
, 0);
442 if (SendMessageW(cl
, LB_GETCURSEL
, 0, 0) == LB_ERR
)
443 EnableWindow(GetDlgItem(hwnd
, IDC_CLONE_REMOVE
), FALSE
);
446 INT_PTR
BtrfsSend::SendDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
449 this->hwnd
= hwndDlg
;
451 GetDlgItemTextW(hwndDlg
, IDCANCEL
, closetext
, sizeof(closetext
) / sizeof(WCHAR
));
455 switch (HIWORD(wParam
)) {
457 switch (LOWORD(wParam
)) {
464 TerminateThread(thread
, 0);
466 if (stream
!= INVALID_HANDLE_VALUE
) {
467 FILE_DISPOSITION_INFO fdi
;
469 fdi
.DeleteFile
= TRUE
;
471 SetFileInformationByHandle(stream
, FileDispositionInfo
, &fdi
, sizeof(FILE_DISPOSITION_INFO
));
475 if (dirh
!= INVALID_HANDLE_VALUE
)
480 SetDlgItemTextW(hwndDlg
, IDCANCEL
, closetext
);
482 EnableWindow(GetDlgItem(hwnd
, IDOK
), TRUE
);
483 EnableWindow(GetDlgItem(hwnd
, IDC_STREAM_DEST
), TRUE
);
484 EnableWindow(GetDlgItem(hwnd
, IDC_BROWSE
), TRUE
);
486 EndDialog(hwndDlg
, 1);
493 case IDC_INCREMENTAL
:
494 incremental
= IsDlgButtonChecked(hwndDlg
, LOWORD(wParam
));
496 EnableWindow(GetDlgItem(hwnd
, IDC_PARENT_SUBVOL
), incremental
);
497 EnableWindow(GetDlgItem(hwnd
, IDC_PARENT_BROWSE
), incremental
);
500 case IDC_PARENT_BROWSE
:
501 BrowseParent(hwndDlg
);
508 case IDC_CLONE_REMOVE
:
509 RemoveClone(hwndDlg
);
515 switch (LOWORD(wParam
)) {
517 EnableWindow(GetDlgItem(hwnd
, IDC_CLONE_REMOVE
), TRUE
);
528 static INT_PTR CALLBACK
stub_SendDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
531 if (uMsg
== WM_INITDIALOG
) {
532 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
533 bs
= (BtrfsSend
*)lParam
;
535 bs
= (BtrfsSend
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
538 return bs
->SendDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
543 void BtrfsSend::Open(HWND hwnd
, LPWSTR path
) {
546 if (DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_SEND_SUBVOL
), hwnd
, stub_SendDlgProc
, (LPARAM
)this) <= 0)
547 ShowError(hwnd
, GetLastError());
550 void CALLBACK
SendSubvolGUIW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
558 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
)) {
559 ShowError(hwnd
, GetLastError());
563 if (!LookupPrivilegeValueW(NULL
, L
"SeManageVolumePrivilege", &luid
)) {
564 ShowError(hwnd
, GetLastError());
568 tp
.PrivilegeCount
= 1;
569 tp
.Privileges
[0].Luid
= luid
;
570 tp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
572 if (!AdjustTokenPrivileges(token
, FALSE
, &tp
, sizeof(TOKEN_PRIVILEGES
), NULL
, NULL
)) {
573 ShowError(hwnd
, GetLastError());
579 bs
->Open(hwnd
, lpszCmdLine
);
586 static void send_subvol(std::wstring subvol
, std::wstring file
, std::wstring parent
, std::vector
<std::wstring
> clones
) {
590 btrfs_send_subvol
* bss
;
591 IO_STATUS_BLOCK iosb
;
593 btrfs_send_header header
;
594 btrfs_send_command end
;
595 BOOL success
= FALSE
;
597 buf
= (char*)malloc(SEND_BUFFER_LEN
);
599 dirh
= CreateFileW(subvol
.c_str(), FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
600 if (dirh
== INVALID_HANDLE_VALUE
)
603 stream
= CreateFileW(file
.c_str(), FILE_WRITE_DATA
| DELETE
, 0, NULL
, OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
604 if (stream
== INVALID_HANDLE_VALUE
) {
609 bss_size
= offsetof(btrfs_send_subvol
, clones
[0]) + (clones
.size() * sizeof(HANDLE
));
610 bss
= (btrfs_send_subvol
*)malloc(bss_size
);
611 memset(bss
, 0, bss_size
);
616 parenth
= CreateFileW(parent
.c_str(), FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
617 if (parenth
== INVALID_HANDLE_VALUE
)
620 bss
->parent
= parenth
;
624 bss
->num_clones
= clones
.size();
626 for (i
= 0; i
< bss
->num_clones
; i
++) {
629 h
= CreateFileW(clones
[i
].c_str(), FILE_READ_ATTRIBUTES
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
630 if (h
== INVALID_HANDLE_VALUE
) {
633 for (j
= 0; j
< i
; j
++) {
634 CloseHandle(bss
->clones
[j
]);
637 if (bss
->parent
) CloseHandle(bss
->parent
);
644 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_SEND_SUBVOL
, bss
, bss_size
, NULL
, 0);
646 for (i
= 0; i
< bss
->num_clones
; i
++) {
647 CloseHandle(bss
->clones
[i
]);
650 if (bss
->parent
) CloseHandle(bss
->parent
);
652 if (!NT_SUCCESS(Status
))
655 memcpy(header
.magic
, BTRFS_SEND_MAGIC
, sizeof(BTRFS_SEND_MAGIC
));
658 if (!WriteFile(stream
, &header
, sizeof(header
), NULL
, NULL
))
662 Status
= NtFsControlFile(dirh
, NULL
, NULL
, NULL
, &iosb
, FSCTL_BTRFS_READ_SEND_BUFFER
, NULL
, 0, buf
, SEND_BUFFER_LEN
);
664 if (NT_SUCCESS(Status
))
665 WriteFile(stream
, buf
, iosb
.Information
, NULL
, NULL
);
666 } while (NT_SUCCESS(Status
));
668 if (Status
!= STATUS_END_OF_FILE
)
672 end
.cmd
= BTRFS_SEND_CMD_END
;
673 end
.csum
= 0x9dc96c50;
675 if (!WriteFile(stream
, &end
, sizeof(end
), NULL
, NULL
))
678 SetEndOfFile(stream
);
684 FILE_DISPOSITION_INFO fdi
;
686 fdi
.DeleteFile
= TRUE
;
688 SetFileInformationByHandle(stream
, FileDispositionInfo
, &fdi
, sizeof(FILE_DISPOSITION_INFO
));
698 void CALLBACK
SendSubvolW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
701 std::wstring subvol
= L
"", parent
= L
"", file
= L
"";
702 std::vector
<std::wstring
> clones
;
704 args
= CommandLineToArgvW(lpszCmdLine
, &num_args
);
715 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
))
718 if (!LookupPrivilegeValueW(NULL
, L
"SeManageVolumePrivilege", &luid
))
721 tp
.PrivilegeCount
= 1;
722 tp
.Privileges
[0].Luid
= luid
;
723 tp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
725 if (!AdjustTokenPrivileges(token
, FALSE
, &tp
, sizeof(TOKEN_PRIVILEGES
), NULL
, NULL
))
730 for (i
= 0; i
< num_args
; i
++) {
731 if (args
[i
][0] == '-') {
732 if (args
[i
][2] == 0 && i
< num_args
- 1) {
733 if (args
[i
][1] == 'p') {
736 } else if (args
[i
][1] == 'c') {
737 clones
.push_back(args
[i
+1]);
744 else if (file
== L
"")
749 if (subvol
!= L
"" && file
!= L
"")
750 send_subvol(subvol
, file
, parent
, clones
);