0cdb609aa58b7aac10b2ac42dee9ce12acf0b644
[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) -= sizeof(type), buf += sizeof (type)) { \
88 (crc) = 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 ULONG 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, bcslen;
182 btrfs_create_subvol* bcs;
183 NTSTATUS Status;
184 IO_STATUS_BLOCK iosb;
185 wstring nameu;
186
187 {
188 char* namebuf;
189 ULONG namelen;
190
191 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&namebuf, &namelen))
192 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
193
194 name = string(namebuf, namelen);
195 }
196
197 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_UUID, (void**)&uuid, &uuidlen))
198 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"uuid");
199
200 if (uuidlen < sizeof(BTRFS_UUID))
201 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"uuid", uuidlen, sizeof(BTRFS_UUID));
202
203 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_TRANSID, (void**)&gen, &genlen))
204 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"transid");
205
206 if (genlen < sizeof(uint64_t))
207 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"transid", genlen, sizeof(uint64_t));
208
209 this->subvol_uuid = *uuid;
210 this->stransid = *gen;
211
212 utf8_to_utf16(name, nameu);
213
214 bcslen = offsetof(btrfs_create_subvol, name[0]) + (nameu.length() * sizeof(WCHAR));
215 bcs = (btrfs_create_subvol*)malloc(bcslen);
216
217 bcs->readonly = true;
218 bcs->posix = true;
219 bcs->namelen = (uint16_t)(nameu.length() * sizeof(WCHAR));
220 memcpy(bcs->name, nameu.c_str(), bcs->namelen);
221
222 Status = NtFsControlFile(parent, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, bcs, bcslen, nullptr, 0);
223 if (!NT_SUCCESS(Status))
224 throw string_error(IDS_RECV_CREATE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
225
226 subvolpath = dirpath;
227 subvolpath += L"\\";
228 subvolpath += nameu;
229
230 if (dir != INVALID_HANDLE_VALUE)
231 CloseHandle(dir);
232
233 if (master != INVALID_HANDLE_VALUE)
234 CloseHandle(master);
235
236 master = CreateFileW(subvolpath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
237 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
238 if (master == INVALID_HANDLE_VALUE)
239 throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
240
241 Status = NtFsControlFile(master, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RESERVE_SUBVOL, bcs, bcslen, nullptr, 0);
242 if (!NT_SUCCESS(Status))
243 throw string_error(IDS_RECV_RESERVE_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
244
245 dir = CreateFileW(subvolpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE,
246 FILE_SHARE_READ | FILE_SHARE_WRITE,
247 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
248 if (dir == INVALID_HANDLE_VALUE)
249 throw string_error(IDS_RECV_CANT_OPEN_PATH, subvolpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
250
251 subvolpath += L"\\";
252
253 add_cache_entry(&this->subvol_uuid, this->stransid, subvolpath);
254
255 num_received++;
256 }
257
258 void BtrfsRecv::add_cache_entry(BTRFS_UUID* uuid, uint64_t transid, const wstring& path) {
259 subvol_cache sc;
260
261 sc.uuid = *uuid;
262 sc.transid = transid;
263 sc.path = path;
264
265 cache.push_back(sc);
266 }
267
268 void BtrfsRecv::cmd_snapshot(HWND hwnd, btrfs_send_command* cmd, uint8_t* data, const win_handle& parent) {
269 string name;
270 BTRFS_UUID *uuid, *parent_uuid;
271 uint64_t *gen, *parent_transid;
272 ULONG uuidlen, genlen, paruuidlen, partransidlen, bcslen;
273 btrfs_create_snapshot* bcs;
274 NTSTATUS Status;
275 IO_STATUS_BLOCK iosb;
276 wstring nameu, parpath;
277 btrfs_find_subvol bfs;
278 WCHAR parpathw[MAX_PATH], volpathw[MAX_PATH];
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 utf8_to_utf16(name, nameu);
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, 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, 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, bmnsize;
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 utf8_to_utf16(string(name, namelen), nameu);
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 utf8_to_utf16(string(pathlink, pathlinklen), pathlinku);
436 }
437
438 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, 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 ULONG rdblen;
469 btrfs_set_inode_info bsii;
470
471 rdblen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]) + (2 * pathlinku.length() * sizeof(WCHAR));
472
473 if (rdblen >= 0x10000)
474 throw string_error(IDS_RECV_PATH_TOO_LONG, funcname);
475
476 rdb = (REPARSE_DATA_BUFFER*)malloc(rdblen);
477
478 rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK;
479 rdb->ReparseDataLength = (uint16_t)(rdblen - offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
480 rdb->Reserved = 0;
481 rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
482 rdb->SymbolicLinkReparseBuffer.SubstituteNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR));
483 rdb->SymbolicLinkReparseBuffer.PrintNameOffset = (uint16_t)(pathlinku.length() * sizeof(WCHAR));
484 rdb->SymbolicLinkReparseBuffer.PrintNameLength = (uint16_t)(pathlinku.length() * sizeof(WCHAR));
485 rdb->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
486
487 memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer, pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
488 memcpy(rdb->SymbolicLinkReparseBuffer.PathBuffer + (rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)),
489 pathlinku.c_str(), rdb->SymbolicLinkReparseBuffer.PrintNameLength);
490
491 win_handle h = CreateFileW((subvolpath + nameu).c_str(), GENERIC_WRITE | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
492 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
493 if (h == INVALID_HANDLE_VALUE) {
494 free(rdb);
495 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
496 }
497
498 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_SET_REPARSE_POINT, rdb, rdblen, nullptr, 0);
499 if (!NT_SUCCESS(Status)) {
500 free(rdb);
501 throw string_error(IDS_RECV_SET_REPARSE_POINT_FAILED, Status, format_ntstatus(Status).c_str());
502 }
503
504 free(rdb);
505
506 memset(&bsii, 0, sizeof(btrfs_set_inode_info));
507
508 bsii.mode_changed = true;
509 bsii.st_mode = 0777;
510
511 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
512 if (!NT_SUCCESS(Status))
513 throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
514 } else if (cmd->cmd == BTRFS_SEND_CMD_MKNOD || cmd->cmd == BTRFS_SEND_CMD_MKFIFO || cmd->cmd == BTRFS_SEND_CMD_MKSOCK) {
515 uint64_t* mode;
516 ULONG modelen;
517
518 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MODE, (void**)&mode, &modelen)) {
519 btrfs_set_inode_info bsii;
520
521 if (modelen < sizeof(uint64_t))
522 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"mode", modelen, sizeof(uint64_t));
523
524 win_handle h = CreateFileW((subvolpath + nameu).c_str(), WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
525 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
526 if (h == INVALID_HANDLE_VALUE)
527 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, nameu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
528
529 memset(&bsii, 0, sizeof(btrfs_set_inode_info));
530
531 bsii.mode_changed = true;
532 bsii.st_mode = (uint32_t)*mode;
533
534 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
535 if (!NT_SUCCESS(Status))
536 throw string_error(IDS_RECV_SETINODEINFO_FAILED, Status, format_ntstatus(Status).c_str());
537 }
538 }
539 }
540
541 void BtrfsRecv::cmd_rename(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
542 wstring pathu, path_tou;
543
544 {
545 char* path;
546 ULONG path_len;
547
548 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len))
549 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
550
551 utf8_to_utf16(string(path, path_len), pathu);
552 }
553
554 {
555 char* path_to;
556 ULONG path_to_len;
557
558 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_TO, (void**)&path_to, &path_to_len))
559 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_to");
560
561 utf8_to_utf16(string(path_to, path_to_len), path_tou);
562 }
563
564 if (!MoveFileW((subvolpath + pathu).c_str(), (subvolpath + path_tou).c_str()))
565 throw string_error(IDS_RECV_MOVEFILE_FAILED, pathu.c_str(), path_tou.c_str(), GetLastError(), format_message(GetLastError()).c_str());
566 }
567
568 void BtrfsRecv::cmd_link(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
569 wstring pathu, path_linku;
570
571 {
572 char* path;
573 ULONG path_len;
574
575 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &path_len))
576 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
577
578 utf8_to_utf16(string(path, path_len), pathu);
579 }
580
581 {
582 char* path_link;
583 ULONG path_link_len;
584
585 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH_LINK, (void**)&path_link, &path_link_len))
586 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path_link");
587
588 utf8_to_utf16(string(path_link, path_link_len), path_linku);
589 }
590
591 if (!CreateHardLinkW((subvolpath + pathu).c_str(), (subvolpath + path_linku).c_str(), nullptr))
592 throw string_error(IDS_RECV_CREATEHARDLINK_FAILED, pathu.c_str(), path_linku.c_str(), GetLastError(), format_message(GetLastError()).c_str());
593 }
594
595 void BtrfsRecv::cmd_unlink(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
596 wstring pathu;
597 ULONG att;
598
599 {
600 char* path;
601 ULONG pathlen;
602
603 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
604 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
605
606 utf8_to_utf16(string(path, pathlen), pathu);
607 }
608
609 att = GetFileAttributesW((subvolpath + pathu).c_str());
610 if (att == INVALID_FILE_ATTRIBUTES)
611 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
612
613 if (att & FILE_ATTRIBUTE_READONLY) {
614 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
615 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
616 }
617
618 if (!DeleteFileW((subvolpath + pathu).c_str()))
619 throw string_error(IDS_RECV_DELETEFILE_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
620 }
621
622 void BtrfsRecv::cmd_rmdir(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
623 wstring pathu;
624 ULONG att;
625
626 {
627 char* path;
628 ULONG pathlen;
629
630 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
631 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
632
633 utf8_to_utf16(string(path, pathlen), pathu);
634 }
635
636 att = GetFileAttributesW((subvolpath + pathu).c_str());
637 if (att == INVALID_FILE_ATTRIBUTES)
638 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
639
640 if (att & FILE_ATTRIBUTE_READONLY) {
641 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
642 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
643 }
644
645 if (!RemoveDirectoryW((subvolpath + pathu).c_str()))
646 throw string_error(IDS_RECV_REMOVEDIRECTORY_FAILED, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
647 }
648
649 void BtrfsRecv::cmd_setxattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
650 string xattrname;
651 uint8_t* xattrdata;
652 ULONG xattrdatalen;
653 wstring pathu;
654
655 {
656 char* path;
657 ULONG pathlen;
658
659 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
660 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
661
662 utf8_to_utf16(string(path, pathlen), pathu);
663 }
664
665 {
666 char* xattrnamebuf;
667 ULONG xattrnamelen;
668
669 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen))
670 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
671
672 xattrname = string(xattrnamebuf, xattrnamelen);
673 }
674
675 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_DATA, (void**)&xattrdata, &xattrdatalen))
676 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_data");
677
678 if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER &&
679 xattrname != EA_DOSATTRIB && xattrname != EA_EA && xattrname != EA_REPARSE) {
680 wstring streamname;
681 ULONG att;
682
683 utf8_to_utf16(xattrname, streamname);
684
685 att = GetFileAttributesW((subvolpath + pathu).c_str());
686 if (att == INVALID_FILE_ATTRIBUTES)
687 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
688
689 if (att & FILE_ATTRIBUTE_READONLY) {
690 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
691 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
692 }
693
694 streamname = streamname.substr(XATTR_USER.length());
695
696 win_handle h = CreateFileW((subvolpath + pathu + L":" + streamname).c_str(), GENERIC_WRITE, 0,
697 nullptr, CREATE_ALWAYS, FILE_FLAG_POSIX_SEMANTICS, nullptr);
698 if (h == INVALID_HANDLE_VALUE)
699 throw string_error(IDS_RECV_CANT_CREATE_FILE, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
700
701 if (xattrdatalen > 0) {
702 if (!WriteFile(h, xattrdata, xattrdatalen, nullptr, nullptr))
703 throw string_error(IDS_RECV_WRITEFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
704 }
705
706 if (att & FILE_ATTRIBUTE_READONLY) {
707 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att))
708 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
709 }
710 } else {
711 IO_STATUS_BLOCK iosb;
712 NTSTATUS Status;
713 ULONG bsxalen, perms = FILE_WRITE_ATTRIBUTES;
714 btrfs_set_xattr* bsxa;
715
716 if (xattrname == EA_NTACL)
717 perms |= WRITE_DAC | WRITE_OWNER;
718 else if (xattrname == EA_EA)
719 perms |= FILE_WRITE_EA;
720
721 win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
722 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr);
723 if (h == INVALID_HANDLE_VALUE)
724 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
725
726 bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length() + xattrdatalen;
727 bsxa = (btrfs_set_xattr*)malloc(bsxalen);
728 if (!bsxa)
729 throw string_error(IDS_OUT_OF_MEMORY);
730
731 bsxa->namelen = (uint16_t)xattrname.length();
732 bsxa->valuelen = (uint16_t)xattrdatalen;
733 memcpy(bsxa->data, xattrname.c_str(), xattrname.length());
734 memcpy(&bsxa->data[xattrname.length()], xattrdata, xattrdatalen);
735
736 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, bsxalen, nullptr, 0);
737 if (!NT_SUCCESS(Status)) {
738 free(bsxa);
739 throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
740 }
741
742 free(bsxa);
743 }
744 }
745
746 void BtrfsRecv::cmd_removexattr(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
747 wstring pathu;
748 string xattrname;
749
750 {
751 char* path;
752 ULONG pathlen;
753
754 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
755 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
756
757 utf8_to_utf16(string(path, pathlen), pathu);
758 }
759
760 {
761 char* xattrnamebuf;
762 ULONG xattrnamelen;
763
764 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_XATTR_NAME, (void**)&xattrnamebuf, &xattrnamelen))
765 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"xattr_name");
766
767 xattrname = string(xattrnamebuf, xattrnamelen);
768 }
769
770 if (xattrname.length() > XATTR_USER.length() && xattrname.substr(0, XATTR_USER.length()) == XATTR_USER && xattrname != EA_DOSATTRIB && xattrname != EA_EA) { // deleting stream
771 ULONG att;
772 wstring streamname;
773
774 utf8_to_utf16(xattrname, streamname);
775
776 streamname = streamname.substr(XATTR_USER.length());
777
778 att = GetFileAttributesW((subvolpath + pathu).c_str());
779 if (att == INVALID_FILE_ATTRIBUTES)
780 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
781
782 if (att & FILE_ATTRIBUTE_READONLY) {
783 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att & ~FILE_ATTRIBUTE_READONLY))
784 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
785 }
786
787 if (!DeleteFileW((subvolpath + pathu + L":" + streamname).c_str()))
788 throw string_error(IDS_RECV_DELETEFILE_FAILED, (pathu + L":" + streamname).c_str(), GetLastError(), format_message(GetLastError()).c_str());
789
790 if (att & FILE_ATTRIBUTE_READONLY) {
791 if (!SetFileAttributesW((subvolpath + pathu).c_str(), att))
792 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
793 }
794 } else {
795 IO_STATUS_BLOCK iosb;
796 NTSTATUS Status;
797 ULONG bsxalen, perms = FILE_WRITE_ATTRIBUTES;
798 btrfs_set_xattr* bsxa;
799
800 if (xattrname == EA_NTACL)
801 perms |= WRITE_DAC | WRITE_OWNER;
802 else if (xattrname == EA_EA)
803 perms |= FILE_WRITE_EA;
804
805 win_handle h = CreateFileW((subvolpath + pathu).c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
806 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_OPEN_REPARSE_POINT, nullptr);
807 if (h == INVALID_HANDLE_VALUE)
808 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
809
810 bsxalen = offsetof(btrfs_set_xattr, data[0]) + xattrname.length();
811 bsxa = (btrfs_set_xattr*)malloc(bsxalen);
812 if (!bsxa)
813 throw string_error(IDS_OUT_OF_MEMORY);
814
815 bsxa->namelen = (uint16_t)(xattrname.length());
816 bsxa->valuelen = 0;
817 memcpy(bsxa->data, xattrname.c_str(), xattrname.length());
818
819 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_XATTR, bsxa, bsxalen, nullptr, 0);
820 if (!NT_SUCCESS(Status)) {
821 free(bsxa);
822 throw string_error(IDS_RECV_SETXATTR_FAILED, Status, format_ntstatus(Status).c_str());
823 }
824
825 free(bsxa);
826 }
827 }
828
829 void BtrfsRecv::cmd_write(HWND hwnd, btrfs_send_command* cmd, uint8_t* data) {
830 uint64_t* offset;
831 uint8_t* writedata;
832 ULONG offsetlen, datalen;
833 wstring pathu;
834 HANDLE h;
835 LARGE_INTEGER offli;
836
837 {
838 char* path;
839 ULONG pathlen;
840
841 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
842 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
843
844 utf8_to_utf16(string(path, pathlen), pathu);
845 }
846
847 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_OFFSET, (void**)&offset, &offsetlen))
848 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"offset");
849
850 if (offsetlen < sizeof(uint64_t))
851 throw string_error(IDS_RECV_SHORT_PARAM, funcname, L"offset", offsetlen, sizeof(uint64_t));
852
853 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_DATA, (void**)&writedata, &datalen))
854 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"data");
855
856 if (lastwritepath != pathu) {
857 FILE_BASIC_INFO fbi;
858
859 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
860 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt))
861 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
862 }
863
864 CloseHandle(lastwritefile);
865
866 lastwriteatt = GetFileAttributesW((subvolpath + pathu).c_str());
867 if (lastwriteatt == INVALID_FILE_ATTRIBUTES)
868 throw string_error(IDS_RECV_GETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
869
870 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
871 if (!SetFileAttributesW((subvolpath + pathu).c_str(), lastwriteatt & ~FILE_ATTRIBUTE_READONLY))
872 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
873 }
874
875 h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING,
876 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
877 if (h == INVALID_HANDLE_VALUE)
878 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
879
880 lastwritepath = pathu;
881 lastwritefile = h;
882
883 memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
884
885 fbi.LastWriteTime.QuadPart = -1;
886
887 if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO)))
888 throw string_error(IDS_RECV_SETFILEINFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
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 utf8_to_utf16(string(path, pathlen), pathu);
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 utf8_to_utf16(string(clonepath, clonepathlen), clonepathu);
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 utf8_to_utf16(string(path, pathlen), pathu);
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 utf8_to_utf16(string(path, pathlen), pathu);
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 utf8_to_utf16(string(path, pathlen), pathu);
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
1191 {
1192 char* path;
1193 ULONG pathlen;
1194
1195 if (!find_tlv(data, cmd->length, BTRFS_SEND_TLV_PATH, (void**)&path, &pathlen))
1196 throw string_error(IDS_RECV_MISSING_PARAM, funcname, L"path");
1197
1198 utf8_to_utf16(string(path, pathlen), pathu);
1199 }
1200
1201 h = CreateFileW((subvolpath + pathu).c_str(), FILE_WRITE_ATTRIBUTES, 0, nullptr, OPEN_EXISTING,
1202 FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS, nullptr);
1203 if (h == INVALID_HANDLE_VALUE)
1204 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, pathu.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1205
1206 memset(&fbi, 0, sizeof(FILE_BASIC_INFO));
1207
1208 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_OTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1209 fbi.CreationTime.QuadPart = unix_time_to_win(time);
1210
1211 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_ATIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1212 fbi.LastAccessTime.QuadPart = unix_time_to_win(time);
1213
1214 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_MTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1215 fbi.LastWriteTime.QuadPart = unix_time_to_win(time);
1216
1217 if (find_tlv(data, cmd->length, BTRFS_SEND_TLV_CTIME, (void**)&time, &timelen) && timelen >= sizeof(BTRFS_TIME))
1218 fbi.ChangeTime.QuadPart = unix_time_to_win(time);
1219
1220 if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(FILE_BASIC_INFO)))
1221 throw string_error(IDS_RECV_SETFILEINFO_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1222 }
1223
1224 static void delete_directory(const wstring& dir) {
1225 WIN32_FIND_DATAW fff;
1226
1227 fff_handle h = FindFirstFileW((dir + L"*").c_str(), &fff);
1228
1229 if (h == INVALID_HANDLE_VALUE)
1230 return;
1231
1232 do {
1233 wstring file;
1234
1235 file = fff.cFileName;
1236
1237 if (file != L"." && file != L"..") {
1238 if (fff.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1239 SetFileAttributesW((dir + file).c_str(), fff.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
1240
1241 if (fff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1242 if (!(fff.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
1243 delete_directory(dir + file + L"\\");
1244 else
1245 RemoveDirectoryW((dir + file).c_str());
1246 } else
1247 DeleteFileW((dir + file).c_str());
1248 }
1249 } while (FindNextFileW(h, &fff));
1250
1251 RemoveDirectoryW(dir.c_str());
1252 }
1253
1254 static bool check_csum(btrfs_send_command* cmd, uint8_t* data) {
1255 uint32_t crc32 = cmd->csum, calc;
1256
1257 cmd->csum = 0;
1258
1259 calc = calc_crc32c(0, (uint8_t*)cmd, sizeof(btrfs_send_command));
1260
1261 if (cmd->length > 0)
1262 calc = calc_crc32c(calc, data, cmd->length);
1263
1264 return calc == crc32 ? true : false;
1265 }
1266
1267 void BtrfsRecv::do_recv(const win_handle& f, uint64_t* pos, uint64_t size, const win_handle& parent) {
1268 try {
1269 btrfs_send_header header;
1270 bool ended = false;
1271
1272 if (!ReadFile(f, &header, sizeof(btrfs_send_header), nullptr, nullptr))
1273 throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1274
1275 *pos += sizeof(btrfs_send_header);
1276
1277 if (memcmp(header.magic, BTRFS_SEND_MAGIC, sizeof(header.magic)))
1278 throw string_error(IDS_RECV_NOT_A_SEND_STREAM);
1279
1280 if (header.version > 1)
1281 throw string_error(IDS_RECV_UNSUPPORTED_VERSION, header.version);
1282
1283 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETRANGE32, 0, (LPARAM)65536);
1284
1285 lastwritefile = INVALID_HANDLE_VALUE;
1286 lastwritepath = L"";
1287 lastwriteatt = 0;
1288
1289 while (true) {
1290 btrfs_send_command cmd;
1291 uint8_t* data = nullptr;
1292 ULONG progress;
1293
1294 if (cancelling)
1295 break;
1296
1297 progress = (ULONG)((float)*pos * 65536.0f / (float)size);
1298 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, progress, 0);
1299
1300 if (!ReadFile(f, &cmd, sizeof(btrfs_send_command), nullptr, nullptr)) {
1301 if (GetLastError() != ERROR_HANDLE_EOF)
1302 throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1303
1304 break;
1305 }
1306
1307 *pos += sizeof(btrfs_send_command);
1308
1309 if (cmd.length > 0) {
1310 if (*pos + cmd.length > size)
1311 throw string_error(IDS_RECV_FILE_TRUNCATED);
1312
1313 data = (uint8_t*)malloc(cmd.length);
1314 if (!data)
1315 throw string_error(IDS_OUT_OF_MEMORY);
1316 }
1317
1318 try {
1319 if (data) {
1320 if (!ReadFile(f, data, cmd.length, nullptr, nullptr))
1321 throw string_error(IDS_RECV_READFILE_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1322
1323 *pos += cmd.length;
1324 }
1325
1326 if (!check_csum(&cmd, data))
1327 throw string_error(IDS_RECV_CSUM_ERROR);
1328
1329 if (cmd.cmd == BTRFS_SEND_CMD_END) {
1330 ended = true;
1331 break;
1332 }
1333
1334 if (lastwritefile != INVALID_HANDLE_VALUE && cmd.cmd != BTRFS_SEND_CMD_WRITE) {
1335 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1336 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt))
1337 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1338 }
1339
1340 CloseHandle(lastwritefile);
1341
1342 lastwritefile = INVALID_HANDLE_VALUE;
1343 lastwritepath = L"";
1344 lastwriteatt = 0;
1345 }
1346
1347 switch (cmd.cmd) {
1348 case BTRFS_SEND_CMD_SUBVOL:
1349 cmd_subvol(hwnd, &cmd, data, parent);
1350 break;
1351
1352 case BTRFS_SEND_CMD_SNAPSHOT:
1353 cmd_snapshot(hwnd, &cmd, data, parent);
1354 break;
1355
1356 case BTRFS_SEND_CMD_MKFILE:
1357 case BTRFS_SEND_CMD_MKDIR:
1358 case BTRFS_SEND_CMD_MKNOD:
1359 case BTRFS_SEND_CMD_MKFIFO:
1360 case BTRFS_SEND_CMD_MKSOCK:
1361 case BTRFS_SEND_CMD_SYMLINK:
1362 cmd_mkfile(hwnd, &cmd, data);
1363 break;
1364
1365 case BTRFS_SEND_CMD_RENAME:
1366 cmd_rename(hwnd, &cmd, data);
1367 break;
1368
1369 case BTRFS_SEND_CMD_LINK:
1370 cmd_link(hwnd, &cmd, data);
1371 break;
1372
1373 case BTRFS_SEND_CMD_UNLINK:
1374 cmd_unlink(hwnd, &cmd, data);
1375 break;
1376
1377 case BTRFS_SEND_CMD_RMDIR:
1378 cmd_rmdir(hwnd, &cmd, data);
1379 break;
1380
1381 case BTRFS_SEND_CMD_SET_XATTR:
1382 cmd_setxattr(hwnd, &cmd, data);
1383 break;
1384
1385 case BTRFS_SEND_CMD_REMOVE_XATTR:
1386 cmd_removexattr(hwnd, &cmd, data);
1387 break;
1388
1389 case BTRFS_SEND_CMD_WRITE:
1390 cmd_write(hwnd, &cmd, data);
1391 break;
1392
1393 case BTRFS_SEND_CMD_CLONE:
1394 cmd_clone(hwnd, &cmd, data);
1395 break;
1396
1397 case BTRFS_SEND_CMD_TRUNCATE:
1398 cmd_truncate(hwnd, &cmd, data);
1399 break;
1400
1401 case BTRFS_SEND_CMD_CHMOD:
1402 cmd_chmod(hwnd, &cmd, data);
1403 break;
1404
1405 case BTRFS_SEND_CMD_CHOWN:
1406 cmd_chown(hwnd, &cmd, data);
1407 break;
1408
1409 case BTRFS_SEND_CMD_UTIMES:
1410 cmd_utimes(hwnd, &cmd, data);
1411 break;
1412
1413 case BTRFS_SEND_CMD_UPDATE_EXTENT:
1414 // does nothing
1415 break;
1416
1417 default:
1418 throw string_error(IDS_RECV_UNKNOWN_COMMAND, cmd.cmd);
1419 }
1420 } catch (...) {
1421 if (data) free(data);
1422 throw;
1423 }
1424
1425 if (data) free(data);
1426 }
1427
1428 if (lastwritefile != INVALID_HANDLE_VALUE) {
1429 if (lastwriteatt & FILE_ATTRIBUTE_READONLY) {
1430 if (!SetFileAttributesW((subvolpath + lastwritepath).c_str(), lastwriteatt))
1431 throw string_error(IDS_RECV_SETFILEATTRIBUTES_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1432 }
1433
1434 CloseHandle(lastwritefile);
1435 }
1436
1437 if (!ended && !cancelling)
1438 throw string_error(IDS_RECV_FILE_TRUNCATED);
1439
1440 if (!cancelling) {
1441 NTSTATUS Status;
1442 IO_STATUS_BLOCK iosb;
1443 btrfs_received_subvol brs;
1444
1445 brs.generation = stransid;
1446 brs.uuid = subvol_uuid;
1447
1448 Status = NtFsControlFile(dir, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_RECEIVED_SUBVOL, &brs, sizeof(btrfs_received_subvol),
1449 nullptr, 0);
1450 if (!NT_SUCCESS(Status))
1451 throw string_error(IDS_RECV_RECEIVED_SUBVOL_FAILED, Status, format_ntstatus(Status).c_str());
1452 }
1453
1454 CloseHandle(dir);
1455
1456 if (master != INVALID_HANDLE_VALUE)
1457 CloseHandle(master);
1458 } catch (...) {
1459 if (subvolpath != L"") {
1460 ULONG attrib;
1461
1462 attrib = GetFileAttributesW(subvolpath.c_str());
1463 attrib &= ~FILE_ATTRIBUTE_READONLY;
1464
1465 if (SetFileAttributesW(subvolpath.c_str(), attrib))
1466 delete_directory(subvolpath);
1467 }
1468
1469 throw;
1470 }
1471 }
1472
1473 DWORD BtrfsRecv::recv_thread() {
1474 LARGE_INTEGER size;
1475 uint64_t pos = 0;
1476 bool b = true;
1477
1478 running = true;
1479
1480 try {
1481 win_handle f = CreateFileW(streamfile.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
1482 if (f == INVALID_HANDLE_VALUE)
1483 throw string_error(IDS_RECV_CANT_OPEN_FILE, funcname, streamfile.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1484
1485 if (!GetFileSizeEx(f, &size))
1486 throw string_error(IDS_RECV_GETFILESIZEEX_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1487
1488 {
1489 win_handle parent = CreateFileW(dirpath.c_str(), FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1490 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, nullptr);
1491 if (parent == INVALID_HANDLE_VALUE)
1492 throw string_error(IDS_RECV_CANT_OPEN_PATH, dirpath.c_str(), GetLastError(), format_message(GetLastError()).c_str());
1493
1494 do {
1495 do_recv(f, &pos, size.QuadPart, parent);
1496 } while (pos < (uint64_t)size.QuadPart);
1497 }
1498 } catch (const exception& e) {
1499 wstring msg;
1500
1501 utf8_to_utf16(e.what(), msg);
1502
1503 SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str());
1504
1505 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0);
1506
1507 b = false;
1508 }
1509
1510 if (b && hwnd) {
1511 wstring s;
1512
1513 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 65536, 0);
1514
1515 if (num_received == 1) {
1516 load_string(module, IDS_RECV_SUCCESS, s);
1517 SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str());
1518 } else {
1519 wstring t;
1520
1521 load_string(module, IDS_RECV_SUCCESS_PLURAL, s);
1522
1523 wstring_sprintf(t, s, num_received);
1524
1525 SetDlgItemTextW(hwnd, IDC_RECV_MSG, t.c_str());
1526 }
1527
1528 load_string(module, IDS_RECV_BUTTON_OK, s);
1529
1530 SetDlgItemTextW(hwnd, IDCANCEL, s.c_str());
1531 }
1532
1533 thread = nullptr;
1534 running = false;
1535
1536 return 0;
1537 }
1538
1539 static DWORD WINAPI global_recv_thread(LPVOID lpParameter) {
1540 BtrfsRecv* br = (BtrfsRecv*)lpParameter;
1541
1542 return br->recv_thread();
1543 }
1544
1545 INT_PTR CALLBACK BtrfsRecv::RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1546 switch (uMsg) {
1547 case WM_INITDIALOG:
1548 try {
1549 this->hwnd = hwndDlg;
1550 thread = CreateThread(nullptr, 0, global_recv_thread, this, 0, nullptr);
1551
1552 if (!thread)
1553 throw string_error(IDS_RECV_CREATETHREAD_FAILED, GetLastError(), format_message(GetLastError()).c_str());
1554 } catch (const exception& e) {
1555 wstring msg;
1556
1557 utf8_to_utf16(e.what(), msg);
1558
1559 SetDlgItemTextW(hwnd, IDC_RECV_MSG, msg.c_str());
1560
1561 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETSTATE, PBST_ERROR, 0);
1562 }
1563 break;
1564
1565 case WM_COMMAND:
1566 switch (HIWORD(wParam)) {
1567 case BN_CLICKED:
1568 switch (LOWORD(wParam)) {
1569 case IDOK:
1570 case IDCANCEL:
1571 if (running) {
1572 wstring s;
1573
1574 cancelling = true;
1575
1576 if (!load_string(module, IDS_RECV_CANCELLED, s))
1577 throw last_error(GetLastError());
1578
1579 SetDlgItemTextW(hwnd, IDC_RECV_MSG, s.c_str());
1580 SendMessageW(GetDlgItem(hwnd, IDC_RECV_PROGRESS), PBM_SETPOS, 0, 0);
1581
1582 if (!load_string(module, IDS_RECV_BUTTON_OK, s))
1583 throw last_error(GetLastError());
1584
1585 SetDlgItemTextW(hwnd, IDCANCEL, s.c_str());
1586 } else
1587 EndDialog(hwndDlg, 1);
1588
1589 return true;
1590 }
1591 break;
1592 }
1593 break;
1594 }
1595
1596 return false;
1597 }
1598
1599 static INT_PTR CALLBACK stub_RecvProgressDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1600 BtrfsRecv* br;
1601
1602 if (uMsg == WM_INITDIALOG) {
1603 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
1604 br = (BtrfsRecv*)lParam;
1605 } else {
1606 br = (BtrfsRecv*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1607 }
1608
1609 if (br)
1610 return br->RecvProgressDlgProc(hwndDlg, uMsg, wParam, lParam);
1611 else
1612 return false;
1613 }
1614
1615 void BtrfsRecv::Open(HWND hwnd, const wstring& file, const wstring& path, bool quiet) {
1616 #ifndef __REACTOS__
1617 uint32_t cpuInfo[4];
1618 #endif
1619
1620 streamfile = file;
1621 dirpath = path;
1622 subvolpath = L"";
1623
1624 #ifndef __REACTOS__
1625 #ifndef _MSC_VER
1626 __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]);
1627 have_sse42 = cpuInfo[2] & bit_SSE4_2;
1628 #else
1629 __cpuid((int*)cpuInfo, 1);
1630 have_sse42 = cpuInfo[2] & (1 << 20);
1631 #endif
1632 #endif
1633
1634 if (quiet)
1635 recv_thread();
1636 else {
1637 if (DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_RECV_PROGRESS), hwnd, stub_RecvProgressDlgProc, (LPARAM)this) <= 0)
1638 throw last_error(GetLastError());
1639 }
1640 }
1641
1642 #ifdef __REACTOS__
1643 extern "C" {
1644 #endif
1645
1646 void CALLBACK RecvSubvolGUIW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1647 try {
1648 OPENFILENAMEW ofn;
1649 WCHAR file[MAX_PATH];
1650 win_handle token;
1651 TOKEN_PRIVILEGES* tp;
1652 LUID luid;
1653 ULONG tplen;
1654
1655 set_dpi_aware();
1656
1657 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1658 throw last_error(GetLastError());
1659
1660 tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
1661 tp = (TOKEN_PRIVILEGES*)malloc(tplen);
1662 if (!tp)
1663 throw string_error(IDS_OUT_OF_MEMORY);
1664
1665 tp->PrivilegeCount = 3;
1666
1667 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) {
1668 free(tp);
1669 throw last_error(GetLastError());
1670 }
1671
1672 tp->Privileges[0].Luid = luid;
1673 tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1674
1675 if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) {
1676 free(tp);
1677 throw last_error(GetLastError());
1678 }
1679
1680 tp->Privileges[1].Luid = luid;
1681 tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
1682
1683 if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) {
1684 free(tp);
1685 throw last_error(GetLastError());
1686 }
1687
1688 tp->Privileges[2].Luid = luid;
1689 tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
1690
1691 if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) {
1692 free(tp);
1693 throw last_error(GetLastError());
1694 }
1695
1696 file[0] = 0;
1697
1698 memset(&ofn, 0, sizeof(OPENFILENAMEW));
1699 ofn.lStructSize = sizeof(OPENFILENAMEW);
1700 ofn.hwndOwner = hwnd;
1701 ofn.hInstance = module;
1702 ofn.lpstrFile = file;
1703 ofn.nMaxFile = sizeof(file) / sizeof(WCHAR);
1704 ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1705
1706 if (GetOpenFileNameW(&ofn)) {
1707 BtrfsRecv recv;
1708
1709 recv.Open(hwnd, file, lpszCmdLine, false);
1710 }
1711
1712 free(tp);
1713 } catch (const exception& e) {
1714 error_message(hwnd, e.what());
1715 }
1716 }
1717
1718 void CALLBACK RecvSubvolW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1719 try {
1720 vector<wstring> args;
1721
1722 command_line_to_args(lpszCmdLine, args);
1723
1724 if (args.size() >= 2) {
1725 win_handle token;
1726 TOKEN_PRIVILEGES* tp;
1727 ULONG tplen;
1728 LUID luid;
1729
1730 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1731 return;
1732
1733 tplen = offsetof(TOKEN_PRIVILEGES, Privileges[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES));
1734 tp = (TOKEN_PRIVILEGES*)malloc(tplen);
1735 if (!tp)
1736 return;
1737
1738 tp->PrivilegeCount = 3;
1739
1740 if (!LookupPrivilegeValueW(nullptr, L"SeManageVolumePrivilege", &luid)) {
1741 free(tp);
1742 return;
1743 }
1744
1745 tp->Privileges[0].Luid = luid;
1746 tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1747
1748 if (!LookupPrivilegeValueW(nullptr, L"SeSecurityPrivilege", &luid)) {
1749 free(tp);
1750 return;
1751 }
1752
1753 tp->Privileges[1].Luid = luid;
1754 tp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
1755
1756 if (!LookupPrivilegeValueW(nullptr, L"SeRestorePrivilege", &luid)) {
1757 free(tp);
1758 return;
1759 }
1760
1761 tp->Privileges[2].Luid = luid;
1762 tp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
1763
1764 if (!AdjustTokenPrivileges(token, false, tp, tplen, nullptr, nullptr)) {
1765 free(tp);
1766 return;
1767 }
1768
1769 free(tp);
1770
1771 BtrfsRecv br;
1772 br.Open(nullptr, args[0], args[1], true);
1773 }
1774 } catch (const exception& e) {
1775 cerr << "Error: " << e.what() << endl;
1776 }
1777 }
1778
1779 #ifdef __REACTOS__
1780 } /* extern "C" */
1781 #endif