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