[SHELLBTRFS] Upgrade to 1.5
[reactos.git] / dll / shellext / shellbtrfs / recv.cpp
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_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