xref: /reactos/dll/shellext/shellbtrfs/recv.cpp (revision aad80191)
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 <string>
24 #include "recv.h"
25 #include "resource.h"
26 
27 #ifndef __REACTOS__
28 #ifndef _MSC_VER
29 #include <cpuid.h>
30 #else
31 #include <intrin.h>
32 #endif
33 
34 #include <smmintrin.h>
35 #endif
36 
37 #define EA_NTACL "security.NTACL"
38 #define EA_DOSATTRIB "user.DOSATTRIB"
39 #define EA_REPARSE "user.reparse"
40 #define EA_EA "user.EA"
41 #define XATTR_USER "user."
42 
43 #ifndef __REACTOS__
44 BOOL have_sse42 = FALSE;
45 #endif
46 
47 static const UINT32 crctable[] = {
48     0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
49     0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
50     0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
51     0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
52     0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
53     0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
54     0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
55     0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
56     0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
57     0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
58     0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
59     0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
60     0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
61     0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
62     0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
63     0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
64     0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
65     0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
66     0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
67     0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
68     0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
69     0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
70     0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
71     0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
72     0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
73     0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
74     0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
75     0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
76     0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
77     0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
78     0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
79     0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
80 };
81 
82 // HW code taken from https://github.com/rurban/smhasher/blob/master/crc32_hw.c
83 #define ALIGN_SIZE      0x08UL
84 #define ALIGN_MASK      (ALIGN_SIZE - 1)
85 #define CALC_CRC(op, crc, type, buf, len)                               \
86 do {                                                                  \
87     for (; (len) >= sizeof (type); (len) -= sizeof(type), buf += sizeof (type)) { \
88         (crc) = op((crc), *(type *) (buf));                               \
89     }                                                                   \
90 } while(0)
91 
92 #ifndef __REACTOS__
93 static UINT32 crc32c_hw(const void *input, ULONG len, UINT32 crc) {
94     const char* buf = (const char*)input;
95 
96     // Annoyingly, the CRC32 intrinsics don't work properly in modern versions of MSVC -
97     // it compiles _mm_crc32_u8 as if it was _mm_crc32_u32. And because we're apparently
98     // not allowed to use inline asm on amd64, there's no easy way to fix this!
99 
100     for (; (len > 0) && ((size_t)buf & ALIGN_MASK); len--, buf++) {
101 #ifdef _MSC_VER
102         crc = crctable[(crc ^ *buf) & 0xff] ^ (crc >> 8);
103 #else
104         crc = _mm_crc32_u8(crc, *buf);
105 #endif
106     }
107 
108 #ifdef _AMD64_
109 #ifdef _MSC_VER
110 #pragma warning(push)
111 #pragma warning(disable:4244) // _mm_crc32_u64 wants to return UINT64(!)
112 #pragma warning(disable:4242)
113 #endif
114     CALC_CRC(_mm_crc32_u64, crc, UINT64, buf, len);
115 #ifdef _MSC_VER
116 #pragma warning(pop)
117 #endif
118 #endif
119     CALC_CRC(_mm_crc32_u32, crc, UINT32, buf, len);
120 
121 #ifdef _MSC_VER
122     for (; len > 0; len--, buf++) {
123         crc = crctable[(crc ^ *buf) & 0xff] ^ (crc >> 8);
124     }
125 #else
126     CALC_CRC(_mm_crc32_u16, crc, UINT16, buf, len);
127     CALC_CRC(_mm_crc32_u8, crc, UINT8, buf, len);
128 #endif
129 
130     return crc;
131 }
132 #endif
133 
134 static UINT32 calc_crc32c(UINT32 seed, UINT8* msg, ULONG msglen) {
135 #ifndef __REACTOS__
136     if (have_sse42)
137         return crc32c_hw(msg, msglen, seed);
138     else {
139 #endif
140         UINT32 rem;
141         ULONG i;
142 
143         rem = seed;
144 
145         for (i = 0; i < msglen; i++) {
146             rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8);
147         }
148 
149         return rem;
150 #ifndef __REACTOS__
151     }
152 #endif
153 }
154 
155 BOOL BtrfsRecv::find_tlv(UINT8* data, ULONG datalen, UINT16 type, void** value, ULONG* len) {
156     ULONG off = 0;
157 
158     while (off < datalen) {
159         btrfs_send_tlv* tlv = (btrfs_send_tlv*)(data + off);
160         UINT8* payload = data + off + sizeof(btrfs_send_tlv);
161 
162         if (off + sizeof(btrfs_send_tlv) + tlv->length > datalen) // file is truncated
163             return FALSE;
164 
165         if (tlv->type == type) {
166             *value = payload;
167             *len = tlv->length;
168             return TRUE;
169         }
170 
171         off += sizeof(btrfs_send_tlv) + tlv->length;
172     }
173 
174     return FALSE;
175 }
176 
177 BOOL BtrfsRecv::utf8_to_utf16(HWND hwnd, char* utf8, ULONG utf8len, std::wstring* utf16) {
178     NTSTATUS Status;
179     ULONG utf16len;
180     WCHAR* buf;
181 
182     Status = RtlUTF8ToUnicodeN(NULL, 0, &utf16len, utf8, utf8len);
183     if (!NT_SUCCESS(Status)) {
184         ShowRecvError(IDS_RECV_RTLUTF8TOUNICODEN_FAILED, Status, format_ntstatus(Status).c_str());
185         return FALSE;
186     }
187 
188     buf = (WCHAR*)malloc(utf16len + sizeof(WCHAR));
189 
190     if (!buf) {
191         ShowRecvError(IDS_OUT_OF_MEMORY);
192         return FALSE;
193     }
194 
195     Status = RtlUTF8ToUnicodeN(buf, utf16len, &utf16len, utf8, utf8len);
196     if (!NT_SUCCESS(Status)) {
197         ShowRecvError(IDS_RECV_RTLUTF8TOUNICODEN_FAILED, Status, format_ntstatus(Status).c_str());
198         free(buf);
199         return FALSE;
200     }
201 
202     buf[utf16len / sizeof(WCHAR)] = 0;
203 
204     *utf16 = buf;
205 
206     free(buf);
207 
208     return TRUE;
209 }
210 
211 BOOL BtrfsRecv::cmd_subvol(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
212     char* name;
213     BTRFS_UUID* uuid;
214     UINT64* gen;
215     ULONG namelen, uuidlen, genlen, bcslen;
216     btrfs_create_subvol* bcs;
217     NTSTATUS Status;
218     IO_STATUS_BLOCK iosb;
219     std::wstring nameu;
220 
221     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) {
222         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
223         return FALSE;
224     }
225 
226     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) {
227         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
228         return FALSE;
229     }
230 
231     if (uuidlen < sizeof(BTRFS_UUID)) {
232         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
233         return FALSE;
234     }
235 
236     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) {
237         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"transid");
238         return FALSE;
239     }
240 
241     if (genlen < sizeof(UINT64)) {
242         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(UINT64));
243         return FALSE;
244     }
245 
246     this->subvol_uuid = *uuid;
247     this->stransid = *gen;
248 
249     if (!utf8_to_utf16(hwnd, name, namelen, &nameu))
250         return FALSE;
251 
252     bcslen = offsetof(btrfs_create_subvol, name[0]) + (nameu.length() * sizeof(WCHAR));
253     bcs = (btrfs_create_subvol*)malloc(bcslen);
254 
255     bcs->readonly = TRUE;
256     bcs->posix = TRUE;
257     bcs->namelen = nameu.length() * sizeof(WCHAR);
258     memcpy(bcs->name, nameu.c_str(), bcs->namelen);
259 
260     Status = NtFsControlFile(parent, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, NULL, 0);
261     if (!NT_SUCCESS(Status)) {
262         ShowRecvError(IDS_RECV_CREATE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
263         return FALSE;
264     }
265 
266     subvolpath = dirpath;
267     subvolpath += L"\\";
268     subvolpath += nameu;
269 
270     if (dir != INVALID_HANDLE_VALUE)
271         CloseHandle(dir);
272 
273     if (master != INVALID_HANDLE_VALUE)
274         CloseHandle(master);
275 
276     master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
277                          NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
278     if (master == INVALID_HANDLE_VALUE) {
279         ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
280         return FALSE;
281     }
282 
283     Status = NtFsControlFile(master, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, bcslen, NULL, 0);
284     if (!NT_SUCCESS(Status)) {
285         ShowRecvError(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
286         return FALSE;
287     }
288 
289     dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE,
290                       FILE_SHARE_READ | FILE_SHARE_WRITE,
291                       NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
292     if (dir == INVALID_HANDLE_VALUE) {
293         ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
294         return FALSE;
295     }
296 
297     subvolpath += L"\\";
298 
299     add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
300 
301     num_received++;
302 
303     return TRUE;
304 }
305 
306 void BtrfsRecv::add_cache_entry(BTRFS_UUID* uuid, UINT64 transid, std::wstring path) {
307     subvol_cache sc;
308 
309     sc.uuid = *uuid;
310     sc.transid = transid;
311     sc.path = path;
312 
313     cache.push_back(sc);
314 }
315 
316 BOOL BtrfsRecv::cmd_snapshot(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
317     char* name;
318     BTRFS_UUID *uuid, *parent_uuid;
319     UINT64 *gen, *parent_transid;
320     ULONG namelen, uuidlen, genlen, paruuidlen, partransidlen, bcslen;
321     btrfs_create_snapshot* bcs;
322     NTSTATUS Status;
323     IO_STATUS_BLOCK iosb;
324     std::wstring nameu, parpath;
325     btrfs_find_subvol bfs;
326     WCHAR parpathw[MAX_PATH], volpathw[MAX_PATH];
327     HANDLE subvol;
328 
329     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) {
330         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
331         return FALSE;
332     }
333 
334     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen)) {
335         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
336         return FALSE;
337     }
338 
339     if (uuidlen < sizeof(BTRFS_UUID)) {
340         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
341         return FALSE;
342     }
343 
344     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen)) {
345         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"transid");
346         return FALSE;
347     }
348 
349     if (genlen < sizeof(UINT64)) {
350         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(UINT64));
351         return FALSE;
352     }
353 
354     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&parent_uuid, &paruuidlen)) {
355         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
356         return FALSE;
357     }
358 
359     if (paruuidlen < sizeof(BTRFS_UUID)) {
360         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", paruuidlen, sizeof(BTRFS_UUID));
361         return FALSE;
362     }
363 
364     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&parent_transid, &partransidlen)) {
365         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
366         return FALSE;
367     }
368 
369     if (partransidlen < sizeof(UINT64)) {
370         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", partransidlen, sizeof(UINT64));
371         return FALSE;
372     }
373 
374     this->subvol_uuid = *uuid;
375     this->stransid = *gen;
376 
377     if (!utf8_to_utf16(hwnd, name, namelen, &nameu))
378         return FALSE;
379 
380     bfs.uuid = *parent_uuid;
381     bfs.ctransid = *parent_transid;
382 
383     Status = NtFsControlFile(parent, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol),
384                              parpathw, sizeof(parpathw));
385     if (Status == STATUS_NOT_FOUND) {
386         ShowRecvError(IDS_RECV_CANT_FIND_PARENT_SUBVOL);
387         return FALSE;
388     } else if (!NT_SUCCESS(Status)) {
389         ShowRecvError(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
390         return FALSE;
391     }
392 
393     if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) {
394         ShowRecvError(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
395         return FALSE;
396     }
397 
398     parpath = volpathw;
399     if (parpath.substr(parpath.length() - 1) == L"\\")
400         parpath = parpath.substr(0, parpath.length() - 1);
401 
402     parpath += parpathw;
403 
404     subvol = CreateFileW(parpath.c_str(), FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
405                          NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
406     if (subvol == INVALID_HANDLE_VALUE) {
407         ShowRecvError(IDS_RECV_CANT_OPEN_PATH, parpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
408         return FALSE;
409     }
410 
411     bcslen = offsetof(btrfs_create_snapshot, name[0]) + (nameu.length() * sizeof(WCHAR));
412     bcs = (btrfs_create_snapshot*)malloc(bcslen);
413 
414     bcs->readonly = TRUE;
415     bcs->posix = TRUE;
416     bcs->subvol = subvol;
417     bcs->namelen = nameu.length() * sizeof(WCHAR);
418     memcpy(bcs->name, nameu.c_str(), bcs->namelen);
419 
420     Status = NtFsControlFile(parent, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, bcs, bcslen, NULL, 0);
421     if (!NT_SUCCESS(Status)) {
422         ShowRecvError(IDS_RECV_CREATE_SNAPSHOT_FAILED, Status, format_ntstatus(Status).c_str());
423         return FALSE;
424     }
425 
426     subvolpath = dirpath;
427     subvolpath += L"\\";
428     subvolpath += nameu;
429 
430     if (dir != INVALID_HANDLE_VALUE)
431         CloseHandle(dir);
432 
433     if (master != INVALID_HANDLE_VALUE)
434         CloseHandle(master);
435 
436     master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
437                          NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
438     if (master == INVALID_HANDLE_VALUE) {
439         ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
440         return FALSE;
441     }
442 
443     Status = NtFsControlFile(master, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, bcslen, NULL, 0);
444     if (!NT_SUCCESS(Status)) {
445         ShowRecvError(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
446         return FALSE;
447     }
448 
449     dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE,
450                       FILE_SHARE_READ | FILE_SHARE_WRITE,
451                       NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
452     if (dir == INVALID_HANDLE_VALUE) {
453         ShowRecvError(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
454         return FALSE;
455     }
456 
457     subvolpath += L"\\";
458 
459     add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
460 
461     num_received++;
462 
463     return TRUE;
464 }
465 
466 BOOL BtrfsRecv::cmd_mkfile(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
467     char *name, *pathlink;
468     UINT64 *inode, *rdev = NULL, *mode = NULL;
469     ULONG namelen, inodelen, bmnsize;
470     NTSTATUS Status;
471     IO_STATUS_BLOCK iosb;
472     btrfs_mknod* bmn;
473     std::wstring nameu, pathlinku;
474 
475     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&name, &namelen)) {
476         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
477         return FALSE;
478     }
479 
480     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_INODE, (void**)&inode, &inodelen)) {
481         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"inode");
482         return FALSE;
483     }
484 
485     if (inodelen < sizeof(UINT64)) {
486         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"inode", inodelen, sizeof(UINT64));
487         return FALSE;
488     }
489 
490     if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) {
491         ULONG rdevlen, modelen;
492 
493         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_RDEV, (void**)&rdev, &rdevlen)) {
494             ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"rdev");
495             return FALSE;
496         }
497 
498         if (rdevlen < sizeof(UINT64)) {
499             ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"rdev", rdev, sizeof(UINT64));
500             return FALSE;
501         }
502 
503         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
504             ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"mode");
505             return FALSE;
506         }
507 
508         if (modelen < sizeof(UINT64)) {
509             ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(UINT64));
510             return FALSE;
511         }
512     } else if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) {
513         ULONG pathlinklen;
514 
515         if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&pathlink, &pathlinklen)) {
516             ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
517             return FALSE;
518         }
519 
520         if (!utf8_to_utf16(hwnd, pathlink, pathlinklen, &pathlinku))
521             return FALSE;
522     }
523 
524     if (!utf8_to_utf16(hwnd, name, namelen, &nameu))
525         return FALSE;
526 
527     bmnsize = sizeof(btrfs_mknod) - sizeof(WCHAR) + (nameu.length() * sizeof(WCHAR));
528     bmn = (btrfs_mknod*)malloc(bmnsize);
529 
530     bmn->inode = *inode;
531 
532     if (cmd->cmd == BTRFS_SEND_CMD_MKDIR)
533         bmn->type = BTRFS_TYPE_DIRECTORY;
534     else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD)
535         bmn->type = *mode & S_IFCHR ? BTRFS_TYPE_CHARDEV : BTRFS_TYPE_BLOCKDEV;
536     else if (cmd->cmd == BTRFS_SEND_CMD_MKFIFO)
537         bmn->type = BTRFS_TYPE_FIFO;
538     else if (cmd->cmd == BTRFS_SEND_CMD_MKSOCK)
539         bmn->type = BTRFS_TYPE_SOCKET;
540     else
541         bmn->type = BTRFS_TYPE_FILE;
542 
543     bmn->st_rdev = rdev ? *rdev : 0;
544     bmn->namelen = nameu.length() * sizeof(WCHAR);
545     memcpy(bmn->name, nameu.c_str(), bmn->namelen);
546 
547     Status = NtFsControlFile(dir, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_MKNOD, bmn, bmnsize, NULL, 0);
548     if (!NT_SUCCESS(Status)) {
549         ShowRecvError(IDS_RECV_MKNOD_FAILED, Status, format_ntstatus(Status).c_str());
550         free(bmn);
551         return FALSE;
552     }
553 
554     free(bmn);
555 
556     if (cmd->cmd == BTRFS_SEND_CMD_SYMLINK) {
557         HANDLE h;
558         REPARSE_DATA_BUFFER* rdb;
559         ULONG rdblen;
560         btrfs_set_inode_info bsii;
561 
562         rdblen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]) + (2 * pathlinku.length() * sizeof(WCHAR));
563 
564         rdb = (REPARSE_DATA_BUFFER*)malloc(rdblen);
565 
566         rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
567         rdb->ReparseDataLength = rdblen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer);
568         rdb->Reserved = 0;
569         rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
570         rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = pathlinku.length() * sizeof(WCHAR);
571         rdb->SymbolicLinkReparseBuffer.PrintNameOffset = pathlinku.length() * sizeof(WCHAR);
572         rdb->SymbolicLinkReparseBuffer.PrintNameLength = pathlinku.length() * sizeof(WCHAR);
573         rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
574 
575         memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer, pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
576         memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer + (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)),
577                 pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.PrintNameLength);
578 
579         h = CreateFileW((subvolpath + nameu).c_str(), GENERIC_WRITE | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
580                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
581         if (h == INVALID_HANDLE_VALUE) {
582             ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
583             free(rdb);
584             return FALSE;
585         }
586 
587         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_SET_REPARSE_POINT, rdb, rdblen, NULL, 0);
588         if (!NT_SUCCESS(Status)) {
589             ShowRecvError(IDS_RECV_SET_REPARSE_POINT_FAILED, Status, format_ntstatus(Status).c_str());
590             free(rdb);
591             CloseHandle(h);
592             return FALSE;
593         }
594 
595         free(rdb);
596 
597         memset(&bsii, 0, sizeof(btrfs_set_inode_info));
598 
599         bsii.mode_changed = TRUE;
600         bsii.st_mode = 0777;
601 
602         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
603         if (!NT_SUCCESS(Status)) {
604             ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
605             CloseHandle(h);
606             return FALSE;
607         }
608 
609         CloseHandle(h);
610     } else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) {
611         UINT64* mode;
612         ULONG modelen;
613 
614         if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
615             HANDLE h;
616             btrfs_set_inode_info bsii;
617 
618             if (modelen < sizeof(UINT64)) {
619                 ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(UINT64));
620                 return FALSE;
621             }
622 
623             h = CreateFileW((subvolpath + nameu).c_str(), WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
624                             NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
625             if (h == INVALID_HANDLE_VALUE) {
626                 ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
627                 return FALSE;
628             }
629 
630             memset(&bsii, 0, sizeof(btrfs_set_inode_info));
631 
632             bsii.mode_changed = TRUE;
633             bsii.st_mode = *mode;
634 
635             Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
636             if (!NT_SUCCESS(Status)) {
637                 ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
638                 CloseHandle(h);
639                 return FALSE;
640             }
641 
642             CloseHandle(h);
643         }
644     }
645 
646     return TRUE;
647 }
648 
649 BOOL BtrfsRecv::cmd_rename(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
650     char *path, *path_to;
651     ULONG path_len, path_to_len;
652     std::wstring pathu, path_tou;
653 
654     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) {
655         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
656         return FALSE;
657     }
658 
659     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_TO, (void**)&path_to, &path_to_len)) {
660         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path_to");
661         return FALSE;
662     }
663 
664     if (!utf8_to_utf16(hwnd, path, path_len, &pathu))
665         return FALSE;
666 
667     if (!utf8_to_utf16(hwnd, path_to, path_to_len, &path_tou))
668         return FALSE;
669 
670     if (!MoveFileW((subvolpath + pathu).c_str(), (subvolpath + path_tou).c_str())) {
671         ShowRecvError(IDS_RECV_MOVEFILE_FAILED, pathu.c_str(), path_tou.c_str(), GetLastError(), format_message(GetLastError()).c_str());
672         return FALSE;
673     }
674 
675     return TRUE;
676 }
677 
678 BOOL BtrfsRecv::cmd_link(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
679     char *path, *path_link;
680     ULONG path_len, path_link_len;
681     std::wstring pathu, path_linku;
682 
683     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len)) {
684         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
685         return FALSE;
686     }
687 
688     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&path_link, &path_link_len)) {
689         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
690         return FALSE;
691     }
692 
693     if (!utf8_to_utf16(hwnd, path, path_len, &pathu))
694         return FALSE;
695 
696     if (!utf8_to_utf16(hwnd, path_link, path_link_len, &path_linku))
697         return FALSE;
698 
699     if (!CreateHardLinkW((subvolpath + pathu).c_str(), (subvolpath + path_linku).c_str(), NULL)) {
700         ShowRecvError(IDS_RECV_CREATEHARDLINK_FAILED, pathu.c_str(), path_linku.c_str(), GetLastError(), format_message(GetLastError()).c_str());
701         return FALSE;
702     }
703 
704     return TRUE;
705 }
706 
707 BOOL BtrfsRecv::cmd_unlink(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
708     char* path;
709     ULONG pathlen;
710     std::wstring pathu;
711     ULONG att;
712 
713     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
714         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
715         return FALSE;
716     }
717 
718     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
719         return FALSE;
720 
721     att = GetFileAttributesW((subvolpath + pathu).c_str());
722     if (att == INVALID_FILE_ATTRIBUTES) {
723         ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
724         return FALSE;
725     }
726 
727     if (att & FILE_ATTRIBUTE_READONLY) {
728         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
729             ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
730             return FALSE;
731         }
732     }
733 
734     if (!DeleteFileW((subvolpath + pathu).c_str())) {
735         ShowRecvError(IDS_RECV_DELETEFILE_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
736         return FALSE;
737     }
738 
739     return TRUE;
740 }
741 
742 BOOL BtrfsRecv::cmd_rmdir(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
743     char* path;
744     ULONG pathlen;
745     std::wstring pathu;
746     ULONG att;
747 
748     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
749         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
750         return FALSE;
751     }
752 
753     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
754         return FALSE;
755 
756     att = GetFileAttributesW((subvolpath + pathu).c_str());
757     if (att == INVALID_FILE_ATTRIBUTES) {
758         ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
759         return FALSE;
760     }
761 
762     if (att & FILE_ATTRIBUTE_READONLY) {
763         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
764             ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
765             return FALSE;
766         }
767     }
768 
769     if (!RemoveDirectoryW((subvolpath + pathu).c_str())) {
770         ShowRecvError(IDS_RECV_REMOVEDIRECTORY_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
771         return FALSE;
772     }
773 
774     return TRUE;
775 }
776 
777 BOOL BtrfsRecv::cmd_setxattr(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
778     char *path, *xattrname;
779     UINT8* xattrdata;
780     ULONG pathlen, xattrnamelen, xattrdatalen;
781     std::wstring pathu;
782 
783     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
784         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
785         return FALSE;
786     }
787 
788     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrname, &xattrnamelen)) {
789         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
790         return FALSE;
791     }
792 
793     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_DATA, (void**)&xattrdata, &xattrdatalen)) {
794         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"xattr_data");
795         return FALSE;
796     }
797 
798     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
799         return FALSE;
800 
801     if (xattrnamelen > strlen(XATTR_USER) && !memcmp(xattrname, XATTR_USER, strlen(XATTR_USER)) &&
802         (xattrnamelen != strlen(EA_DOSATTRIB) || memcmp(xattrname, EA_DOSATTRIB, xattrnamelen)) &&
803         (xattrnamelen != strlen(EA_EA) || memcmp(xattrname, EA_EA, xattrnamelen)) &&
804         (xattrnamelen != strlen(EA_REPARSE) || memcmp(xattrname, EA_REPARSE, xattrnamelen))) {
805         HANDLE h;
806         std::wstring streamname;
807         ULONG att;
808 
809         if (!utf8_to_utf16(hwnd, xattrname, xattrnamelen, &streamname))
810             return FALSE;
811 
812         att = GetFileAttributesW((subvolpath + pathu).c_str());
813         if (att == INVALID_FILE_ATTRIBUTES) {
814             ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
815             return FALSE;
816         }
817 
818         if (att & FILE_ATTRIBUTE_READONLY) {
819             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
820                 ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
821                 return FALSE;
822             }
823         }
824 
825         streamname = streamname.substr(strlen(XATTR_USER));
826 
827         h = CreateFileW((subvolpath + pathu + L":" + streamname).c_str(), GENERIC_WRITE, 0,
828                         NULL, CREATE_ALWAYS, FILE_FLAG_POSIX_SEMANTICS, NULL);
829         if (h == INVALID_HANDLE_VALUE) {
830             ShowRecvError(IDS_RECV_CANT_CREATE_FILE, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
831             return FALSE;
832         }
833 
834         if (xattrdatalen > 0) {
835             if (!WriteFile(h, xattrdata, xattrdatalen, NULL, NULL)) {
836                 ShowRecvError(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
837                 CloseHandle(h);
838                 return FALSE;
839             }
840         }
841 
842         CloseHandle(h);
843 
844         if (att & FILE_ATTRIBUTE_READONLY) {
845             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) {
846                 ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
847                 return FALSE;
848             }
849         }
850     } else {
851         HANDLE h;
852         IO_STATUS_BLOCK iosb;
853         NTSTATUS Status;
854         ULONG bsxalen, perms = FILE_WRITE_ATTRIBUTES;
855         btrfs_set_xattr* bsxa;
856 
857         if (xattrnamelen == strlen(EA_NTACL) && !memcmp(xattrname, EA_NTACL, xattrnamelen))
858             perms |= WRITE_DAC | WRITE_OWNER;
859         else if (xattrnamelen == strlen(EA_EA) && !memcmp(xattrname, EA_EA, xattrnamelen))
860             perms |= FILE_WRITE_EA;
861 
862         h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
863                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);
864         if (h == INVALID_HANDLE_VALUE) {
865             ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
866             return FALSE;
867         }
868 
869         bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrnamelen + xattrdatalen;
870         bsxa = (btrfs_set_xattr*)malloc(bsxalen);
871         if (!bsxa) {
872             ShowRecvError(IDS_OUT_OF_MEMORY);
873             CloseHandle(h);
874             return FALSE;
875         }
876 
877         bsxa->namelen = xattrnamelen;
878         bsxa->valuelen = xattrdatalen;
879         memcpy(bsxa->data, xattrname, xattrnamelen);
880         memcpy(&bsxa->data[xattrnamelen], xattrdata, xattrdatalen);
881 
882         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, bsxalen, NULL, 0);
883         if (!NT_SUCCESS(Status)) {
884             ShowRecvError(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
885             free(bsxa);
886             CloseHandle(h);
887             return FALSE;
888         }
889 
890         free(bsxa);
891         CloseHandle(h);
892     }
893 
894     return TRUE;
895 }
896 
897 BOOL BtrfsRecv::cmd_removexattr(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
898     char *path, *xattrname;
899     ULONG pathlen, xattrnamelen;
900     std::wstring pathu;
901 
902     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
903         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
904         return FALSE;
905     }
906 
907     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrname, &xattrnamelen)) {
908         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
909         return FALSE;
910     }
911 
912     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
913         return FALSE;
914 
915     if (xattrnamelen > strlen(XATTR_USER) && !memcmp(xattrname, XATTR_USER, strlen(XATTR_USER)) &&
916         (xattrnamelen != strlen(EA_DOSATTRIB) || memcmp(xattrname, EA_DOSATTRIB, xattrnamelen)) &&
917         (xattrnamelen != strlen(EA_EA) || memcmp(xattrname, EA_EA, xattrnamelen))) { // deleting stream
918         ULONG att;
919         std::wstring streamname;
920 
921         if (!utf8_to_utf16(hwnd, xattrname, xattrnamelen, &streamname))
922             return FALSE;
923 
924         streamname = streamname.substr(strlen(XATTR_USER));
925 
926         att = GetFileAttributesW((subvolpath + pathu).c_str());
927         if (att == INVALID_FILE_ATTRIBUTES) {
928             ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
929             return FALSE;
930         }
931 
932         if (att & FILE_ATTRIBUTE_READONLY) {
933             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
934                 ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
935                 return FALSE;
936             }
937         }
938 
939         if (!DeleteFileW((subvolpath + pathu + L":" + streamname).c_str())) {
940             ShowRecvError(IDS_RECV_DELETEFILE_FAILED, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
941             return FALSE;
942         }
943 
944         if (att & FILE_ATTRIBUTE_READONLY) {
945             if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) {
946                 ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
947                 return FALSE;
948             }
949         }
950     } else {
951         HANDLE h;
952         IO_STATUS_BLOCK iosb;
953         NTSTATUS Status;
954         ULONG bsxalen, perms = FILE_WRITE_ATTRIBUTES;
955         btrfs_set_xattr* bsxa;
956 
957         if (xattrnamelen == strlen(EA_NTACL) && !memcmp(xattrname, EA_NTACL, xattrnamelen))
958             perms |= WRITE_DAC | WRITE_OWNER;
959         else if (xattrnamelen == strlen(EA_EA) && !memcmp(xattrname, EA_EA, xattrnamelen))
960             perms |= FILE_WRITE_EA;
961 
962         h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
963                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);
964         if (h == INVALID_HANDLE_VALUE) {
965             ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
966             return FALSE;
967         }
968 
969         bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrnamelen;
970         bsxa = (btrfs_set_xattr*)malloc(bsxalen);
971         if (!bsxa) {
972             ShowRecvError(IDS_OUT_OF_MEMORY);
973             CloseHandle(h);
974             return FALSE;
975         }
976 
977         bsxa->namelen = xattrnamelen;
978         bsxa->valuelen = 0;
979         memcpy(bsxa->data, xattrname, xattrnamelen);
980 
981         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, bsxalen, NULL, 0);
982         if (!NT_SUCCESS(Status)) {
983             ShowRecvError(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
984             free(bsxa);
985             CloseHandle(h);
986             return FALSE;
987         }
988 
989         free(bsxa);
990         CloseHandle(h);
991     }
992 
993     return TRUE;
994 }
995 
996 BOOL BtrfsRecv::cmd_write(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
997     char* path;
998     UINT64* offset;
999     UINT8* writedata;
1000     ULONG pathlen, offsetlen, datalen;
1001     std::wstring pathu;
1002     HANDLE h;
1003     LARGE_INTEGER offli;
1004 
1005     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
1006         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
1007         return FALSE;
1008     }
1009 
1010     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) {
1011         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"offset");
1012         return FALSE;
1013     }
1014 
1015     if (offsetlen < sizeof(UINT64)) {
1016         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(UINT64));
1017         return FALSE;
1018     }
1019 
1020     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_DATA, (void**)&writedata, &datalen)) {
1021         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"data");
1022         return FALSE;
1023     }
1024 
1025     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
1026         return FALSE;
1027 
1028     if (lastwritepath != pathu) {
1029         FILE_BASIC_INFO fbi;
1030 
1031         if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1032             if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) {
1033                 ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1034                 return FALSE;
1035             }
1036         }
1037 
1038         CloseHandle(lastwritefile);
1039 
1040         lastwriteatt = GetFileAttributesW((subvolpath + pathu).c_str());
1041         if (lastwriteatt == INVALID_FILE_ATTRIBUTES) {
1042             ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1043             return FALSE;
1044         }
1045 
1046         if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1047             if (!SetFileAttributesW((subvolpath + pathu).c_str(), lastwriteatt & ~FILE_ATTRIBUTE_READONLY)) {
1048                 ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1049                 return FALSE;
1050             }
1051         }
1052 
1053         h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
1054                         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
1055         if (h == INVALID_HANDLE_VALUE) {
1056             ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1057             return FALSE;
1058         }
1059 
1060         lastwritepath = pathu;
1061         lastwritefile = h;
1062 
1063         memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
1064 
1065         fbi.LastWriteTime.QuadPart = -1;
1066 
1067         if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) {
1068             ShowRecvError(IDS_RECV_SETFILEINFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1069             return FALSE;
1070         }
1071     } else
1072         h = lastwritefile;
1073 
1074     offli.QuadPart = *offset;
1075 
1076     if (SetFilePointer(h, offli.LowPart, &offli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
1077         ShowRecvError(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1078         return FALSE;
1079     }
1080 
1081     if (!WriteFile(h, writedata, datalen, NULL, NULL)) {
1082         ShowRecvError(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1083         return FALSE;
1084     }
1085 
1086     return TRUE;
1087 }
1088 
1089 BOOL BtrfsRecv::cmd_clone(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
1090     char *path, *clonepath;
1091     UINT64 *offset, *cloneoffset, *clonetransid, *clonelen;
1092     BTRFS_UUID* cloneuuid;
1093     ULONG i, offsetlen, pathlen, clonepathlen, cloneoffsetlen, cloneuuidlen, clonetransidlen, clonelenlen;
1094     std::wstring pathu, clonepathu, clonepar;
1095     btrfs_find_subvol bfs;
1096     NTSTATUS Status;
1097     IO_STATUS_BLOCK iosb;
1098     WCHAR cloneparw[MAX_PATH];
1099     HANDLE src, dest;
1100     DUPLICATE_EXTENTS_DATA ded;
1101     LARGE_INTEGER filesize;
1102     BOOL found = FALSE;
1103 
1104     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen)) {
1105         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"offset");
1106         return FALSE;
1107     }
1108 
1109     if (offsetlen < sizeof(UINT64)) {
1110         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(UINT64));
1111         return FALSE;
1112     }
1113 
1114     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_LENGTH, (void**)&clonelen, &clonelenlen)) {
1115         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_len");
1116         return FALSE;
1117     }
1118 
1119     if (clonelenlen < sizeof(UINT64)) {
1120         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_len", clonelenlen, sizeof(UINT64));
1121         return FALSE;
1122     }
1123 
1124     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
1125         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
1126         return FALSE;
1127     }
1128 
1129     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
1130         return FALSE;
1131 
1132     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_UUID, (void**)&cloneuuid, &cloneuuidlen)) {
1133         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_uuid");
1134         return FALSE;
1135     }
1136 
1137     if (cloneuuidlen < sizeof(BTRFS_UUID)) {
1138         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_uuid", cloneuuidlen, sizeof(BTRFS_UUID));
1139         return FALSE;
1140     }
1141 
1142     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_CTRANSID, (void**)&clonetransid, &clonetransidlen)) {
1143         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_ctransid");
1144         return FALSE;
1145     }
1146 
1147     if (clonetransidlen < sizeof(UINT64)) {
1148         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_ctransid", clonetransidlen, sizeof(UINT64));
1149         return FALSE;
1150     }
1151 
1152     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_PATH, (void**)&clonepath, &clonepathlen)) {
1153         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_path");
1154         return FALSE;
1155     }
1156 
1157     if (!utf8_to_utf16(hwnd, clonepath, clonepathlen, &clonepathu))
1158         return FALSE;
1159 
1160     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_CLONE_OFFSET, (void**)&cloneoffset, &cloneoffsetlen)) {
1161         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"clone_offset");
1162         return FALSE;
1163     }
1164 
1165     if (cloneoffsetlen < sizeof(UINT64)) {
1166         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"clone_offset", cloneoffsetlen, sizeof(UINT64));
1167         return FALSE;
1168     }
1169 
1170     for (i = 0; i < cache.size(); i++) {
1171         if (!memcmp(cloneuuid, &cache[i].uuid, sizeof(BTRFS_UUID)) && *clonetransid == cache[i].transid) {
1172             clonepar = cache[i].path;
1173             found = TRUE;
1174             break;
1175         }
1176     }
1177 
1178     if (!found) {
1179         WCHAR volpathw[MAX_PATH];
1180 
1181         bfs.uuid = *cloneuuid;
1182         bfs.ctransid = *clonetransid;
1183 
1184         Status = NtFsControlFile(dir, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_FIND_SUBVOL, &bfs, sizeof(btrfs_find_subvol),
1185                                  cloneparw, sizeof(cloneparw));
1186         if (Status == STATUS_NOT_FOUND) {
1187             ShowRecvError(IDS_RECV_CANT_FIND_CLONE_SUBVOL);
1188             return FALSE;
1189         } else if (!NT_SUCCESS(Status)) {
1190             ShowRecvError(IDS_RECV_FIND_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
1191             return FALSE;
1192         }
1193 
1194         if (!GetVolumePathNameW(dirpath.c_str(), volpathw, (sizeof(volpathw) / sizeof(WCHAR)) - 1)) {
1195             ShowRecvError(IDS_RECV_GETVOLUMEPATHNAME_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1196             return FALSE;
1197         }
1198 
1199         clonepar = volpathw;
1200         if (clonepar.substr(clonepar.length() - 1) == L"\\")
1201             clonepar = clonepar.substr(0, clonepar.length() - 1);
1202 
1203         clonepar += cloneparw;
1204         clonepar += L"\\";
1205 
1206         add_cache_entry(cloneuuid, *clonetransid, clonepar);
1207     }
1208 
1209     src = CreateFileW((clonepar + clonepathu).c_str(), FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1210                       NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
1211     if (src == INVALID_HANDLE_VALUE) {
1212         ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, (clonepar + clonepathu).c_str(), GetLastError(), format_message(GetLastError()).c_str());
1213         return FALSE;
1214     }
1215 
1216     dest = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1217                        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
1218     if (dest == INVALID_HANDLE_VALUE) {
1219         ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1220         CloseHandle(src);
1221         return FALSE;
1222     }
1223 
1224     if (!GetFileSizeEx(dest, &filesize)) {
1225         ShowRecvError(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1226         CloseHandle(dest);
1227         CloseHandle(src);
1228         return FALSE;
1229     }
1230 
1231     if ((UINT64)filesize.QuadPart < *offset + *clonelen) {
1232         LARGE_INTEGER sizeli;
1233 
1234         sizeli.QuadPart = *offset + *clonelen;
1235 
1236         if (SetFilePointer(dest, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
1237             ShowRecvError(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1238             CloseHandle(dest);
1239             CloseHandle(src);
1240             return FALSE;
1241         }
1242 
1243         if (!SetEndOfFile(dest)) {
1244             ShowRecvError(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1245             CloseHandle(dest);
1246             CloseHandle(src);
1247             return FALSE;
1248         }
1249     }
1250 
1251     ded.FileHandle = src;
1252     ded.SourceFileOffset.QuadPart = *cloneoffset;
1253     ded.TargetFileOffset.QuadPart = *offset;
1254     ded.ByteCount.QuadPart = *clonelen;
1255 
1256     Status = NtFsControlFile(dest, NULL, NULL, NULL, &iosb, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &ded, sizeof(DUPLICATE_EXTENTS_DATA),
1257                              NULL, 0);
1258     if (!NT_SUCCESS(Status)) {
1259         ShowRecvError(IDS_RECV_DUPLICATE_EXTENTS_FAILED, Status, format_ntstatus(Status).c_str());
1260         CloseHandle(dest);
1261         CloseHandle(src);
1262         return FALSE;
1263     }
1264 
1265     CloseHandle(dest);
1266     CloseHandle(src);
1267 
1268     return TRUE;
1269 }
1270 
1271 BOOL BtrfsRecv::cmd_truncate(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
1272     char* path;
1273     UINT64* size;
1274     ULONG pathlen, sizelen;
1275     std::wstring pathu;
1276     HANDLE h;
1277     LARGE_INTEGER sizeli;
1278     DWORD att;
1279 
1280     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
1281         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
1282         return FALSE;
1283     }
1284 
1285     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_SIZE, (void**)&size, &sizelen)) {
1286         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"size");
1287         return FALSE;
1288     }
1289 
1290     if (sizelen < sizeof(UINT64)) {
1291         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"size", sizelen, sizeof(UINT64));
1292         return FALSE;
1293     }
1294 
1295     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
1296         return FALSE;
1297 
1298     att = GetFileAttributesW((subvolpath + pathu).c_str());
1299     if (att == INVALID_FILE_ATTRIBUTES) {
1300         ShowRecvError(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1301         return FALSE;
1302     }
1303 
1304     if (att & FILE_ATTRIBUTE_READONLY) {
1305         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY)) {
1306             ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1307             return FALSE;
1308         }
1309     }
1310 
1311     h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA, 0, NULL, OPEN_EXISTING,
1312                     FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
1313     if (h == INVALID_HANDLE_VALUE) {
1314         ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1315         return FALSE;
1316     }
1317 
1318     sizeli.QuadPart = *size;
1319 
1320     if (SetFilePointer(h, sizeli.LowPart, &sizeli.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
1321         ShowRecvError(IDS_RECV_SETFILEPOINTER_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1322         CloseHandle(h);
1323         return FALSE;
1324     }
1325 
1326     if (!SetEndOfFile(h)) {
1327         ShowRecvError(IDS_RECV_SETENDOFFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1328         CloseHandle(h);
1329         return FALSE;
1330     }
1331 
1332     CloseHandle(h);
1333 
1334     if (att & FILE_ATTRIBUTE_READONLY) {
1335         if (!SetFileAttributesW((subvolpath + pathu).c_str(), att)) {
1336             ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1337             return FALSE;
1338         }
1339     }
1340 
1341     return TRUE;
1342 }
1343 
1344 BOOL BtrfsRecv::cmd_chmod(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
1345     HANDLE h;
1346     char* path;
1347     UINT32* mode;
1348     ULONG pathlen, modelen;
1349     std::wstring pathu;
1350     btrfs_set_inode_info bsii;
1351     NTSTATUS Status;
1352     IO_STATUS_BLOCK iosb;
1353 
1354     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
1355         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
1356         return FALSE;
1357     }
1358 
1359     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
1360         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"mode");
1361         return FALSE;
1362     }
1363 
1364     if (modelen < sizeof(UINT32)) {
1365         ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(UINT32));
1366         return FALSE;
1367     }
1368 
1369     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
1370         return FALSE;
1371 
1372     h = CreateFileW((subvolpath + pathu).c_str(), WRITE_DAC, 0, NULL, OPEN_EXISTING,
1373                     FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
1374     if (h == INVALID_HANDLE_VALUE) {
1375         ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1376         return FALSE;
1377     }
1378 
1379     memset(&bsii, 0, sizeof(btrfs_set_inode_info));
1380 
1381     bsii.mode_changed = TRUE;
1382     bsii.st_mode = *mode;
1383 
1384     Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
1385     if (!NT_SUCCESS(Status)) {
1386         ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
1387         CloseHandle(h);
1388         return FALSE;
1389     }
1390 
1391     CloseHandle(h);
1392 
1393     return TRUE;
1394 }
1395 
1396 BOOL BtrfsRecv::cmd_chown(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
1397     HANDLE h;
1398     char* path;
1399     UINT32 *uid, *gid;
1400     ULONG pathlen, uidlen, gidlen;
1401     std::wstring pathu;
1402     btrfs_set_inode_info bsii;
1403 
1404     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
1405         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
1406         return FALSE;
1407     }
1408 
1409     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
1410         return FALSE;
1411 
1412     h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES | WRITE_OWNER | WRITE_DAC, 0, NULL, OPEN_EXISTING,
1413                     FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
1414     if (h == INVALID_HANDLE_VALUE) {
1415         ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1416         return FALSE;
1417     }
1418 
1419     memset(&bsii, 0, sizeof(btrfs_set_inode_info));
1420 
1421     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_UID, (void**)&uid, &uidlen)) {
1422         if (uidlen < sizeof(UINT32)) {
1423             ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"uid", uidlen, sizeof(UINT32));
1424             CloseHandle(h);
1425             return FALSE;
1426         }
1427 
1428         bsii.uid_changed = TRUE;
1429         bsii.st_uid = *uid;
1430     }
1431 
1432     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_GID, (void**)&gid, &gidlen)) {
1433         if (gidlen < sizeof(UINT32)) {
1434             ShowRecvError(IDS_RECV_SHORT_PARAM, funcname, L"gid", gidlen, sizeof(UINT32));
1435             CloseHandle(h);
1436             return FALSE;
1437         }
1438 
1439         bsii.gid_changed = TRUE;
1440         bsii.st_gid = *gid;
1441     }
1442 
1443     if (bsii.uid_changed || bsii.gid_changed) {
1444         NTSTATUS Status;
1445         IO_STATUS_BLOCK iosb;
1446 
1447         Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), NULL, 0);
1448         if (!NT_SUCCESS(Status)) {
1449             ShowRecvError(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
1450             CloseHandle(h);
1451             return FALSE;
1452         }
1453     }
1454 
1455     CloseHandle(h);
1456 
1457     return TRUE;
1458 }
1459 
1460 static __inline UINT64 unix_time_to_win(BTRFS_TIME* t) {
1461     return (t->seconds * 10000000) + (t->nanoseconds / 100) + 116444736000000000;
1462 }
1463 
1464 BOOL BtrfsRecv::cmd_utimes(HWND hwnd, btrfs_send_command* cmd, UINT8* data) {
1465     char* path;
1466     ULONG pathlen;
1467     std::wstring pathu;
1468     HANDLE h;
1469     FILE_BASIC_INFO fbi;
1470     BTRFS_TIME* time;
1471     ULONG timelen;
1472 
1473     if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen)) {
1474         ShowRecvError(IDS_RECV_MISSING_PARAM, funcname, L"path");
1475         return FALSE;
1476     }
1477 
1478     if (!utf8_to_utf16(hwnd, path, pathlen, &pathu))
1479         return FALSE;
1480 
1481     h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
1482                     FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, NULL);
1483     if (h == INVALID_HANDLE_VALUE) {
1484         ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1485         return FALSE;
1486     }
1487 
1488     memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
1489 
1490     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_OTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1491         fbi.CreationTime.QuadPart = unix_time_to_win(time);
1492 
1493     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_ATIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1494         fbi.LastAccessTime.QuadPart = unix_time_to_win(time);
1495 
1496     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1497         fbi.LastWriteTime.QuadPart = unix_time_to_win(time);
1498 
1499     if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_CTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1500         fbi.ChangeTime.QuadPart = unix_time_to_win(time);
1501 
1502     if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO))) {
1503         ShowRecvError(IDS_RECV_SETFILEINFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1504         CloseHandle(h);
1505         return FALSE;
1506     }
1507 
1508     CloseHandle(h);
1509 
1510     return TRUE;
1511 }
1512 
1513 void BtrfsRecv::ShowRecvError(int resid, ...) {
1514     WCHAR s[1024], t[1024];
1515     va_list ap;
1516 
1517     if (!hwnd)
1518         return;
1519 
1520     if (!LoadStringW(module, resid, s, sizeof(s) / sizeof(WCHAR))) {
1521         ShowError(hwnd, GetLastError());
1522         return;
1523     }
1524 
1525     va_start(ap, resid);
1526 #ifndef __REACTOS__
1527     vswprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
1528 #else
1529     vsnwprintf(t, sizeof(t) / sizeof(WCHAR), s, ap);
1530 #endif
1531 
1532     SetDlgItemTextW(hwnd, IDC_RECV_MSG, t);
1533 
1534     va_end(ap);
1535 
1536     SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0);
1537 }
1538 
1539 static void delete_directory(std::wstring dir) {
1540     HANDLE h;
1541     WIN32_FIND_DATAW fff;
1542 
1543     h = FindFirstFileW((dir + L"*").c_str(), &fff);
1544 
1545     if (h == INVALID_HANDLE_VALUE)
1546         return;
1547 
1548     do {
1549         std::wstring file;
1550 
1551         file = fff.cFileName;
1552 
1553         if (file != L"." && file != L"..") {
1554             if (fff.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1555                 SetFileAttributesW((dir + file).c_str(), fff.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
1556 
1557             if (fff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1558                 if (!(fff.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
1559                     delete_directory(dir + file + L"\\");
1560                 else
1561                     RemoveDirectoryW((dir + file).c_str());
1562             } else
1563                 DeleteFileW((dir + file).c_str());
1564         }
1565     } while (FindNextFileW(h, &fff));
1566 
1567     FindClose(h);
1568 
1569     RemoveDirectoryW(dir.c_str());
1570 }
1571 
1572 static BOOL check_csum(btrfs_send_command* cmd, UINT8* data) {
1573     UINT32 crc32 = cmd->csum, calc;
1574 
1575     cmd->csum = 0;
1576 
1577     calc = calc_crc32c(0, (UINT8*)cmd, sizeof(btrfs_send_command));
1578 
1579     if (cmd->length > 0)
1580         calc = calc_crc32c(calc, data, cmd->length);
1581 
1582     return calc == crc32 ? TRUE : FALSE;
1583 }
1584 
1585 BOOL BtrfsRecv::do_recv(HANDLE f, UINT64* pos, UINT64 size) {
1586     btrfs_send_header header;
1587     BOOL b = TRUE, ended = FALSE;
1588 
1589     if (!ReadFile(f, &header, sizeof(btrfs_send_header), NULL, NULL)) {
1590         ShowRecvError(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1591         return FALSE;
1592     }
1593 
1594     *pos += sizeof(btrfs_send_header);
1595 
1596     if (memcmp(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic))) {
1597         ShowRecvError(IDS_RECV_NOT_A_SEND_STREAM);
1598         return FALSE;
1599     }
1600 
1601     if (header.version > 1) {
1602         ShowRecvError(IDS_RECV_UNSUPPORTED_VERSION, header.version);
1603         return FALSE;
1604     }
1605 
1606     SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)65536);
1607 
1608     lastwritefile = INVALID_HANDLE_VALUE;
1609     lastwritepath = L"";
1610     lastwriteatt = 0;
1611 
1612     while (TRUE) {
1613         btrfs_send_command cmd;
1614         UINT8* data;
1615         ULONG progress;
1616 
1617         if (cancelling) {
1618             b = FALSE;
1619             break;
1620         }
1621 
1622         progress = (ULONG)((float)*pos * 65536.0f / (float)size);
1623         SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, progress, 0);
1624 
1625         if (!ReadFile(f, &cmd, sizeof(btrfs_send_command), NULL, NULL)) {
1626             if (GetLastError() != ERROR_HANDLE_EOF) {
1627                 ShowRecvError(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1628                 break;
1629             }
1630 
1631             break;
1632         }
1633 
1634         *pos += sizeof(btrfs_send_command);
1635 
1636         if (cmd.length > 0) {
1637             if (*pos + cmd.length > size) {
1638                 ShowRecvError(IDS_RECV_FILE_TRUNCATED);
1639                 b = FALSE;
1640                 break;
1641             }
1642 
1643             data = (UINT8*)malloc(cmd.length);
1644             if (!data) {
1645                 ShowRecvError(IDS_OUT_OF_MEMORY);
1646                 b = FALSE;
1647                 break;
1648             }
1649 
1650             if (!ReadFile(f, data, cmd.length, NULL, NULL)) {
1651                 ShowRecvError(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1652                 free(data);
1653                 b = FALSE;
1654                 break;
1655             }
1656 
1657             *pos += cmd.length;
1658         } else
1659             data = NULL;
1660 
1661         if (!check_csum(&cmd, data)) {
1662             ShowRecvError(IDS_RECV_CSUM_ERROR);
1663             if (data) free(data);
1664             b = FALSE;
1665             break;
1666         }
1667 
1668         if (cmd.cmd == BTRFS_SEND_CMD_END) {
1669             if (data) free(data);
1670             ended = TRUE;
1671             break;
1672         }
1673 
1674         if (lastwritefile != INVALID_HANDLE_VALUE && cmd.cmd != BTRFS_SEND_CMD_WRITE) {
1675             if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1676                 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) {
1677                     ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1678                     return FALSE;
1679                 }
1680             }
1681 
1682             CloseHandle(lastwritefile);
1683 
1684             lastwritefile = INVALID_HANDLE_VALUE;
1685             lastwritepath = L"";
1686             lastwriteatt = 0;
1687         }
1688 
1689         switch (cmd.cmd) {
1690             case BTRFS_SEND_CMD_SUBVOL:
1691                 b = cmd_subvol(hwnd, &cmd, data);
1692             break;
1693 
1694             case BTRFS_SEND_CMD_SNAPSHOT:
1695                 b = cmd_snapshot(hwnd, &cmd, data);
1696             break;
1697 
1698             case BTRFS_SEND_CMD_MKFILE:
1699             case BTRFS_SEND_CMD_MKDIR:
1700             case BTRFS_SEND_CMD_MKNOD:
1701             case BTRFS_SEND_CMD_MKFIFO:
1702             case BTRFS_SEND_CMD_MKSOCK:
1703             case BTRFS_SEND_CMD_SYMLINK:
1704                 b = cmd_mkfile(hwnd, &cmd, data);
1705             break;
1706 
1707             case BTRFS_SEND_CMD_RENAME:
1708                 b = cmd_rename(hwnd, &cmd, data);
1709             break;
1710 
1711             case BTRFS_SEND_CMD_LINK:
1712                 b = cmd_link(hwnd, &cmd, data);
1713             break;
1714 
1715             case BTRFS_SEND_CMD_UNLINK:
1716                 b = cmd_unlink(hwnd, &cmd, data);
1717             break;
1718 
1719             case BTRFS_SEND_CMD_RMDIR:
1720                 b = cmd_rmdir(hwnd, &cmd, data);
1721             break;
1722 
1723             case BTRFS_SEND_CMD_SET_XATTR:
1724                 b = cmd_setxattr(hwnd, &cmd, data);
1725             break;
1726 
1727             case BTRFS_SEND_CMD_REMOVE_XATTR:
1728                 b = cmd_removexattr(hwnd, &cmd, data);
1729             break;
1730 
1731             case BTRFS_SEND_CMD_WRITE:
1732                 b = cmd_write(hwnd, &cmd, data);
1733             break;
1734 
1735             case BTRFS_SEND_CMD_CLONE:
1736                 b = cmd_clone(hwnd, &cmd, data);
1737             break;
1738 
1739             case BTRFS_SEND_CMD_TRUNCATE:
1740                 b = cmd_truncate(hwnd, &cmd, data);
1741             break;
1742 
1743             case BTRFS_SEND_CMD_CHMOD:
1744                 b = cmd_chmod(hwnd, &cmd, data);
1745             break;
1746 
1747             case BTRFS_SEND_CMD_CHOWN:
1748                 b = cmd_chown(hwnd, &cmd, data);
1749             break;
1750 
1751             case BTRFS_SEND_CMD_UTIMES:
1752                 b = cmd_utimes(hwnd, &cmd, data);
1753             break;
1754 
1755             case BTRFS_SEND_CMD_UPDATE_EXTENT:
1756                 // does nothing
1757             break;
1758 
1759             default:
1760                 ShowRecvError(IDS_RECV_UNKNOWN_COMMAND, cmd.cmd);
1761                 b = FALSE;
1762             break;
1763         }
1764 
1765         if (data) free(data);
1766 
1767         if (!b)
1768             break;
1769     }
1770 
1771     if (lastwritefile != INVALID_HANDLE_VALUE) {
1772         if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1773             if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt)) {
1774                 ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1775                 return FALSE;
1776             }
1777         }
1778 
1779         CloseHandle(lastwritefile);
1780     }
1781 
1782     if (b && !ended) {
1783         ShowRecvError(IDS_RECV_FILE_TRUNCATED);
1784         b = FALSE;
1785     }
1786 
1787     if (b) {
1788         NTSTATUS Status;
1789         IO_STATUS_BLOCK iosb;
1790         btrfs_received_subvol brs;
1791 
1792         brs.generation = stransid;
1793         brs.uuid = subvol_uuid;
1794 
1795         Status = NtFsControlFile(dir, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_RECEIVED_SUBVOL, &brs, sizeof(btrfs_received_subvol),
1796                                  NULL, 0);
1797         if (!NT_SUCCESS(Status)) {
1798             ShowRecvError(IDS_RECV_RECEIVED_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
1799             b = FALSE;
1800         }
1801     }
1802 
1803     CloseHandle(dir);
1804 
1805     if (master != INVALID_HANDLE_VALUE)
1806         CloseHandle(master);
1807 
1808     if (!b && subvolpath != L"") {
1809         ULONG attrib;
1810 
1811         attrib = GetFileAttributesW(subvolpath.c_str());
1812         attrib &= ~FILE_ATTRIBUTE_READONLY;
1813 
1814         if (!SetFileAttributesW(subvolpath.c_str(), attrib))
1815             ShowRecvError(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1816         else
1817             delete_directory(subvolpath);
1818     }
1819 
1820     return b;
1821 }
1822 
1823 DWORD BtrfsRecv::recv_thread() {
1824     HANDLE f;
1825     LARGE_INTEGER size;
1826     UINT64 pos = 0;
1827     BOOL b;
1828 
1829     running = TRUE;
1830 
1831     f = CreateFileW(streamfile.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1832     if (f == INVALID_HANDLE_VALUE) {
1833         ShowRecvError(IDS_RECV_CANT_OPEN_FILE, funcname, streamfile.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1834         goto end;
1835     }
1836 
1837     if (!GetFileSizeEx(f, &size)) {
1838         ShowRecvError(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1839         goto end;
1840     }
1841 
1842     parent = CreateFileW(dirpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1843                          NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL);
1844     if (parent == INVALID_HANDLE_VALUE) {
1845         ShowRecvError(IDS_RECV_CANT_OPEN_PATH, dirpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1846         CloseHandle(f);
1847         goto end;
1848     }
1849 
1850     do {
1851         b = do_recv(f, &pos, size.QuadPart);
1852     } while (b && pos < (UINT64)size.QuadPart);
1853 
1854     CloseHandle(parent);
1855     CloseHandle(f);
1856 
1857     if (b && hwnd) {
1858         WCHAR s[255];
1859 
1860         SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 65536, 0);
1861 
1862         if (num_received == 1) {
1863             if (!LoadStringW(module, IDS_RECV_SUCCESS, s, sizeof(s) / sizeof(WCHAR)))
1864                 ShowError(hwnd, GetLastError());
1865             else
1866                 SetDlgItemTextW(hwnd, IDC_RECV_MSG, s);
1867         } else {
1868             if (!LoadStringW(module, IDS_RECV_SUCCESS_PLURAL, s, sizeof(s) / sizeof(WCHAR)))
1869                 ShowError(hwnd, GetLastError());
1870             else {
1871                 WCHAR t[255];
1872 
1873                 if (StringCchPrintfW(t, sizeof(t) / sizeof(WCHAR), s, num_received) == STRSAFE_E_INSUFFICIENT_BUFFER)
1874                     ShowError(hwnd, ERROR_INSUFFICIENT_BUFFER);
1875                 else
1876                     SetDlgItemTextW(hwnd, IDC_RECV_MSG, t);
1877             }
1878         }
1879 
1880         if (!LoadStringW(module, IDS_RECV_BUTTON_OK, s, sizeof(s) / sizeof(WCHAR)))
1881             ShowError(hwnd, GetLastError());
1882         else
1883             SetDlgItemTextW(hwnd, IDCANCEL, s);
1884     }
1885 
1886 end:
1887     thread = NULL;
1888     running = FALSE;
1889 
1890     return 0;
1891 }
1892 
1893 static DWORD WINAPI global_recv_thread(LPVOID lpParameter) {
1894     BtrfsRecv* br = (BtrfsRecv*)lpParameter;
1895 
1896     return br->recv_thread();
1897 }
1898 
1899 INT_PTR CALLBACK BtrfsRecv::RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1900     switch (uMsg) {
1901         case WM_INITDIALOG:
1902             this->hwnd = hwndDlg;
1903             thread = CreateThread(NULL, 0, global_recv_thread, this, 0, NULL);
1904 
1905             if (!thread)
1906                 ShowRecvError(IDS_RECV_CREATETHREAD_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1907         break;
1908 
1909         case WM_COMMAND:
1910             switch (HIWORD(wParam)) {
1911                 case BN_CLICKED:
1912                     switch (LOWORD(wParam)) {
1913                         case IDOK:
1914                         case IDCANCEL:
1915                             if (running) {
1916                                 WCHAR s[255];
1917 
1918                                 cancelling = TRUE;
1919 
1920                                 if (!LoadStringW(module, IDS_RECV_CANCELLED, s, sizeof(s) / sizeof(WCHAR))) {
1921                                     ShowError(hwndDlg, GetLastError());
1922                                     return FALSE;
1923                                 }
1924 
1925                                 SetDlgItemTextW(hwnd, IDC_RECV_MSG, s);
1926                                 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 0, 0);
1927 
1928                                 if (!LoadStringW(module, IDS_RECV_BUTTON_OK, s, sizeof(s) / sizeof(WCHAR))) {
1929                                     ShowError(hwndDlg, GetLastError());
1930                                     return FALSE;
1931                                 }
1932 
1933                                 SetDlgItemTextW(hwnd, IDCANCEL, s);
1934                             } else
1935                                 EndDialog(hwndDlg, 1);
1936 
1937                             return TRUE;
1938                     }
1939                 break;
1940             }
1941         break;
1942     }
1943 
1944     return FALSE;
1945 }
1946 
1947 static INT_PTR CALLBACK stub_RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1948     BtrfsRecv* br;
1949 
1950     if (uMsg == WM_INITDIALOG) {
1951         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
1952         br = (BtrfsRecv*)lParam;
1953     } else {
1954         br = (BtrfsRecv*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1955     }
1956 
1957     if (br)
1958         return br->RecvProgressDlgProc(hwndDlg, uMsg, wParam, lParam);
1959     else
1960         return FALSE;
1961 }
1962 
1963 void BtrfsRecv::Open(HWND hwnd, WCHAR* file, WCHAR* path, BOOL quiet) {
1964 #ifndef __REACTOS__
1965     UINT32 cpuInfo[4];
1966 #endif
1967 
1968     streamfile = file;
1969     dirpath = path;
1970     subvolpath = L"";
1971 
1972 #ifndef __REACTOS__
1973 #ifndef _MSC_VER
1974     __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
1975     have_sse42 = cpuInfo[2] & bit_SSE4_2;
1976 #else
1977     __cpuid((int*)cpuInfo, 1);
1978     have_sse42 = cpuInfo[2] & (1 << 20);
1979 #endif
1980 #endif
1981 
1982     if (quiet)
1983         recv_thread();
1984     else {
1985         if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RECV_PROGRESS), hwnd, stub_RecvProgressDlgProc, (LPARAM)this) <= 0)
1986             ShowError(hwnd, GetLastError());
1987     }
1988 }
1989 
1990 void CALLBACK RecvSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1991     OPENFILENAMEW ofn;
1992     WCHAR file[MAX_PATH];
1993     BtrfsRecv* recv;
1994     HANDLE token;
1995     TOKEN_PRIVILEGES* tp;
1996     LUID luid;
1997     ULONG tplen;
1998 
1999     set_dpi_aware();
2000 
2001     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
2002         ShowError(hwnd, GetLastError());
2003         return;
2004     }
2005 
2006     tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
2007     tp = (TOKEN_PRIVILEGES*)malloc(tplen);
2008     if (!tp) {
2009         ShowStringError(hwnd, IDS_OUT_OF_MEMORY);
2010         CloseHandle(token);
2011         return;
2012     }
2013 
2014     tp->PrivilegeCount = 3;
2015 
2016     if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
2017         ShowError(hwnd, GetLastError());
2018         free(tp);
2019         CloseHandle(token);
2020         return;
2021     }
2022 
2023     tp->Privileges[0].Luid = luid;
2024     tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
2025 
2026     if (!LookupPrivilegeValueW(NULL, L"SeSecurityPrivilege", &luid)) {
2027         ShowError(hwnd, GetLastError());
2028         free(tp);
2029         CloseHandle(token);
2030         return;
2031     }
2032 
2033     tp->Privileges[1].Luid = luid;
2034     tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
2035 
2036     if (!LookupPrivilegeValueW(NULL, L"SeRestorePrivilege", &luid)) {
2037         ShowError(hwnd, GetLastError());
2038         free(tp);
2039         CloseHandle(token);
2040         return;
2041     }
2042 
2043     tp->Privileges[2].Luid = luid;
2044     tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
2045 
2046     if (!AdjustTokenPrivileges(token, FALSE, tp, tplen, NULL, NULL)) {
2047         ShowError(hwnd, GetLastError());
2048         free(tp);
2049         CloseHandle(token);
2050         return;
2051     }
2052 
2053     file[0] = 0;
2054 
2055     memset(&ofn, 0, sizeof(OPENFILENAMEW));
2056     ofn.lStructSize = sizeof(OPENFILENAMEW);
2057     ofn.hwndOwner = hwnd;
2058     ofn.hInstance = module;
2059     ofn.lpstrFile = file;
2060     ofn.nMaxFile = sizeof(file) / sizeof(WCHAR);
2061     ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
2062 
2063     if (GetOpenFileNameW(&ofn)) {
2064         recv = new BtrfsRecv;
2065 
2066         recv->Open(hwnd, file, lpszCmdLine, FALSE);
2067 
2068         delete recv;
2069     }
2070 
2071     free(tp);
2072     CloseHandle(token);
2073 }
2074 
2075 void CALLBACK RecvSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
2076     LPWSTR* args;
2077     int num_args;
2078 
2079     args = CommandLineToArgvW(lpszCmdLine, &num_args);
2080 
2081     if (!args)
2082         return;
2083 
2084     if (num_args >= 2) {
2085         BtrfsRecv* br;
2086         HANDLE token;
2087         TOKEN_PRIVILEGES* tp;
2088         ULONG tplen;
2089         LUID luid;
2090 
2091         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
2092             goto end;
2093 
2094         tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
2095         tp = (TOKEN_PRIVILEGES*)malloc(tplen);
2096         if (!tp) {
2097             CloseHandle(token);
2098             goto end;
2099         }
2100 
2101         tp->PrivilegeCount = 3;
2102 
2103         if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
2104             free(tp);
2105             CloseHandle(token);
2106             goto end;
2107         }
2108 
2109         tp->Privileges[0].Luid = luid;
2110         tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
2111 
2112         if (!LookupPrivilegeValueW(NULL, L"SeSecurityPrivilege", &luid)) {
2113             free(tp);
2114             CloseHandle(token);
2115             goto end;
2116         }
2117 
2118         tp->Privileges[1].Luid = luid;
2119         tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
2120 
2121         if (!LookupPrivilegeValueW(NULL, L"SeRestorePrivilege", &luid)) {
2122             free(tp);
2123             CloseHandle(token);
2124             goto end;
2125         }
2126 
2127         tp->Privileges[2].Luid = luid;
2128         tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
2129 
2130         if (!AdjustTokenPrivileges(token, FALSE, tp, tplen, NULL, NULL)) {
2131             free(tp);
2132             CloseHandle(token);
2133             goto end;
2134         }
2135 
2136         free(tp);
2137         CloseHandle(token);
2138 
2139         br = new BtrfsRecv;
2140         br->Open(NULL, args[0], args[1], TRUE);
2141 
2142         delete br;
2143     }
2144 
2145 end:
2146     LocalFree(args);
2147 }
2148