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