xref: /reactos/dll/shellext/shellbtrfs/recv.cpp (revision 14beaec1)
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 <windows.h>
20 #include <strsafe.h>
21 #include <stddef.h>
22 #include <sys/stat.h>
23 #include <iostream>
24 #include "recv.h"
25 #include "resource.h"
26 #include "crc32c.h"
27 
28 
29 #ifndef _MSC_VER
30 #ifdef __REACTOS__
31 #define __cpuidex __cpuidex_ // prevent redeclaration
32 #endif
33 #include <cpuid.h>
34 #else
35 #include <intrin.h>
36 #endif
37 
38 
39 const string EA_NTACL = "security.NTACL";
40 const string EA_DOSATTRIB = "user.DOSATTRIB";
41 const string EA_REPARSE = "user.reparse";
42 const string EA_EA = "user.EA";
43 const string XATTR_USER = "user.";
44 
find_tlv(uint8_t * data,ULONG datalen,uint16_t type,void ** value,ULONG * len)45 bool BtrfsRecv::find_tlv(uint8_t* data, ULONG datalen, uint16_t type, void** value, ULONG* len) {
46     size_t off = 0;
47 
48     while (off < datalen) {
49         btrfs_send_tlv* tlv = (btrfs_send_tlv*)(data + off);
50         uint8_t* payload = data + off + sizeof(btrfs_send_tlv);
51 
52         if (off + sizeof(btrfs_send_tlv) + tlv->length > datalen) // file is truncated
53             return false;
54 
55         if (tlv->type == type) {
56             *value = payload;
57             *len = tlv->length;
58             return true;
59         }
60 
61         off += sizeof(btrfs_send_tlv) + tlv->length;
62     }
63 
64     return false;
65 }
66 
cmd_subvol(HWND hwnd,btrfs_send_command * cmd,uint8_t * data,const win_handle & parent)67 void BtrfsRecv::cmd_subvol(HWND hwnd, btrfs_send_command* cmd, uint8_t* data, const win_handle& parent) {
68     string name;
69     BTRFS_UUID* uuid;
70     uint64_t* gen;
71     ULONG uuidlen, genlen;
72     btrfs_create_subvol* bcs;
73     NTSTATUS Status;
74     IO_STATUS_BLOCK iosb;
75 
76     {
77         char* namebuf;
78         ULONG namelen;
79 
80         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&namebuf, &namelen))
81             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
82 
83         name = string(namebuf, namelen);
84     }
85 
86     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen))
87         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
88 
89     if (uuidlen < sizeof(BTRFS_UUID))
90         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
91 
92     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen))
93         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"transid");
94 
95     if (genlen < sizeof(uint64_t))
96         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(uint64_t));
97 
98     this->subvol_uuid = *uuid;
99     this->stransid = *gen;
100 
101     auto nameu = utf8_to_utf16(name);
102 
103     size_t bcslen = offsetof(btrfs_create_subvol, name[0]) + (nameu.length() * sizeof(WCHAR));
104     bcs = (btrfs_create_subvol*)malloc(bcslen);
105 
106     bcs->readonly = true;
107     bcs->posix = true;
108     bcs->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR));
109     memcpy(bcs->name, nameu.c_str(), bcs->namelen);
110 
111     Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0);
112     if (!NT_SUCCESS(Status))
113         throw string_error(IDS_RECV_CREATE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
114 
115     subvolpath = dirpath;
116     subvolpath += L"\\";
117     subvolpath += nameu;
118 
119     if (dir != INVALID_HANDLE_VALUE)
120         CloseHandle(dir);
121 
122     if (master != INVALID_HANDLE_VALUE)
123         CloseHandle(master);
124 
125     master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
126                          nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
127     if (master == INVALID_HANDLE_VALUE)
128         throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
129 
130     Status = NtFsControlFile(master, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0);
131     if (!NT_SUCCESS(Status))
132         throw string_error(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
133 
134     dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE,
135                       FILE_SHARE_READ | FILE_SHARE_WRITE,
136                       nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
137     if (dir == INVALID_HANDLE_VALUE)
138         throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
139 
140     subvolpath += L"\\";
141 
142     add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
143 
144     num_received++;
145 }
146 
add_cache_entry(BTRFS_UUID * uuid,uint64_t transid,const wstring & path)147 void BtrfsRecv::add_cache_entry(BTRFS_UUID* uuid, uint64_t transid, const wstring& path) {
148     subvol_cache sc;
149 
150     sc.uuid = *uuid;
151     sc.transid = transid;
152     sc.path = path;
153 
154     cache.push_back(sc);
155 }
156 
cmd_snapshot(HWND hwnd,btrfs_send_command * cmd,uint8_t * data,const win_handle & parent)157 void BtrfsRecv::cmd_snapshot(HWND hwnd, btrfs_send_command* cmd, uint8_t* data, const win_handle& parent) {
158     string name;
159     BTRFS_UUID *uuid, *parent_uuid;
160     uint64_t *gen, *parent_transid;
161     ULONG uuidlen, genlen, paruuidlen, partransidlen;
162     btrfs_create_snapshot* bcs;
163     NTSTATUS Status;
164     IO_STATUS_BLOCK iosb;
165     wstring parpath;
166     btrfs_find_subvol bfs;
167     WCHAR parpathw[MAX_PATH], volpathw[MAX_PATH];
168     size_t bcslen;
169 
170     {
171         char* namebuf;
172         ULONG namelen;
173 
174         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&namebuf, &namelen))
175             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
176 
177         name = string(namebuf, namelen);
178     }
179 
180     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen))
181         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
182 
183     if (uuidlen < sizeof(BTRFS_UUID))
184         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
185 
186     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen))
187         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"transid");
188 
189     if (genlen < sizeof(uint64_t))
190         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(uint64_t));
191 
192     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&parent_uuid, &paruuidlen))
193         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
194 
195     if (paruuidlen < sizeof(BTRFS_UUID))
196         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", paruuidlen, sizeof(BTRFS_UUID));
197 
198     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&parent_transid, &partransidlen))
199         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
200 
201     if (partransidlen < sizeof(uint64_t))
202         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", partransidlen, sizeof(uint64_t));
203 
204     this->subvol_uuid = *uuid;
205     this->stransid = *gen;
206 
207     auto nameu = utf8_to_utf16(name);
208 
209     bfs.uuid = *parent_uuid;
210     bfs.ctransid = *parent_transid;
211 
212     Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol),
213                              parpathw, sizeof(parpathw));
214     if (Status == STATUS_NOT_FOUND)
215         throw string_error(IDS_RECV_CANT_FIND_PARENT_SUBVOL);
216     else if (!NT_SUCCESS(Status))
217         throw string_error(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
218 
219     if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1))
220         throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
221 
222     parpath = volpathw;
223     if (parpath.substr(parpath.length() - 1) == L"\\")
224         parpath = parpath.substr(0, parpath.length() - 1);
225 
226     parpath += parpathw;
227 
228     {
229         win_handle subvol = CreateFileW(parpath.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
230                                         nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
231         if (subvol == INVALID_HANDLE_VALUE)
232             throw string_error(IDS_RECV_CANT_OPEN_PATH, parpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
233 
234         bcslen = offsetof(btrfs_create_snapshot, name[0]) + (nameu.length() * sizeof(WCHAR));
235         bcs = (btrfs_create_snapshot*)malloc(bcslen);
236 
237         bcs->readonly = true;
238         bcs->posix = true;
239         bcs->subvol = subvol;
240         bcs->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR));
241         memcpy(bcs->name, nameu.c_str(), bcs->namelen);
242 
243         Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, (ULONG)bcslen, nullptr, 0);
244         if (!NT_SUCCESS(Status))
245             throw string_error(IDS_RECV_CREATE_SNAPSHOT_FAILED, Status, format_ntstatus(Status).c_str());
246     }
247 
248     subvolpath = dirpath;
249     subvolpath += L"\\";
250     subvolpath += nameu;
251 
252     if (dir != INVALID_HANDLE_VALUE)
253         CloseHandle(dir);
254 
255     if (master != INVALID_HANDLE_VALUE)
256         CloseHandle(master);
257 
258     master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
259                          nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
260     if (master == INVALID_HANDLE_VALUE)
261         throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
262 
263     Status = NtFsControlFile(master, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, (ULONG)bcslen, nullptr, 0);
264     if (!NT_SUCCESS(Status))
265         throw string_error(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
266 
267     dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE,
268                       FILE_SHARE_READ | FILE_SHARE_WRITE,
269                       nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
270     if (dir == INVALID_HANDLE_VALUE)
271         throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
272 
273     subvolpath += L"\\";
274 
275     add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
276 
277     num_received++;
278 }
279 
cmd_mkfile(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)280 void BtrfsRecv::cmd_mkfile(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
281     uint64_t *inode, *rdev = nullptr, *mode = nullptr;
282     ULONG inodelen;
283     NTSTATUS Status;
284     IO_STATUS_BLOCK iosb;
285     btrfs_mknod* bmn;
286     wstring nameu, pathlinku;
287 
288     {
289         char* name;
290         ULONG namelen;
291 
292         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen))
293             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
294 
295         nameu = utf8_to_utf16(string(name, namelen));
296     }
297 
298     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_INODE, (void**)&inode, &inodelen))
299         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"inode");
300 
301     if (inodelen < sizeof(uint64_t))
302         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"inode", inodelen, sizeof(uint64_t));
303 
304     if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) {
305         ULONG rdevlen, modelen;
306 
307         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_RDEV, (void**)&rdev, &rdevlen))
308             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"rdev");
309 
310         if (rdevlen < sizeof(uint64_t))
311             throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"rdev", rdev, sizeof(uint64_t));
312 
313         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen))
314             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"mode");
315 
316         if (modelen < sizeof(uint64_t))
317             throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint64_t));
318     } else if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) {
319         char* pathlink;
320         ULONG pathlinklen;
321 
322         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&pathlink, &pathlinklen))
323             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
324 
325         pathlinku = utf8_to_utf16(string(pathlink, pathlinklen));
326     }
327 
328     size_t bmnsize = sizeof(btrfs_mknod) - sizeof(WCHAR) + (nameu.length() * sizeof(WCHAR));
329     bmn = (btrfs_mknod*)malloc(bmnsize);
330 
331     bmn->inode = *inode;
332 
333     if (cmd->cmd == BTRFS_SEND_CMD_MKDIR)
334         bmn->type = BTRFS_TYPE_DIRECTORY;
335     else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD)
336         bmn->type = *mode & S_IFCHR ? BTRFS_TYPE_CHARDEV : BTRFS_TYPE_BLOCKDEV;
337     else if (cmd->cmd == BTRFS_SEND_CMD_MKFIFO)
338         bmn->type = BTRFS_TYPE_FIFO;
339     else if (cmd->cmd == BTRFS_SEND_CMD_MKSOCK)
340         bmn->type = BTRFS_TYPE_SOCKET;
341     else
342         bmn->type = BTRFS_TYPE_FILE;
343 
344     bmn->st_rdev = rdev ? *rdev : 0;
345     bmn->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR));
346     memcpy(bmn->name, nameu.c_str(), bmn->namelen);
347 
348     Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_MKNOD, bmn, (ULONG)bmnsize, nullptr, 0);
349     if (!NT_SUCCESS(Status)) {
350         free(bmn);
351         throw string_error(IDS_RECV_MKNOD_FAILED, Status, format_ntstatus(Status).c_str());
352     }
353 
354     free(bmn);
355 
356     if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) {
357         REPARSE_DATA_BUFFER* rdb;
358         btrfs_set_inode_info bsii;
359 
360         size_t rdblen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]) + (2 * pathlinku.length() * sizeof(WCHAR));
361 
362         if (rdblen >= 0x10000)
363             throw string_error(IDS_RECV_PATH_TOO_LONG, funcname);
364 
365         rdb = (REPARSE_DATA_BUFFER*)malloc(rdblen);
366 
367         rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
368         rdb->ReparseDataLength = (uint16_t)(rdblen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
369         rdb->Reserved = 0;
370         rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
371         rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR));
372         rdb->SymbolicLinkReparseBuffer.PrintNameOffset = (uint16_t)(pathlinku.length() * sizeof(WCHAR));
373         rdb->SymbolicLinkReparseBuffer.PrintNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR));
374         rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
375 
376         memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer, pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
377         memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer + (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)),
378                 pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.PrintNameLength);
379 
380         win_handle h = CreateFileW((subvolpath + nameu).c_str(), GENERIC_WRITE | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
381                                    nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
382         if (h == INVALID_HANDLE_VALUE) {
383             free(rdb);
384             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
385         }
386 
387         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_SET_REPARSE_POINT, rdb, (ULONG)rdblen, nullptr, 0);
388         if (!NT_SUCCESS(Status)) {
389             free(rdb);
390             throw string_error(IDS_RECV_SET_REPARSE_POINT_FAILED, Status, format_ntstatus(Status).c_str());
391         }
392 
393         free(rdb);
394 
395         memset(&bsii, 0, sizeof(btrfs_set_inode_info));
396 
397         bsii.mode_changed = true;
398         bsii.st_mode = 0777;
399 
400         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
401         if (!NT_SUCCESS(Status))
402             throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
403     } else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) {
404         uint64_t* mode;
405         ULONG modelen;
406 
407         if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
408             btrfs_set_inode_info bsii;
409 
410             if (modelen < sizeof(uint64_t))
411                 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint64_t));
412 
413             win_handle h = CreateFileW((subvolpath + nameu).c_str(), WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
414                                        nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
415             if (h == INVALID_HANDLE_VALUE)
416                 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
417 
418             memset(&bsii, 0, sizeof(btrfs_set_inode_info));
419 
420             bsii.mode_changed = true;
421             bsii.st_mode = (uint32_t)*mode;
422 
423             Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
424             if (!NT_SUCCESS(Status))
425                 throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
426         }
427     }
428 }
429 
cmd_rename(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)430 void BtrfsRecv::cmd_rename(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
431     wstring pathu, path_tou;
432 
433     {
434         char* path;
435         ULONG path_len;
436 
437         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len))
438             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
439 
440         pathu = utf8_to_utf16(string(path, path_len));
441     }
442 
443     {
444         char* path_to;
445         ULONG path_to_len;
446 
447         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_TO, (void**)&path_to, &path_to_len))
448             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_to");
449 
450         path_tou = utf8_to_utf16(string(path_to, path_to_len));
451     }
452 
453     if (!MoveFileW((subvolpath + pathu).c_str(), (subvolpath + path_tou).c_str()))
454         throw string_error(IDS_RECV_MOVEFILE_FAILED, pathu.c_str(), path_tou.c_str(), GetLastError(), format_message(GetLastError()).c_str());
455 }
456 
cmd_link(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)457 void BtrfsRecv::cmd_link(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
458     wstring pathu, path_linku;
459 
460     {
461         char* path;
462         ULONG path_len;
463 
464         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len))
465             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
466 
467         pathu = utf8_to_utf16(string(path, path_len));
468     }
469 
470     {
471         char* path_link;
472         ULONG path_link_len;
473 
474         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&path_link, &path_link_len))
475             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
476 
477         path_linku = utf8_to_utf16(string(path_link, path_link_len));
478     }
479 
480     if (!CreateHardLinkW((subvolpath + pathu).c_str(), (subvolpath + path_linku).c_str(), nullptr))
481         throw string_error(IDS_RECV_CREATEHARDLINK_FAILED, pathu.c_str(), path_linku.c_str(), GetLastError(), format_message(GetLastError()).c_str());
482 }
483 
cmd_unlink(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)484 void BtrfsRecv::cmd_unlink(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
485     wstring pathu;
486     ULONG att;
487 
488     {
489         char* path;
490         ULONG pathlen;
491 
492         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
493             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
494 
495         pathu = utf8_to_utf16(string(path, pathlen));
496     }
497 
498     att = GetFileAttributesW((subvolpath + pathu).c_str());
499     if (att == INVALID_FILE_ATTRIBUTES)
500         throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
501 
502     if (att & FILE_ATTRIBUTE_READONLY) {
503         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
504             throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
505     }
506 
507     if (!DeleteFileW((subvolpath + pathu).c_str()))
508         throw string_error(IDS_RECV_DELETEFILE_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
509 }
510 
cmd_rmdir(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)511 void BtrfsRecv::cmd_rmdir(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
512     wstring pathu;
513     ULONG att;
514 
515     {
516         char* path;
517         ULONG pathlen;
518 
519         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
520             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
521 
522         pathu = utf8_to_utf16(string(path, pathlen));
523     }
524 
525     att = GetFileAttributesW((subvolpath + pathu).c_str());
526     if (att == INVALID_FILE_ATTRIBUTES)
527         throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
528 
529     if (att & FILE_ATTRIBUTE_READONLY) {
530         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
531             throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
532     }
533 
534     if (!RemoveDirectoryW((subvolpath + pathu).c_str()))
535         throw string_error(IDS_RECV_REMOVEDIRECTORY_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
536 }
537 
cmd_setxattr(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)538 void BtrfsRecv::cmd_setxattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
539     string xattrname;
540     uint8_t* xattrdata;
541     ULONG xattrdatalen;
542     wstring pathu;
543 
544     {
545         char* path;
546         ULONG pathlen;
547 
548         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
549             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
550 
551         pathu = utf8_to_utf16(string(path, pathlen));
552     }
553 
554     {
555         char* xattrnamebuf;
556         ULONG xattrnamelen;
557 
558         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen))
559             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
560 
561         xattrname = string(xattrnamebuf, xattrnamelen);
562     }
563 
564     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_DATA, (void**)&xattrdata, &xattrdatalen))
565         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_data");
566 
567     if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER &&
568         xattrname != EA_DOSATTRIB && xattrname != EA_EA && xattrname != EA_REPARSE) {
569         ULONG att;
570 
571         auto streamname = utf8_to_utf16(xattrname);
572 
573         att = GetFileAttributesW((subvolpath + pathu).c_str());
574         if (att == INVALID_FILE_ATTRIBUTES)
575             throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
576 
577         if (att & FILE_ATTRIBUTE_READONLY) {
578             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
579                 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
580         }
581 
582         streamname = streamname.substr(XATTR_USER.length());
583 
584         win_handle h = CreateFileW((subvolpath + pathu + L":" + streamname).c_str(), GENERIC_WRITE, 0,
585                                    nullptr, CREATE_ALWAYS, FILE_FLAG_POSIX_SEMANTICS, nullptr);
586         if (h == INVALID_HANDLE_VALUE)
587             throw string_error(IDS_RECV_CANT_CREATE_FILE, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
588 
589         if (xattrdatalen > 0) {
590             if (!WriteFile(h, xattrdata, xattrdatalen, nullptr, nullptr))
591                 throw string_error(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
592         }
593 
594         if (att & FILE_ATTRIBUTE_READONLY) {
595             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att))
596                 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
597         }
598     } else {
599         IO_STATUS_BLOCK iosb;
600         NTSTATUS Status;
601         ULONG perms = FILE_WRITE_ATTRIBUTES;
602         btrfs_set_xattr* bsxa;
603 
604         if (xattrname == EA_NTACL)
605             perms |= WRITE_DAC | WRITE_OWNER;
606         else if (xattrname == EA_EA)
607             perms |= FILE_WRITE_EA;
608 
609         win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
610                                    nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr);
611         if (h == INVALID_HANDLE_VALUE)
612             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
613 
614         size_t bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length() + xattrdatalen;
615         bsxa = (btrfs_set_xattr*)malloc(bsxalen);
616         if (!bsxa)
617             throw string_error(IDS_OUT_OF_MEMORY);
618 
619         bsxa->namelen = (uint16_t)xattrname.length();
620         bsxa->valuelen = (uint16_t)xattrdatalen;
621         memcpy(bsxa->data, xattrname.c_str(), xattrname.length());
622         memcpy(&bsxa->data[xattrname.length()], xattrdata, xattrdatalen);
623 
624         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, (ULONG)bsxalen, nullptr, 0);
625         if (!NT_SUCCESS(Status)) {
626             free(bsxa);
627             throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
628         }
629 
630         free(bsxa);
631     }
632 }
633 
cmd_removexattr(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)634 void BtrfsRecv::cmd_removexattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
635     wstring pathu;
636     string xattrname;
637 
638     {
639         char* path;
640         ULONG pathlen;
641 
642         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
643             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
644 
645         pathu = utf8_to_utf16(string(path, pathlen));
646     }
647 
648     {
649         char* xattrnamebuf;
650         ULONG xattrnamelen;
651 
652         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen))
653             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
654 
655         xattrname = string(xattrnamebuf, xattrnamelen);
656     }
657 
658     if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER && xattrname != EA_DOSATTRIB && xattrname != EA_EA) { // deleting stream
659         ULONG att;
660 
661         auto streamname = utf8_to_utf16(xattrname);
662 
663         streamname = streamname.substr(XATTR_USER.length());
664 
665         att = GetFileAttributesW((subvolpath + pathu).c_str());
666         if (att == INVALID_FILE_ATTRIBUTES)
667             throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
668 
669         if (att & FILE_ATTRIBUTE_READONLY) {
670             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
671                 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
672         }
673 
674         if (!DeleteFileW((subvolpath + pathu + L":" + streamname).c_str()))
675             throw string_error(IDS_RECV_DELETEFILE_FAILED, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
676 
677         if (att & FILE_ATTRIBUTE_READONLY) {
678             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att))
679                 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
680         }
681     } else {
682         IO_STATUS_BLOCK iosb;
683         NTSTATUS Status;
684         ULONG perms = FILE_WRITE_ATTRIBUTES;
685         btrfs_set_xattr* bsxa;
686 
687         if (xattrname == EA_NTACL)
688             perms |= WRITE_DAC | WRITE_OWNER;
689         else if (xattrname == EA_EA)
690             perms |= FILE_WRITE_EA;
691 
692         win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
693                                    nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr);
694         if (h == INVALID_HANDLE_VALUE)
695             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
696 
697         size_t bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length();
698         bsxa = (btrfs_set_xattr*)malloc(bsxalen);
699         if (!bsxa)
700             throw string_error(IDS_OUT_OF_MEMORY);
701 
702         bsxa->namelen = (uint16_t)(xattrname.length());
703         bsxa->valuelen = 0;
704         memcpy(bsxa->data, xattrname.c_str(), xattrname.length());
705 
706         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, (ULONG)bsxalen, nullptr, 0);
707         if (!NT_SUCCESS(Status)) {
708             free(bsxa);
709             throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
710         }
711 
712         free(bsxa);
713     }
714 }
715 
cmd_write(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)716 void BtrfsRecv::cmd_write(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
717     uint64_t* offset;
718     uint8_t* writedata;
719     ULONG offsetlen, datalen;
720     wstring pathu;
721     HANDLE h;
722     LARGE_INTEGER offli;
723     NTSTATUS Status;
724     IO_STATUS_BLOCK iosb;
725 
726     {
727         char* path;
728         ULONG pathlen;
729 
730         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
731             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
732 
733         pathu = utf8_to_utf16(string(path, pathlen));
734     }
735 
736     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen))
737         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"offset");
738 
739     if (offsetlen < sizeof(uint64_t))
740         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(uint64_t));
741 
742     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_DATA, (void**)&writedata, &datalen))
743         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"data");
744 
745     if (lastwritepath != pathu) {
746         FILE_BASIC_INFO fbi;
747 
748         if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
749             if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt))
750                 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
751         }
752 
753         CloseHandle(lastwritefile);
754 
755         lastwriteatt = GetFileAttributesW((subvolpath + pathu).c_str());
756         if (lastwriteatt == INVALID_FILE_ATTRIBUTES)
757             throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
758 
759         if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
760             if (!SetFileAttributesW((subvolpath + pathu).c_str(), lastwriteatt & ~FILE_ATTRIBUTE_READONLY))
761                 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
762         }
763 
764         h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING,
765                         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
766         if (h == INVALID_HANDLE_VALUE)
767             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
768 
769         lastwritepath = pathu;
770         lastwritefile = h;
771 
772         memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
773 
774         fbi.LastWriteTime.QuadPart = -1;
775 
776         Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation);
777         if (!NT_SUCCESS(Status))
778             throw ntstatus_error(Status);
779     } else
780         h = lastwritefile;
781 
782     offli.QuadPart = *offset;
783 
784     if (SetFilePointer(h, offli.LowPart, &offli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
785         throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
786 
787     if (!WriteFile(h, writedata, datalen, nullptr, nullptr))
788         throw string_error(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
789 }
790 
cmd_clone(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)791 void BtrfsRecv::cmd_clone(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
792     uint64_t *offset, *cloneoffset, *clonetransid, *clonelen;
793     BTRFS_UUID* cloneuuid;
794     ULONG i, offsetlen, cloneoffsetlen, cloneuuidlen, clonetransidlen, clonelenlen;
795     wstring pathu, clonepathu, clonepar;
796     btrfs_find_subvol bfs;
797     NTSTATUS Status;
798     IO_STATUS_BLOCK iosb;
799     WCHAR cloneparw[MAX_PATH];
800     DUPLICATE_EXTENTS_DATA ded;
801     LARGE_INTEGER filesize;
802     bool found = false;
803 
804     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen))
805         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"offset");
806 
807     if (offsetlen < sizeof(uint64_t))
808         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(uint64_t));
809 
810     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_LENGTH, (void**)&clonelen, &clonelenlen))
811         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_len");
812 
813     if (clonelenlen < sizeof(uint64_t))
814         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_len", clonelenlen, sizeof(uint64_t));
815 
816     {
817         char* path;
818         ULONG pathlen;
819 
820         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
821             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
822 
823         pathu = utf8_to_utf16(string(path, pathlen));
824     }
825 
826     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&cloneuuid, &cloneuuidlen))
827         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
828 
829     if (cloneuuidlen < sizeof(BTRFS_UUID))
830         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", cloneuuidlen, sizeof(BTRFS_UUID));
831 
832     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&clonetransid, &clonetransidlen))
833         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
834 
835     if (clonetransidlen < sizeof(uint64_t))
836         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", clonetransidlen, sizeof(uint64_t));
837 
838     {
839         char* clonepath;
840         ULONG clonepathlen;
841 
842         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_PATH, (void**)&clonepath, &clonepathlen))
843             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_path");
844 
845         clonepathu = utf8_to_utf16(string(clonepath, clonepathlen));
846     }
847 
848     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_OFFSET, (void**)&cloneoffset, &cloneoffsetlen))
849         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"clone_offset");
850 
851     if (cloneoffsetlen < sizeof(uint64_t))
852         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"clone_offset", cloneoffsetlen, sizeof(uint64_t));
853 
854     for (i = 0; i < cache.size(); i++) {
855         if (!memcmp(cloneuuid, &cache[i].uuid, sizeof(BTRFS_UUID)) && *clonetransid == cache[i].transid) {
856             clonepar = cache[i].path;
857             found = true;
858             break;
859         }
860     }
861 
862     if (!found) {
863         WCHAR volpathw[MAX_PATH];
864 
865         bfs.uuid = *cloneuuid;
866         bfs.ctransid = *clonetransid;
867 
868         Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol),
869                                  cloneparw, sizeof(cloneparw));
870         if (Status == STATUS_NOT_FOUND)
871             throw string_error(IDS_RECV_CANT_FIND_CLONE_SUBVOL);
872         else if (!NT_SUCCESS(Status))
873             throw string_error(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
874 
875         if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1))
876             throw string_error(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
877 
878         clonepar = volpathw;
879         if (clonepar.substr(clonepar.length() - 1) == L"\\")
880             clonepar = clonepar.substr(0, clonepar.length() - 1);
881 
882         clonepar += cloneparw;
883         clonepar += L"\\";
884 
885         add_cache_entry(cloneuuid, *clonetransid, clonepar);
886     }
887 
888     {
889         win_handle src = CreateFileW((clonepar + clonepathu).c_str(), FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
890                                      nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr);
891         if (src == INVALID_HANDLE_VALUE)
892             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, (clonepar + clonepathu).c_str(), GetLastError(), format_message(GetLastError()).c_str());
893 
894         win_handle dest = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
895                                       nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr);
896         if (dest == INVALID_HANDLE_VALUE)
897             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
898 
899         if (!GetFileSizeEx(dest, &filesize))
900             throw string_error(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
901 
902         if ((uint64_t)filesize.QuadPart < *offset + *clonelen) {
903             LARGE_INTEGER sizeli;
904 
905             sizeli.QuadPart = *offset + *clonelen;
906 
907             if (SetFilePointer(dest, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
908                 throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
909 
910             if (!SetEndOfFile(dest))
911                 throw string_error(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
912         }
913 
914         ded.FileHandle = src;
915         ded.SourceFileOffset.QuadPart = *cloneoffset;
916         ded.TargetFileOffset.QuadPart = *offset;
917         ded.ByteCount.QuadPart = *clonelen;
918 
919         Status = NtFsControlFile(dest, nullptr, nullptr, nullptr, &iosb, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA),
920                                  nullptr, 0);
921         if (!NT_SUCCESS(Status))
922             throw string_error(IDS_RECV_DUPLICATE_EXTENTS_FAILED, Status, format_ntstatus(Status).c_str());
923     }
924 }
925 
cmd_truncate(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)926 void BtrfsRecv::cmd_truncate(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
927     uint64_t* size;
928     ULONG sizelen;
929     wstring pathu;
930     LARGE_INTEGER sizeli;
931     DWORD att;
932 
933     {
934         char* path;
935         ULONG pathlen;
936 
937         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
938             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
939 
940         pathu = utf8_to_utf16(string(path, pathlen));
941     }
942 
943     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_SIZE, (void**)&size, &sizelen))
944         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"size");
945 
946     if (sizelen < sizeof(uint64_t))
947         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"size", sizelen, sizeof(uint64_t));
948 
949     att = GetFileAttributesW((subvolpath + pathu).c_str());
950     if (att == INVALID_FILE_ATTRIBUTES)
951         throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
952 
953     if (att & FILE_ATTRIBUTE_READONLY) {
954         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
955             throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
956     }
957 
958     {
959         win_handle h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, 0, nullptr, OPEN_EXISTING,
960                                    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
961 
962         if (h == INVALID_HANDLE_VALUE)
963             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
964 
965         sizeli.QuadPart = *size;
966 
967         if (SetFilePointer(h, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
968             throw string_error(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
969 
970         if (!SetEndOfFile(h))
971             throw string_error(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
972     }
973 
974     if (att & FILE_ATTRIBUTE_READONLY) {
975         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att))
976             throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
977     }
978 }
979 
cmd_chmod(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)980 void BtrfsRecv::cmd_chmod(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
981     win_handle h;
982     uint32_t* mode;
983     ULONG modelen;
984     wstring pathu;
985     btrfs_set_inode_info bsii;
986     NTSTATUS Status;
987     IO_STATUS_BLOCK iosb;
988 
989     {
990         char* path;
991         ULONG pathlen;
992 
993         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
994             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
995 
996         pathu = utf8_to_utf16(string(path, pathlen));
997     }
998 
999     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen))
1000         throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"mode");
1001 
1002     if (modelen < sizeof(uint32_t))
1003         throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint32_t));
1004 
1005     h = CreateFileW((subvolpath + pathu).c_str(), WRITE_DAC, 0, nullptr, OPEN_EXISTING,
1006                     FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr);
1007     if (h == INVALID_HANDLE_VALUE)
1008         throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1009 
1010     memset(&bsii, 0, sizeof(btrfs_set_inode_info));
1011 
1012     bsii.mode_changed = true;
1013     bsii.st_mode = *mode;
1014 
1015     Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
1016     if (!NT_SUCCESS(Status))
1017         throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
1018 }
1019 
cmd_chown(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)1020 void BtrfsRecv::cmd_chown(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
1021     win_handle h;
1022     uint32_t *uid, *gid;
1023     ULONG uidlen, gidlen;
1024     wstring pathu;
1025     btrfs_set_inode_info bsii;
1026 
1027     {
1028         char* path;
1029         ULONG pathlen;
1030 
1031         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
1032             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
1033 
1034         pathu = utf8_to_utf16(string(path, pathlen));
1035     }
1036 
1037     h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES | WRITE_OWNER | WRITE_DAC, 0, nullptr, OPEN_EXISTING,
1038                     FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr);
1039     if (h == INVALID_HANDLE_VALUE)
1040         throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1041 
1042     memset(&bsii, 0, sizeof(btrfs_set_inode_info));
1043 
1044     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_UID, (void**)&uid, &uidlen)) {
1045         if (uidlen < sizeof(uint32_t))
1046             throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uid", uidlen, sizeof(uint32_t));
1047 
1048         bsii.uid_changed = true;
1049         bsii.st_uid = *uid;
1050     }
1051 
1052     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_GID, (void**)&gid, &gidlen)) {
1053         if (gidlen < sizeof(uint32_t))
1054             throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"gid", gidlen, sizeof(uint32_t));
1055 
1056         bsii.gid_changed = true;
1057         bsii.st_gid = *gid;
1058     }
1059 
1060     if (bsii.uid_changed || bsii.gid_changed) {
1061         NTSTATUS Status;
1062         IO_STATUS_BLOCK iosb;
1063 
1064         Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
1065         if (!NT_SUCCESS(Status))
1066             throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
1067     }
1068 }
1069 
unix_time_to_win(BTRFS_TIME * t)1070 static __inline uint64_t unix_time_to_win(BTRFS_TIME* t) {
1071     return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000;
1072 }
1073 
cmd_utimes(HWND hwnd,btrfs_send_command * cmd,uint8_t * data)1074 void BtrfsRecv::cmd_utimes(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
1075     wstring pathu;
1076     win_handle h;
1077     FILE_BASIC_INFO fbi;
1078     BTRFS_TIME* time;
1079     ULONG timelen;
1080     IO_STATUS_BLOCK iosb;
1081     NTSTATUS Status;
1082 
1083     {
1084         char* path;
1085         ULONG pathlen;
1086 
1087         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
1088             throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
1089 
1090         pathu = utf8_to_utf16(string(path, pathlen));
1091     }
1092 
1093     h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING,
1094                     FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr);
1095     if (h == INVALID_HANDLE_VALUE)
1096         throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1097 
1098     memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
1099 
1100     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_OTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1101         fbi.CreationTime.QuadPart = unix_time_to_win(time);
1102 
1103     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_ATIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1104         fbi.LastAccessTime.QuadPart = unix_time_to_win(time);
1105 
1106     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1107         fbi.LastWriteTime.QuadPart = unix_time_to_win(time);
1108 
1109     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_CTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1110         fbi.ChangeTime.QuadPart = unix_time_to_win(time);
1111 
1112     Status = NtSetInformationFile(h, &iosb, &fbi, sizeof(FILE_BASIC_INFO), FileBasicInformation);
1113     if (!NT_SUCCESS(Status))
1114         throw ntstatus_error(Status);
1115 }
1116 
delete_directory(const wstring & dir)1117 static void delete_directory(const wstring& dir) {
1118     WIN32_FIND_DATAW fff;
1119 
1120     fff_handle h = FindFirstFileW((dir + L"*").c_str(), &fff);
1121 
1122     if (h == INVALID_HANDLE_VALUE)
1123         return;
1124 
1125     do {
1126         wstring file;
1127 
1128         file = fff.cFileName;
1129 
1130         if (file != L"." && file != L"..") {
1131             if (fff.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1132                 SetFileAttributesW((dir + file).c_str(), fff.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
1133 
1134             if (fff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1135                 if (!(fff.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
1136                     delete_directory(dir + file + L"\\");
1137                 else
1138                     RemoveDirectoryW((dir + file).c_str());
1139             } else
1140                 DeleteFileW((dir + file).c_str());
1141         }
1142     } while (FindNextFileW(h, &fff));
1143 
1144     RemoveDirectoryW(dir.c_str());
1145 }
1146 
check_csum(btrfs_send_command * cmd,uint8_t * data)1147 static bool check_csum(btrfs_send_command* cmd, uint8_t* data) {
1148     uint32_t crc32 = cmd->csum, calc;
1149 
1150     cmd->csum = 0;
1151 
1152     calc = calc_crc32c(0, (uint8_t*)cmd, sizeof(btrfs_send_command));
1153 
1154     if (cmd->length > 0)
1155         calc = calc_crc32c(calc, data, cmd->length);
1156 
1157     return calc == crc32 ? true : false;
1158 }
1159 
do_recv(const win_handle & f,uint64_t * pos,uint64_t size,const win_handle & parent)1160 void BtrfsRecv::do_recv(const win_handle& f, uint64_t* pos, uint64_t size, const win_handle& parent) {
1161     try {
1162         btrfs_send_header header;
1163         bool ended = false;
1164 
1165         if (!ReadFile(f, &header, sizeof(btrfs_send_header), nullptr, nullptr))
1166             throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1167 
1168         *pos += sizeof(btrfs_send_header);
1169 
1170         if (memcmp(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic)))
1171             throw string_error(IDS_RECV_NOT_A_SEND_STREAM);
1172 
1173         if (header.version > 1)
1174             throw string_error(IDS_RECV_UNSUPPORTED_VERSION, header.version);
1175 
1176         SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)65536);
1177 
1178         lastwritefile = INVALID_HANDLE_VALUE;
1179         lastwritepath = L"";
1180         lastwriteatt = 0;
1181 
1182         while (true) {
1183             btrfs_send_command cmd;
1184             uint8_t* data = nullptr;
1185             ULONG progress;
1186 
1187             if (cancelling)
1188                 break;
1189 
1190             progress = (ULONG)((float)*pos * 65536.0f / (float)size);
1191             SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, progress, 0);
1192 
1193             if (!ReadFile(f, &cmd, sizeof(btrfs_send_command), nullptr, nullptr)) {
1194                 if (GetLastError() != ERROR_HANDLE_EOF)
1195                     throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1196 
1197                 break;
1198             }
1199 
1200             *pos += sizeof(btrfs_send_command);
1201 
1202             if (cmd.length > 0) {
1203                 if (*pos + cmd.length > size)
1204                     throw string_error(IDS_RECV_FILE_TRUNCATED);
1205 
1206                 data = (uint8_t*)malloc(cmd.length);
1207                 if (!data)
1208                     throw string_error(IDS_OUT_OF_MEMORY);
1209             }
1210 
1211             try {
1212                 if (data) {
1213                     if (!ReadFile(f, data, cmd.length, nullptr, nullptr))
1214                         throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1215 
1216                     *pos += cmd.length;
1217                 }
1218 
1219                 if (!check_csum(&cmd, data))
1220                     throw string_error(IDS_RECV_CSUM_ERROR);
1221 
1222                 if (cmd.cmd == BTRFS_SEND_CMD_END) {
1223                     ended = true;
1224                     break;
1225                 }
1226 
1227                 if (lastwritefile != INVALID_HANDLE_VALUE && cmd.cmd != BTRFS_SEND_CMD_WRITE) {
1228                     if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1229                         if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt))
1230                             throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1231                     }
1232 
1233                     CloseHandle(lastwritefile);
1234 
1235                     lastwritefile = INVALID_HANDLE_VALUE;
1236                     lastwritepath = L"";
1237                     lastwriteatt = 0;
1238                 }
1239 
1240                 switch (cmd.cmd) {
1241                     case BTRFS_SEND_CMD_SUBVOL:
1242                         cmd_subvol(hwnd, &cmd, data, parent);
1243                     break;
1244 
1245                     case BTRFS_SEND_CMD_SNAPSHOT:
1246                         cmd_snapshot(hwnd, &cmd, data, parent);
1247                     break;
1248 
1249                     case BTRFS_SEND_CMD_MKFILE:
1250                     case BTRFS_SEND_CMD_MKDIR:
1251                     case BTRFS_SEND_CMD_MKNOD:
1252                     case BTRFS_SEND_CMD_MKFIFO:
1253                     case BTRFS_SEND_CMD_MKSOCK:
1254                     case BTRFS_SEND_CMD_SYMLINK:
1255                         cmd_mkfile(hwnd, &cmd, data);
1256                     break;
1257 
1258                     case BTRFS_SEND_CMD_RENAME:
1259                         cmd_rename(hwnd, &cmd, data);
1260                     break;
1261 
1262                     case BTRFS_SEND_CMD_LINK:
1263                         cmd_link(hwnd, &cmd, data);
1264                     break;
1265 
1266                     case BTRFS_SEND_CMD_UNLINK:
1267                         cmd_unlink(hwnd, &cmd, data);
1268                     break;
1269 
1270                     case BTRFS_SEND_CMD_RMDIR:
1271                         cmd_rmdir(hwnd, &cmd, data);
1272                     break;
1273 
1274                     case BTRFS_SEND_CMD_SET_XATTR:
1275                         cmd_setxattr(hwnd, &cmd, data);
1276                     break;
1277 
1278                     case BTRFS_SEND_CMD_REMOVE_XATTR:
1279                         cmd_removexattr(hwnd, &cmd, data);
1280                     break;
1281 
1282                     case BTRFS_SEND_CMD_WRITE:
1283                         cmd_write(hwnd, &cmd, data);
1284                     break;
1285 
1286                     case BTRFS_SEND_CMD_CLONE:
1287                         cmd_clone(hwnd, &cmd, data);
1288                     break;
1289 
1290                     case BTRFS_SEND_CMD_TRUNCATE:
1291                         cmd_truncate(hwnd, &cmd, data);
1292                     break;
1293 
1294                     case BTRFS_SEND_CMD_CHMOD:
1295                         cmd_chmod(hwnd, &cmd, data);
1296                     break;
1297 
1298                     case BTRFS_SEND_CMD_CHOWN:
1299                         cmd_chown(hwnd, &cmd, data);
1300                     break;
1301 
1302                     case BTRFS_SEND_CMD_UTIMES:
1303                         cmd_utimes(hwnd, &cmd, data);
1304                     break;
1305 
1306                     case BTRFS_SEND_CMD_UPDATE_EXTENT:
1307                         // does nothing
1308                     break;
1309 
1310                     default:
1311                         throw string_error(IDS_RECV_UNKNOWN_COMMAND, cmd.cmd);
1312                 }
1313             } catch (...) {
1314                 if (data) free(data);
1315                 throw;
1316             }
1317 
1318             if (data) free(data);
1319         }
1320 
1321         if (lastwritefile != INVALID_HANDLE_VALUE) {
1322             if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1323                 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt))
1324                     throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1325             }
1326 
1327             CloseHandle(lastwritefile);
1328         }
1329 
1330         if (!ended && !cancelling)
1331             throw string_error(IDS_RECV_FILE_TRUNCATED);
1332 
1333         if (!cancelling) {
1334             NTSTATUS Status;
1335             IO_STATUS_BLOCK iosb;
1336             btrfs_received_subvol brs;
1337 
1338             brs.generation = stransid;
1339             brs.uuid = subvol_uuid;
1340 
1341             Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RECEIVED_SUBVOL, &brs, sizeof(btrfs_received_subvol),
1342                                     nullptr, 0);
1343             if (!NT_SUCCESS(Status))
1344                 throw string_error(IDS_RECV_RECEIVED_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
1345         }
1346 
1347         CloseHandle(dir);
1348 
1349         if (master != INVALID_HANDLE_VALUE)
1350             CloseHandle(master);
1351     } catch (...) {
1352         if (subvolpath != L"") {
1353             ULONG attrib;
1354 
1355             attrib = GetFileAttributesW(subvolpath.c_str());
1356             attrib &= ~FILE_ATTRIBUTE_READONLY;
1357 
1358             if (SetFileAttributesW(subvolpath.c_str(), attrib))
1359                 delete_directory(subvolpath);
1360         }
1361 
1362         throw;
1363     }
1364 }
1365 
1366 #if defined(_X86_) || defined(_AMD64_)
check_cpu()1367 static void check_cpu() {
1368     bool have_sse42 = false;
1369 
1370 #ifndef _MSC_VER
1371     {
1372         uint32_t eax, ebx, ecx, edx;
1373 
1374         __cpuid(1, eax, ebx, ecx, edx);
1375 
1376         if (__get_cpuid(1, &eax, &ebx, &ecx, &edx))
1377             have_sse42 = ecx & bit_SSE4_2;
1378     }
1379 #else
1380     {
1381         int cpu_info[4];
1382 
1383         __cpuid(cpu_info, 1);
1384         have_sse42 = (unsigned int)cpu_info[2] & (1 << 20);
1385     }
1386 #endif
1387 
1388     if (have_sse42)
1389         calc_crc32c = calc_crc32c_hw;
1390 }
1391 #endif
1392 
recv_thread()1393 DWORD BtrfsRecv::recv_thread() {
1394     LARGE_INTEGER size;
1395     uint64_t pos = 0;
1396     bool b = true;
1397 
1398     running = true;
1399 
1400     try {
1401         win_handle f = CreateFileW(streamfile.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
1402         if (f == INVALID_HANDLE_VALUE)
1403             throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, streamfile.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1404 
1405         if (!GetFileSizeEx(f, &size))
1406             throw string_error(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1407 
1408         {
1409             win_handle parent = CreateFileW(dirpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1410                                             nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
1411             if (parent == INVALID_HANDLE_VALUE)
1412                 throw string_error(IDS_RECV_CANT_OPEN_PATH, dirpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1413 
1414             do {
1415                 do_recv(f, &pos, size.QuadPart, parent);
1416             } while (pos < (uint64_t)size.QuadPart);
1417         }
1418     } catch (const exception& e) {
1419         auto msg = utf8_to_utf16(e.what());
1420 
1421         SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str());
1422 
1423         SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0);
1424 
1425         b = false;
1426     }
1427 
1428     if (b && hwnd) {
1429         wstring s;
1430 
1431         SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 65536, 0);
1432 
1433         if (num_received == 1) {
1434             load_string(module, IDS_RECV_SUCCESS, s);
1435             SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str());
1436         } else {
1437             wstring t;
1438 
1439             load_string(module, IDS_RECV_SUCCESS_PLURAL, s);
1440 
1441             wstring_sprintf(t, s, num_received);
1442 
1443             SetDlgItemTextW(hwnd, IDC_RECV_MSG, t.c_str());
1444         }
1445 
1446         load_string(module, IDS_RECV_BUTTON_OK, s);
1447 
1448         SetDlgItemTextW(hwnd, IDCANCEL, s.c_str());
1449     }
1450 
1451     thread = nullptr;
1452     running = false;
1453 
1454     return 0;
1455 }
1456 
global_recv_thread(LPVOID lpParameter)1457 static DWORD WINAPI global_recv_thread(LPVOID lpParameter) {
1458     BtrfsRecv* br = (BtrfsRecv*)lpParameter;
1459 
1460     return br->recv_thread();
1461 }
1462 
RecvProgressDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1463 INT_PTR CALLBACK BtrfsRecv::RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1464     switch (uMsg) {
1465         case WM_INITDIALOG:
1466             try {
1467                 this->hwnd = hwndDlg;
1468                 thread = CreateThread(nullptr, 0, global_recv_thread, this, 0, nullptr);
1469 
1470                 if (!thread)
1471                     throw string_error(IDS_RECV_CREATETHREAD_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1472             } catch (const exception& e) {
1473                 auto msg = utf8_to_utf16(e.what());
1474 
1475                 SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str());
1476 
1477                 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0);
1478             }
1479         break;
1480 
1481         case WM_COMMAND:
1482             switch (HIWORD(wParam)) {
1483                 case BN_CLICKED:
1484                     switch (LOWORD(wParam)) {
1485                         case IDOK:
1486                         case IDCANCEL:
1487                             if (running) {
1488                                 wstring s;
1489 
1490                                 cancelling = true;
1491 
1492                                 if (!load_string(module, IDS_RECV_CANCELLED, s))
1493                                     throw last_error(GetLastError());
1494 
1495                                 SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str());
1496                                 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 0, 0);
1497 
1498                                 if (!load_string(module, IDS_RECV_BUTTON_OK, s))
1499                                     throw last_error(GetLastError());
1500 
1501                                 SetDlgItemTextW(hwnd, IDCANCEL, s.c_str());
1502                             } else
1503                                 EndDialog(hwndDlg, 1);
1504 
1505                             return true;
1506                     }
1507                 break;
1508             }
1509         break;
1510     }
1511 
1512     return false;
1513 }
1514 
stub_RecvProgressDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1515 static INT_PTR CALLBACK stub_RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1516     BtrfsRecv* br;
1517 
1518     if (uMsg == WM_INITDIALOG) {
1519         SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
1520         br = (BtrfsRecv*)lParam;
1521     } else {
1522         br = (BtrfsRecv*)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
1523     }
1524 
1525     if (br)
1526         return br->RecvProgressDlgProc(hwndDlg, uMsg, wParam, lParam);
1527     else
1528         return false;
1529 }
1530 
Open(HWND hwnd,const wstring & file,const wstring & path,bool quiet)1531 void BtrfsRecv::Open(HWND hwnd, const wstring& file, const wstring& path, bool quiet) {
1532     streamfile = file;
1533     dirpath = path;
1534     subvolpath = L"";
1535 
1536 #if defined(_X86_) || defined(_AMD64_)
1537     check_cpu();
1538 #endif
1539 
1540     if (quiet)
1541         recv_thread();
1542     else {
1543         if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RECV_PROGRESS), hwnd, stub_RecvProgressDlgProc, (LPARAM)this) <= 0)
1544             throw last_error(GetLastError());
1545     }
1546 }
1547 
RecvSubvolGUIW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)1548 extern "C" void CALLBACK RecvSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1549     try {
1550         OPENFILENAMEW ofn;
1551         WCHAR file[MAX_PATH];
1552         win_handle token;
1553         TOKEN_PRIVILEGES* tp;
1554         LUID luid;
1555         ULONG tplen;
1556 
1557         set_dpi_aware();
1558 
1559         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1560             throw last_error(GetLastError());
1561 
1562         tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
1563         tp = (TOKEN_PRIVILEGES*)malloc(tplen);
1564         if (!tp)
1565             throw string_error(IDS_OUT_OF_MEMORY);
1566 
1567         tp->PrivilegeCount = 3;
1568 
1569 #ifdef __clang__
1570 #pragma clang diagnostic push
1571 #pragma clang diagnostic ignored "-Warray-bounds"
1572 #endif // __clang__
1573 
1574         if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) {
1575             free(tp);
1576             throw last_error(GetLastError());
1577         }
1578 
1579         tp->Privileges[0].Luid = luid;
1580         tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1581 
1582         if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) {
1583             free(tp);
1584             throw last_error(GetLastError());
1585         }
1586 
1587         tp->Privileges[1].Luid = luid;
1588         tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
1589 
1590         if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) {
1591             free(tp);
1592             throw last_error(GetLastError());
1593         }
1594 
1595         tp->Privileges[2].Luid = luid;
1596         tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
1597 
1598         if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) {
1599             free(tp);
1600             throw last_error(GetLastError());
1601         }
1602 
1603 #ifdef __clang__
1604     #pragma clang diagnostic pop
1605 #endif // __clang__
1606 
1607         file[0] = 0;
1608 
1609         memset(&ofn, 0, sizeof(OPENFILENAMEW));
1610         ofn.lStructSize = sizeof(OPENFILENAMEW);
1611         ofn.hwndOwner = hwnd;
1612         ofn.hInstance = module;
1613         ofn.lpstrFile = file;
1614         ofn.nMaxFile = sizeof(file) / sizeof(WCHAR);
1615         ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1616 
1617         if (GetOpenFileNameW(&ofn)) {
1618             BtrfsRecv recv;
1619 
1620             recv.Open(hwnd, file, lpszCmdLine, false);
1621         }
1622 
1623         free(tp);
1624     } catch (const exception& e) {
1625         error_message(hwnd, e.what());
1626     }
1627 }
1628 
RecvSubvolW(HWND hwnd,HINSTANCE hinst,LPWSTR lpszCmdLine,int nCmdShow)1629 extern "C" void CALLBACK RecvSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1630     try {
1631         vector<wstring> args;
1632 
1633         command_line_to_args(lpszCmdLine, args);
1634 
1635         if (args.size() >= 2) {
1636             win_handle token;
1637             TOKEN_PRIVILEGES* tp;
1638             ULONG tplen;
1639             LUID luid;
1640 
1641             if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1642                 return;
1643 
1644             tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
1645             tp = (TOKEN_PRIVILEGES*)malloc(tplen);
1646             if (!tp)
1647                 return;
1648 
1649             tp->PrivilegeCount = 3;
1650 
1651 #ifdef __clang__
1652 #pragma clang diagnostic push
1653 #pragma clang diagnostic ignored "-Warray-bounds"
1654 #endif // __clang__
1655 
1656             if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) {
1657                 free(tp);
1658                 return;
1659             }
1660 
1661             tp->Privileges[0].Luid = luid;
1662             tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1663 
1664             if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) {
1665                 free(tp);
1666                 return;
1667             }
1668 
1669             tp->Privileges[1].Luid = luid;
1670             tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
1671 
1672             if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) {
1673                 free(tp);
1674                 return;
1675             }
1676 
1677             tp->Privileges[2].Luid = luid;
1678             tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
1679 
1680 #ifdef __clang__
1681 #pragma clang diagnostic pop
1682 #endif // __clang__
1683 
1684             if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) {
1685                 free(tp);
1686                 return;
1687             }
1688 
1689             free(tp);
1690 
1691             BtrfsRecv br;
1692             br.Open(nullptr, args[0], args[1], true);
1693         }
1694     } catch (const exception& e) {
1695         cerr << "Error: " << e.what() << endl;
1696     }
1697 }
1698